react 使用 html2Canvas 在微信中长按保存图片

3,865 阅读2分钟

先准备一个 html2canvashooks

import React from 'react'
import html2Canvas from 'html2canvas'

type UseHtml2Canvas = (opt: {
  dom_id: string
  html_2_canvas_option: Html2Canvas.Html2CanvasOptions
}) => [string, () => void]

export const useHtml2Canvas: UseHtml2Canvas = ({
  dom_id,
  html_2_canvas_option = {
    useCORS: true,
    scale: devicePixelRatio,
    width: window.innerWidth,
    height: window.innerHeight,
    windowWidth: window.innerWidth,
    windowHeight: window.innerHeight,
  },
}) => {
  const [save_img, changeSaveImg] = React.useState(undefined)

  const create_save_img = React.useCallback(() => {
    html2Canvas(document.getElementById(dom_id), html_2_canvas_option).then((canvas) => {
      changeSaveImg(canvas.toDataURL('image/jpeg'))
    })
  }, [])

  return [save_img, create_save_img]
}

再来一个调用示例

import React from 'react'
import {useHtml2Canvas} from '@com/use-html-2-canvas'

const list = Array.from({length: 20}).map((v, k) => k)

const randomColor = () => {
  const random = () => Math.round(Math.random() * 255)

  return `rgb(${random()}, ${random()}, ${random()})`
}

export default () => {
  const [img_src, createSaveImg] = useHtml2Canvas({
    dom_id: 'test-save-img',
    html_2_canvas_option: {
      useCORS: true,
      scale: devicePixelRatio,
      width: window.innerWidth,
      height: window.innerHeight,
      windowWidth: window.innerWidth,
      windowHeight: window.innerHeight,
    },
  })

  React.useEffect(() => {
    createSaveImg()
  }, [])

  return (
    <div>
      {img_src && <img src={img_src} alt="" style={{width: '100vw', height: '20vh'}} />}

      <ul style={{width: '100vw', height: '100vh'}} id="test-save-img">
        {list.map((text, key) => (
          <li key={key} style={{height: `${100 / list.length}vh`, backgroundColor: `${randomColor()}`}}>
            {text}
          </li>
        ))}
      </ul>
    </div>
  )
}

注意点

  1. 时间:2020年07月18日,html2canvas 使用 1.0.0-rc.5 版本, ios 设备上会没有任何显示。then函数不会被回调也不会报错。请降级强制使用 1.0.0-rc.4 版本。(注意package.json 内的 "^1.0.0-rc.4" ^ 符号要去掉)
  2. 所有 dom 内的图片不能跨域,否则会导致白屏等奇怪问题。即使加上useCORSallowTaint也无法解决,浏览器控制台直接会打印CORS相关报错。
  3. 生成的图片清晰度受原始图片的质量与widthheightwindowWidthwindowHeightscale 影响,请根据实际情况来调节这几个参数。
  4. 如果生成的base64图片过大,会导致图片无法显示,请相应减小3中的几个参数值。
  5. 不可将图片转换为Blob的形式,设备虽然可以正常显示图片,但是无法进行长按保存,保存下来的图片可能为一个空白的图像。
  6. 在指定dom来生成图片时,会受offsetTopoffsetLeft影响。例如 offsetTop20 时,生成的图片顶部 20px(不一定为 20px)的距离会被所设置的backgroundColor(默认白色)颜色填充。这时,可使用 xy参数进行校正。
  7. 如果一直出现跨域的情况,或者图片一直不出现的情况,可考虑将图片资源转换成 base64。例如就遇到二维码图片链接某些设备上一直不出现,则可以考虑接口直接返回二维码内容,由前端根据返回的内容来创建二维码。
  8. 当还出现一些奇奇怪怪的问题时,可尝试切换html2canvas版本。

附上一个创建二维码的 hooks

import React from 'react'
import QRCode from 'qrcode'

type UseCreateQR = (QR_content: string) => [string]

export const useCreateQR: UseCreateQR = (QR_content) => {
  const [qr, changeQR] = React.useState('')

  React.useEffect(() => {
    QRCode.toDataURL(QR_content, {margin: 0}, (err, url) => {
      changeQR(url)
    })
  }, [QR_content])

  return [qr]
}