如何等待生成的页面完全加载后再进行截图?

问题描述 投票:0回答:1

我正在使用 puppeteer 使用

screenshot
方法将一些 HTML 转换为 PNG。

首先,我获取一些 SVG,然后创建一个页面,并将 SVG 设置为页面内容。


fetch(url)
  .then(data => data.text())
  .then((svgText) => {
    // res.set('Content-Type', 'text/html');
    const $ = cheerio.load(svgText)
    return $.html()
  })
  .then(async (html) => {
    const browser = await puppeteer.launch()
    const page = await browser.newPage()
    await page.setContent(html)

    const file = await page.screenshot()
    res.set('Content-Type', 'image/png');
    await browser.close()
    res.send(file)
  })
  .catch((err) => {
    console.log(err);
    logger.log({
      level: 'error', message: 'GET /product', err
    })
  })
})

问题是,我的 SVG 中的文本包含特定字体。该字体是使用 @import CSS 标签加载的。如果我将方法设置为返回 HTML,则会加载字体,然后,在稍有延迟后,它们就会应用到我的文本中。不幸的是,当使用

screenshot
方法时,我的文本不再有样式。我想这是因为屏幕截图是在加载和应用字体之前拍摄的,因此使用后备字体渲染文本。

有没有办法在截图之前确保页面完全渲染?

我尝试使用 page.on('load') 事件侦听器,但这不会改变脚本永远运行的任何内容。

puppeteer google-font-api
1个回答
0
投票

当然可以

waitForNetworkIdle()
,但我很确定这个方法不能用于检测字体是否正确加载。 您绝对可以使用
page.on('request')
检查页面的所有请求,并使用
resourceType
过滤此字体作为
font

但是你还是得再耽搁一些时间,因为如果字体文件足够大的话,可能会有一些FOUC(Flash Of Unloaded Content)(大的TTF有时可能无法加载到浏览器中)。您可以将时间设置为正确的时间,因为字体已正确加载。因此,在用于生产/测试之前测试此脚本。

如果您不确定浏览器本身会加载什么类型的字体,您可以直接使用字体 URL 过滤页面请求,如 EOT、WOFF、WOFF2、SVG、TTF、OTF 等。

字体文件下载成功后,不要忘记等待一段时间,您可以使用自己的延迟功能。在此示例中,我将一个名为

fontSuccessLoaded
的变量作为用于标记计时的布尔值,将
fontSuccessLoaded
作为用于标记下载失败的布尔值以及用于等待目的的
delayFor
函数。

// Method for waiting delay timing
async delayFor(time) {
    return new Promise(function(resolve) {
        setTimeout(resolve, time)
    })
}

fetch(url)
  .then(data => data.text())
  .then((svgText) => {
    // res.set('Content-Type', 'text/html');
    const $ = cheerio.load(svgText)
    return $.html()
  })
  .then(async (html) => {
    let fontSuccessLoaded = 0
    let fontDownloadFailed = 0
    const browser = await puppeteer.launch()
    const page = await browser.newPage()
    const pathsUrl = 'https://www.example.com/fonts/'
    const fontsExt = ['eot', 'otf', 'ttf', 'svg', 'woff', 'woff2']
    page.setRequestInterception(true)
    page.on('request', async (request) => {

        // Filter using resource type
        if (request.resourceType() === 'font') {
            console.log('Font starting to be requested')
        }
 
        // Filter using fonts URL directly
        if (request.url().search(pathsUrl) > -1) {
            fontsExt.forEach(extension => {
                if (request.url().toLowerCase().search(extension) > -1) {
                    console.log('Font type requested:', extension)
                }
            })
        }
        // You'll have to use this continue,
        // or all if your page requests will be blocked
        await request.continue()
    })
    page.on('response', async (response) => {
        if (response.request().resourceType() === 'font') {
            console.log('Font download starting')
        }
    })
    page.on('requestfinished', async (request) => {
        if (request.resourceType() === 'font') {
            console.log('Font download finished')
            await delayFor(1000)
            fontSuccessLoaded = 1
        }
    })
    page.on('requestfailed', async (request) => {
        if (request.resourceType() === 'font') {
            console.log('Font request failed')
            fontDownloadFailed = 1
        }
    })

    await page.setContent(html)

    // This will delay the screenshot process
    // before font file download and loaded
    // and not failed to download (aborted)
    while (!fontSuccessLoaded && !fontDownloadFailed) {
        await delayFor(500)
    }

    const file = await page.screenshot()
    res.set('Content-Type', 'image/png');
    await browser.close()
    res.send(file)
  })
  .catch((err) => {
    console.log(err);
    logger.log({
      level: 'error', message: 'GET /product', err
    })
  })
})

然后可以在 console.log 中显示如下:

Font starting to be requested
Font type requested: woff
Font download starting
Font download finished
© www.soinside.com 2019 - 2024. All rights reserved.