Exploration of generating screenshot poster at mobile terminal H5

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

html2canvaspuppeteer
speedFast, the page can be generated after loadingSlow, you need to run headless browser in the background and transfer base64 pictures
compatibilityThe supported styles are a subset of css, and the pictures have cross domain problemsNo compatibility issues
Usability Simple, mainly working on adjusting the style and solving the problems of html2canvasRelatively 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

html2canvas

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:

  1. Change the picture link to a local picture.
  2. 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

express.js

puppeteer

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.

Keywords: Javascript Front-end html5

Added by ScottCFR on Thu, 30 Dec 2021 02:08:01 +0200