dom-to-image分享多张异步图片遇到的坑

dom-to-image库 存在的问题

github-issue地址
问题:当超过一张图片时,ios/safari首次会出现某张图片空白,再次生成canvas才正常。
之前有一张图片时通过执行2次domtoimage.toJpeg(魔法),当超过1张图片时,图片第一次仍然会空白
原因:ios下svg第一次svg生成的很慢。dom-to-image库是先将html转化成svg格式,然后再绘制到canvas上。需要等待所有dom(尤其是所有图片)渲染完成后,再去执行html转化换。

Apparently each browser renders images with svg sources differently.
In Safari the embedded images inside the svg appear to load very
slowly the first time the browser encounters them, and sometimes they
never load (unless you append the element visibly to the DOM)

看issue都说完美方案,是替换成html2canvas,但是这个坑也特别多。

方案1: dom-to-image等待所有图片都加载完成,再执行绘制

// 需要监听加载的图片总数量
const totalLoadImgCount = useRef(0)
// 加载完成的图片数量
const loadSuccessImgCount = useRef(0)  
/**
* 监听图片加载
* tips:
* dom-to-image是将dom转化成svg,然后转化成canvas。
* 只有所有图片需要加载完成后才能绘制成功,不然会可能出现第一次渲染图片空白的情况
*/
const onLoadImage = () => {
  loadSuccessImgCount.current++

  if (loadSuccessImgCount.current === totalLoadImgCount.current) {
    createImgUrl()
  }
}

useEffect(() => {
  options?.extends?.imgUrl && totalLoadImgCount.current++
  options?.extends?.initImageUrl && totalLoadImgCount.current++
}, [options])


return<>
   <img
    className="w-full"
    src={toXSmallImage(options?.extends?.imgUrl as string)}
    alt=""
    onLoad={() => {
     onLoadImage()
    }}
   />
  <img
    className="relative w-full"
    id="initImageUrl"
    src={toXSmallImage(options?.extends?.initImageUrl as string)}
    alt=""
    onLoad={() => {
      onLoadImage()
    }}
  />
</>)

看了issue其他解决方法,发现都不成功。找到方案后并回复了issue
dom-to-image分享多张异步图片遇到的坑_第1张图片

其他尝试,但失败
useEffect预加载所有图片,本来期望利用图片缓存的方式。其时间会早于真实dom中所有图片onload时间,转化时,图片可能还没完全加载成功。

方案2: html2canvas库,解决多张图片问题(不采用,样式问题太多)

原理:直接将将图片绘制到canvas当中,因而就存在上面的问题。
遇到的问题:

  1. 图片不展示
    添加属性:{ useCORS: true }
  2. 部分css3属性不支持。
    https://www.jianshu.com/p/e74dab30ea2c比如:省略号,可以变成手动…的方式
    https://github.com/niklasvh/html2canvas/issues/1594
  3. 文字行高不对,
    原因:主要原因是image出生block,会出现一个空白区域,将文字顶出去了
    解决:
    方式1: 利用style标签,设置全局body->img设置inline-block
    const style = document.createElement('style')
    document.head.appendChild(style)
    style.sheet?.insertRule(
      'body > div:last-child img { display: inline-block; }'
    )
    const canvas = await html2canvas(
      document.getElementById('share-dom') as HTMLElement,
      { useCORS: true }
    )
    style.remove()
    const imgUrl = canvas.toDataURL('image/jpeg', 1.0)

方式2,但会影响到所有的img
issues

@layer base {
  img {
    @apply inline-block;
  }
}

你可能感兴趣的:(javascript,前端)