Demand scenario
The mobile terminal H5 generates pictures and posters for sharing, which is a common interaction method. The generated posters often contain user's personalized information, such as avatar, nickname and so on.
In order to realize this interaction, we explore a feasible implementation scheme.
Scheme selection
According to the place where the picture is generated, the scheme can be divided into front-end generation and server-side generation.
Front end generation
Generate canvas with html2canvas, and then generate base64 pictures from canvas.
Server generation
Run the headless browser with puppeter on the server side, and send the screenshot to the front end.
Scheme comparison
html2canvas | puppeteer | |
---|---|---|
speed | Fast, the page can be generated after loading | Slow, you need to run headless browser in the background and transfer base64 pictures |
compatibility | The supported styles are a subset of css, and the pictures have cross domain problems | No compatibility issues |
Usability | Simple, mainly working on adjusting the style and solving the problems of html2canvas | Relatively simple, you need to develop an additional page for generating pictures |
Let's encapsulate the implementation of these two schemes. In the table above, the comparison of ease of use is also based on calling the encapsulated method.
html2canvas scheme
Reference documents
html to image
In this scheme, the author also stepped on some pits, and finally summarized a relatively stable component.
We pass in the target component that needs to generate the picture, generate the canvas by html2canvas, convert the canvas to base64 picture, and set it to src of img tag.
/** * Dom Convert to picture * @exports conver * @param {string} trigger Dom id of * @param {string} imageElId id of the picture to be displayed * * @example * Dom2Image.conver("app", "appImage"); */ const conver = function (trigger, imageElId, callback) { const opts = { useCORS: true, // (picture cross domain correlation) // y: window.pageYOffset / / the screenshot fails due to the resolution of scroll, backgroundColor: 'transparent', }; const imgDom = document.getElementById(trigger); if (imgDom) { // Zoom in on the canvas to avoid blurring the screenshot const canvas = document.createElement('canvas'); const scale = 3; const width = imgDom.offsetWidth; const height = imgDom.offsetHeight; canvas.width = width * scale; canvas.height = height * scale; opts.scale = scale; opts.canvas = canvas; opts.width = width; opts.height = height; } loader(html2canvasJSLink, () => { html2canvas(document.getElementById(trigger), opts).then((canvas) => { const base64ImgSrc = canvas.toDataURL('image/png'); const img = document.getElementById(imageElId); img.src = base64ImgSrc; if (callback && typeof callback === 'function') { callback(); } }) .catch((error) => { console.log(error); }); }); };
Solve the cross domain problem of picture links
If there is a picture link in the dom that generates the picture, a picture cross domain error may be reported on the mobile terminal. This is because html2canvas uses the download attribute of HTML to request the picture link. This attribute is almost not supported on the mobile terminal.
To solve this problem, there are two solutions:
- Change the picture link to a local picture.
- Download the image through other methods, convert it to base64, and then assign it to the src of img tag.
Scheme 1 will increase the package volume. Generally, scheme 2 is preferred. Here is also a method encapsulated for scheme 2.
/** * Solve the cross domain problem of pictures, and convert the network picture URL to base64 URL. * @exports url2Base64 * @param {string} src Web picture URL * @returns {Promise} Promise Object returns the base64 URL * * @example * Dom2Image.url2Base64("http://test.com/image.png").then(url=>{}); */ const url2Base64 = src => new Promise((resolve) => { const img = new Image(); img.setAttribute('crossOrigin', 'anonymous'); img.src = src; img.onload = () => { const canvas = convertImageToCanvas(img); const url = canvas.toDataURL('image/png'); resolve(url); }; img.onerror = () => { resolve(''); }; });
Through the above encapsulation, the scheme of generating posters by html2canvas. When we use it, the main work is to adjust the style. The styles not supported by html2canvas cannot be used.
The list of supported styles is here: https://html2canvas.hertzen.com/features
If the style of the generated image is disordered, priority should be given to whether the unsupported css style is used.
The probability of other possible problems may be the problem of html2canvas. Most answers can be found in the issues of his warehouse.
Puppeter scheme
Reference documents
The server runs a headless browser and takes a screenshot.
Here use express JS to implement the server code is also relatively simple.
const express = require('express'); const router = express.Router(); const puppeteer = require('puppeteer'); router.post('/', async (req, res) => { try { const { url, width = 375, height = 667 } = req.body; console.log('url', url); const browser = await puppeteer.launch({ headless: true, }); const page = await browser.newPage(); await page.setViewport({ width, height }); await page.goto(url); await page.waitForTimeout(1000); const imgBuffer = await page.screenshot({ type: 'png', }); const base64Str = imgBuffer.toString('base64'); res.json({ code: 200, data: `data:image/png;base64,${base64Str}`, msg: 'Poster generated successfully', }); browser.close(); } catch (error) { res.status(500).json({ message: error.message, errors: error.stack, }); } });
Mode of use
The client only needs to pass the H5 link of the generated picture as a parameter.
axios({ method: 'POST', url: 'http://localhost:3000/screenshot ', / / the page hosting the poster data: { url: `https://xxx.com/poster`, } })
summary
The scheme generated by the front-end has been used by the author in many activities. The advantage is that there is no need for the server. At the beginning, there are pits everywhere, but after it is slowly flattened, it is actually a more convenient scheme.
In fact, the compatibility is OK. At least the problems encountered can be solved through various debugging. Of course, it takes a lot of time, and I don't know whether there are unknown compatibility problems on the model system I haven't encountered.
The scheme generated by the server has only recently been contacted by the author and has not been used in formal business. Its advantage is that it does not need to consider the compatibility problem. If it is formally used, it also needs to consider the performance of the server.