html2canvas 目标元素过长canvas渲染不全【折中方案】

Problem

使用html2canvas.js处理浏览器截屏时,发现当元素宽度超过某个阈值后会出现超出部分渲染异常现象。

Debug

html2canvas截屏,实质是dom转canvns,canvas转图片。

目标宽度20000+px

  • 无操作,copyDom显示正常,canvas宽高正常,但右侧元素显示为黑块,且位置居左。
  • withBlack
  • option添加foreignObjectRendering: true,copyDom显示正常,canvas宽高正常,但右侧原异常元素缺失部分文本。
  • lostLabel
  • option指定width为10000,copyDom显示正常,canvas宽度10000,导出10000宽-图正常。

初步推断canvas渲染有宽度上限,百度一番仍无定论

Solution

首先不是视图外元素缺失的问题,试了很多都无结果。最后敲定折中方案就是根据宽度计算 拆分出图。

Old code

/**
 * @description html转图片导出
 * @param ele
 * @param option
 * @param fileName
 * @param uuidKey
 */
export const handleHtml2Down = (ele, option, fileName, uuidKey) => {
    window.pageYOffset = 0;
    document.documentElement.scrollTop = 0
    document.body.scrollTop = 0
    const targetDom = document.querySelector(ele)
    const copyDom = targetDom.cloneNode(true)
    copyDom.style.width = targetDom.scrollWidth + 'px'
    copyDom.style.height = targetDom.scrollHeight + 'px'
    copyDom.style.position = 'absolute'
    copyDom.style.top = '0px'
    copyDom.style.zIndex = '-1'
    copyDom.style.backgroundColor = 'white'
    document.body.appendChild(copyDom)
    html2canvas(copyDom, {
        height: copyDom.scrollHeight,
        width: copyDom.scrollWidth,
        allowTaint: false,
        useCORS: true,
        ...option
    }).then((canvas => {
        copyDom.parentNode.removeChild(copyDom)
        canvas.style.width = parseFloat(canvas.style.width) * 0.8 + 'px'
        canvas.style.height = parseFloat(canvas.style.height) * 0.6 + 'px'
        let imgURL = canvas.toDataURL('image/png',1.0)
        const alink = document.createElement("a")
        alink.href = imgURL
        let theName =  fileName || getUUID(uuidKey)
        alink.download = `${theName}.png`
        alink.click()
    }))
}

New code

/**
 * @description html转图片导出【宽度分页】
 * @param ele 目标dom元素
 * @param option html2canvas函数执行参数
 * @param fileName 导出图片文件(可不传)
 * @param uuidKey uuid前缀字符(可不传)
 */
export const handleHtml2Down = async (ele, option, fileName, uuidKey) => {
    //  reset this page scroll
    window.pageYOffset = 0;
    document.documentElement.scrollTop = 0
    document.body.scrollTop = 0
    //  targetDom - target 2 img
    const targetDom = document.querySelector(ele)
    //  copyDom - copy dom from targetDom
    const copyDom = targetDom.cloneNode(true)
    //   copyWrapper - wrapper contain the copyDom , use for with-overflow
    const copyWrapper = document.createElement('div')
    //  init the copyDom
    copyDom.style.width = targetDom.scrollWidth + 'px'
    copyDom.style.height = targetDom.scrollHeight + 'px'
    copyDom.style.transform = ''
    copyDom.style.margin = '0 0'
    //  define the maxWidth:15000(px)
    const maxW = 15000
    //  define the val
    let urls = [], w = targetDom.scrollWidth,index=0
    //  init the copyWrapper
    copyWrapper.style.backgroundColor = 'white'
    copyWrapper.style.width = (w > maxW ? maxW : w) + 'px'
    copyWrapper.style.height = targetDom.scrollHeight + 'px'
    //  fix the element
    copyWrapper.style.position = 'fixed'
    copyWrapper.style.top = '0px'
    //  make sure the copyWrapper is invisible
    copyWrapper.style.zIndex = '-1'
    //  use for pageControl
    copyWrapper.style.overflow = 'hidden'
    //  execute dom append
    copyWrapper.appendChild(copyDom)
    document.body.appendChild(copyWrapper)
    //  generate canvas from dom in Loop
    while (w > 0){
        await html2canvas(copyWrapper, {
            height: copyWrapper.scrollHeight,
            width: (w > maxW ? maxW : w),
            foreignObjectRendering: true,
            allowTaint: false,
            useCORS: true,
            ...option
        }).then((canvas => {
            canvas.style.width = parseFloat(canvas.style.width) * 0.8 + 'px'
            canvas.style.height = parseFloat(canvas.style.height) * 0.6 + 'px'
            urls.push(canvas.toDataURL('image/png',1.0))
            w = w - maxW
            index++
            copyWrapper.style.width = w
            copyDom.style.marginLeft = `-${maxW * index}px`
        }))
    }
    //  execute dom remove
    copyDom.parentNode.removeChild(copyDom)
    console.log(urls)
    //  export urls & execute img download
    urls.forEach(url=>{
        let alink = document.createElement("a")
        alink.href = url 
        alink.download = `${fileName || getUUID(uuidKey)}.png`
        alink.click()
    })
}
expory getUUID = (preffix)=> { return preffix + 'i-am-uuid-123'}

Result

r1
r2

End

thanks 4 read & welcome 4 leaving a message.

阅读 176

推荐阅读