Demand scenarios

Mobile terminal H5 generates pictures and posters to share, which is a common way of interaction. In the generated posters, there will often be personalized information of the user, such as avatar, nickname and so on.

To achieve this interaction, let’s explore possible implementations.

Scheme selection

The choice of scheme, according to the place to generate pictures, we can be divided into front-end generation and server generation.

The front-end generated

Html2canvas generates canvas, and then generates base64 picture by canvas.

Server generation

Run the headless browser with puppeteer on the server and pass the screenshot to the front end.

Scheme comparison

html2canvas puppeteer
speed Quick, the page can be generated as soon as it loads Slow, need to run a headless browser in the background and transfer Base64 pictures
compatibility Support for a subset of CSS styles, images have cross-domain issues Incompatibility problems
How easy Simple, the main work is to adjust the style and solve the problems existing in HTML2Canvas Simpler, you need to develop an additional page for generating images

The implementation of these two schemes is encapsulated as follows. In the table above, the ease-of-use comparison is also made based on the call of encapsulated methods.

Html2canvas scheme

Reference documentation

html2canvas

Turn HTML image

In this scheme, the author also stepped on some pits, and finally concluded a relatively stable component.

We pass in the target component that needs to generate the image. Html2canvas generates canvas, and then convert canvas to base64 image and set it to SRC of img tag.

/** * Dom to image *@exports conver
  * @param {string} Trigger Dom ID *@param {string} ImageElId Specifies the ID of the image to display@example* Dom2Image.conver("app", "appImage"); * /
const conver = function (trigger, imageElId, callback) {
  const opts = {
    useCORS: true.// (image cross-domain correlation)
    // y: window.pageYOffset //
    backgroundColor: 'transparent'};const imgDom = document.getElementById(trigger);
  if (imgDom) { // Zoom in on canvas to avoid screenshot blurring
    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);
      });
  });
};
Copy the code

Solve image link cross – domain problem

If there is an image link in the DOM of the generated image, an image cross-domain error may be reported on the mobile end. This is because HTML2Canvas uses the HTML download attribute to request the image link, which is almost not supported on the mobile end.

To solve this problem, there are two solutions:

  1. Change the image link to a local image.
  2. Use other methods to download the image, convert it to base64 and assign it to SRC of the IMG tag.

Scheme 1 will increase the package size. In general, Scheme 2 is preferred. Here, a method is encapsulated for Scheme 2.

/** * change the URL of network image to base64 URL. *@exports url2Base64
  * @param {string} SRC Network image URL *@returns {Promise} The Promise object returns 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(' ');
  };
});
Copy the code

Through the above encapsulation, html2Canvas generates the poster scheme. When we use it, the main work is to adjust the style. Html2canvas does not support the style, can not use.

Support the style list here: html2canvas.hertzen.com/features

If the generated image is out of style, consider using an unsupported CSS style first.

Other possible problems may be html2Canvas, most of the answers can be found in the issues of his warehouse.

Puppeteer program

Reference documentation

express.js

puppeteer

Server running headless browser, and screenshot.

Express.js is used to implement the server-side code, which is also relatively simple to implement.

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, }); }});Copy the code

use

The client only needs to pass the H5 link of the generated image as a parameter.

axios({
    method: 'POST'.url: 'http://localhost:3000/screenshot'.// Hosting the poster page
    data: {
        url: `https://xxx.com/poster`,}})Copy the code

conclusion

The front-end generated scheme, the author side has been used in many activities, the advantage is that there is no need for the server, at the beginning of the pit is indeed everywhere, but slowly spread out, in fact, a more convenient scheme.

Compatibility is also ok, at least encountered problems, finally can be solved through various debugging, of course, this is also very time-consuming, and DO not know in the model system, whether there are unknown compatibility problems.

The server-side generated solution, which the author has only recently come into contact with, has not yet been used in formal business. Its advantage is that it does not need to consider compatibility issues, and if it is used formally, it also needs to consider server-side performance and so on.