获取iframe中canvas画面
截屏commit信息 https://github.com/goplus/builder/commit/566a8a3e4c7cf61cab2c5f5eec5c7d0bd723b6d6(同时也复用到了录屏中https://github.com/goplus/builder/commit/06cd95c434fe5a4ff90792e4d5c36b7b76aa73c9)
目标:获取 XBuildr 游戏画面插入到海报中
考虑到是否同源会影响资源获取策略,于是我先try了iframe.contentWindow.document看看,结果是同源的
接着我用开发者工具找到了游戏画面为 id: game-canvas
然后取 runner 里面添加了截屏方法并且一层层往上暴露
ProjectRunner (统一接口)
├── ProjectRunnerV2 (直接管理iframe)
└── ProjectRunnerV1 └── IframeDisplay (实际管理iframe)
// 添加截图方法
const takeScreenshot = async () => {const iframeWindow = iframe.value?.contentWindow as IframeWindow | nullif (!iframeWindow) {throw new Error('iframe未准备好')}// 获取iframe内的game-canvas元素const gameCanvas = iframeWindow.document.querySelector('#game-canvas') as HTMLCanvasElementif (!gameCanvas) {throw new Error('未找到game-canvas元素')}// 直接从canvas获取图像数据const dataURL = gameCanvas.toDataURL('image/png')return {dataURL,width: gameCanvas.width,height: gameCanvas.height}
}// 暴露截图方法
defineExpose({takeScreenshot
})
然后在project.vue运行过程中通过事件应用
const handleScreenshot = useMessageHandle(async () => {try {// 获取项目运行器引用const projectRunner = projectRunnerRef.valueif (!projectRunner) {throw new Error('项目运行器未准备好')}// 使用项目运行器的截图方法const screenshot = await projectRunner.takeScreenshot()if (!screenshot) {throw new Error('截图方法返回空结果')}// 设置截图数据screenshotDataUrl.value = screenshot.dataURLscreenshotWidth.value = screenshot.widthscreenshotHeight.value = screenshot.heightconsole.log('成功从游戏canvas获取截图', screenshot.width, 'x', screenshot.height)isScreenshotModalVisible.value = true} catch (error) {console.error('截图失败:', error)throw error}},{ en: 'Failed to take screenshot', zh: '截屏失败' }
)
可以简化为
// 统一的截图服务
class ScreenshotService {static async takeScreenshot(iframeElement: HTMLIFrameElement) {const iframeWindow = iframeElement.contentWindowconst gameCanvas = iframeWindow.document.querySelector('#game-canvas')return gameCanvas.toDataURL('image/png')}
}// 在需要的地方直接调用
const screenshot = await ScreenshotService.takeScreenshot(iframeRef.value)