关于前端:html2canvas-目标元素过长canvas渲染不全折中方案

36次阅读

共计 3438 个字符,预计需要花费 9 分钟才能阅读完成。

Problem

应用 html2canvas.js 解决浏览器截屏时, 发现当元素宽度超过某个阈值后会呈现超出局部渲染异常现象。

Debug

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

指标宽度 20000+px

  • 无操作,copyDom 显示失常,canvas 宽高失常,但右侧元素显示为黑块,且地位居左。
  • option增加foreignObjectRendering: true,copyDom 显示失常,canvas 宽高失常,但右侧原异样元素缺失局部文本。
  • 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


End

thanks 4 read & welcome 4 leaving a message.

正文完
 0