import puppeteer from 'puppeteer'; class ScreenshotService { constructor() { this.browser = null; this.isInitialized = false; } async initBrowser() { if (!this.browser) { console.log('初始化Puppeteer浏览器...'); this.browser = await puppeteer.launch({ headless: 'new', args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--single-process', '--disable-gpu', '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', '--disable-renderer-backgrounding', '--disable-features=TranslateUI', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-default-browser-check', '--disable-default-apps', '--disable-background-networking', '--disable-sync', '--metrics-recording-only', '--disable-domain-reliability', '--disable-component-extensions-with-background-pages', '--force-device-scale-factor=1', '--enable-precise-memory-info' ] }); console.log('Puppeteer浏览器初始化完成'); } return this.browser; } async closeBrowser() { if (this.browser) { await this.browser.close(); this.browser = null; console.log('Puppeteer浏览器已关闭'); } } async modifyHtmlForScreenshot(htmlContent, targetWidth, targetHeight) { console.log(`开始修改HTML for截图, 目标尺寸: ${targetWidth}x${targetHeight}`); // 创建完全针对截图优化的HTML版本 const optimizedHtml = htmlContent.replace( /
/i, ` ` ); // 注入截图专用JavaScript const finalHtml = optimizedHtml.replace( /<\/body>/i, ` ` ); console.log('HTML修改完成,已注入截图优化代码'); return finalHtml; } async captureScreenshot(htmlContent, width, height, options = {}) { try { await this.initBrowser(); console.log(`开始截图, 目标尺寸: ${width}x${height}`); // 修改HTML内容以适应截图 const optimizedHtml = await this.modifyHtmlForScreenshot(htmlContent, width, height); // 创建新页面 const page = await this.browser.newPage(); try { // 设置精确的viewport尺寸 await page.setViewport({ width: width, height: height, deviceScaleFactor: options.deviceScaleFactor || 2, // 高清截图 }); // 设置页面内容 await page.setContent(optimizedHtml, { waitUntil: ['load', 'domcontentloaded', 'networkidle0'], timeout: 30000 }); // 等待页面完全渲染 await page.waitForTimeout(2000); // 执行最终的尺寸验证和调整 await page.evaluate((targetWidth, targetHeight) => { const html = document.documentElement; const body = document.body; const container = document.querySelector('.slide-container'); // 最终强制设置 [html, body].forEach(element => { if (element) { element.style.width = targetWidth + 'px'; element.style.height = targetHeight + 'px'; element.style.minWidth = targetWidth + 'px'; element.style.minHeight = targetHeight + 'px'; element.style.maxWidth = targetWidth + 'px'; element.style.maxHeight = targetHeight + 'px'; element.style.margin = '0'; element.style.padding = '0'; element.style.border = 'none'; element.style.outline = 'none'; element.style.overflow = 'hidden'; element.style.position = 'fixed'; element.style.top = '0'; element.style.left = '0'; } }); if (container) { container.style.width = targetWidth + 'px'; container.style.height = targetHeight + 'px'; container.style.minWidth = targetWidth + 'px'; container.style.minHeight = targetHeight + 'px'; container.style.maxWidth = targetWidth + 'px'; container.style.maxHeight = targetHeight + 'px'; container.style.position = 'fixed'; container.style.top = '0'; container.style.left = '0'; container.style.margin = '0'; container.style.padding = '0'; container.style.border = 'none'; container.style.outline = 'none'; container.style.overflow = 'hidden'; container.style.transform = 'none'; container.style.boxShadow = 'none'; } console.log('最终页面尺寸确认:', { html: html.offsetWidth + 'x' + html.offsetHeight, body: body.offsetWidth + 'x' + body.offsetHeight, container: container ? container.offsetWidth + 'x' + container.offsetHeight : 'none' }); }, width, height); // 再次等待以确保所有更改生效 await page.waitForTimeout(1000); // 执行截图,使用精确的剪裁区域 const screenshot = await page.screenshot({ type: options.format || 'png', quality: options.quality || 100, clip: { x: 0, y: 0, width: width, height: height }, omitBackground: false, // 包含背景 captureBeyondViewport: false, // 不截取视口外内容 }); console.log(`截图完成, 生成了 ${screenshot.length} 字节的图片数据`); return screenshot; } finally { await page.close(); } } catch (error) { console.error('截图失败:', error); throw new Error(`截图失败: ${error.message}`); } } } export default new ScreenshotService();