在vue2中使用html2canvas导出png图片

这次的需求是把甘特图导出为png图片,记录下滚动区域及超出内容区域的内容怎么截取到图片。

具体实现需要用到js+css。在点击导出按钮的时候,对目前元素进行css样式处理,即设置所有元素大小由内容撑开,不在折叠隐藏,不在滚动。

结合实际业务就是,给需要截图的元素添加特殊样式

 1 // 需要截图的元素
 2 .visibleDiv {
 3     width: max-content !important;
 4     height: max-content !important;
 5     max-width: unset !important;
 6     overflow: visible !important;
 7     & /deep/ div {
 8       flex-wrap: nowrap;
 9       width: max-content;
10       height: max-content;
11       max-width: unset !important;
12       overflow: visible !important;
13     }
14   }

对html2canvas封装一下

  1 import html2canvas from 'html2canvas'
  2 
  3 const exportWrapDomId = 'img-export-canvas-id'
  4 const removeCssList = ['boxShadow']
  5 
  6 export function cloneCanvas(oldCanvas, newCanvas) {
  7   const context = newCanvas.getContext('2d')
  8 
  9   if (context) {
 10     context.drawImage(oldCanvas, 0, 0)
 11   }
 12 
 13   return newCanvas
 14 }
 15 
 16 export function replaceCanvas(targetDom, newNode) {
 17   const validCanvasDomArr = targetDom.getElementsByTagName('canvas')
 18   const invalidCanvasDomArr = newNode.getElementsByTagName('canvas')
 19   if (validCanvasDomArr.length < 1) {
 20     return newNode
 21   }
 22   for (let i = validCanvasDomArr.length - 1; i >= 0; i--) {
 23     const validCanvasDom = validCanvasDomArr[i]
 24     const invalidCanvasDom = invalidCanvasDomArr[i]
 25     cloneCanvas(validCanvasDom, invalidCanvasDom)
 26   }
 27 
 28   return newNode
 29 }
 30 
 31 export function removeCSS(node, cssNames = []) {
 32   if (!node) {
 33     return
 34   }
 35 
 36   const { style } = node
 37   for (const cssName of cssNames) {
 38     // @ts-ignore
 39     style[cssName] = 'none'
 40   }
 41 
 42   const { children = [] } = node
 43   // @ts-ignore
 44   for (const child of children) {
 45     removeCSS(child, cssNames)
 46   }
 47 }
 48 
 49 export function cloneDom(domId) {
 50   const dom = document.querySelector(`#${domId}`)
 51   if (dom) {
 52     const cloneNode = document.importNode(dom, true)
 53 
 54     return replaceCanvas(dom, cloneNode)
 55   } else {
 56     return null
 57   }
 58 }
 59 // 判断浏览器类型
 60 export function getOperaType() {
 61   var userAgent = navigator.userAgent //取得浏览器的userAgent字符串
 62   if (userAgent.indexOf('Opera') > -1) {
 63     //判断是否Opera浏览器
 64     return 'Opera'
 65   }
 66   if (userAgent.indexOf('Firefox') > -1) {
 67     //判断是否Firefox浏览器
 68     return 'FF'
 69   }
 70   if (userAgent.indexOf('Chrome') > -1) {
 71     //判断是否Chrome浏览器
 72     return 'Chrome'
 73   }
 74   if (userAgent.indexOf('Safari') > -1) {
 75     //判断是否Safari浏览器
 76     return 'Safari'
 77   }
 78   if (userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1 && !isOpera) {
 79     //判断是否IE浏览器
 80     return 'IE'
 81   }
 82 }
 83 // 设置截图最宽、高
 84 export function getWH(eleW, eleH, maxLen, maxArea) {
 85   if (eleH <= maxLen && eleW <= maxLen) {
 86     return {
 87       width: eleW,
 88       height: eleH
 89     }
 90   } else if (eleH <= maxLen) {
 91     return {
 92       width: parseInt(maxArea / eleH) >= maxLen ? maxLen : parseInt(maxArea / eleH),
 93       height: eleH
 94     }
 95   } else if (eleW <= maxLen) {
 96     return {
 97       width: eleW,
 98       height: parseInt(maxArea / eleW) >= maxLen ? maxLen : parseInt(maxArea / eleW)
 99     }
100   }
101 }
102 export function getMaxWidthHeight(eleW, eleH) {
103   let maxWH = 8192
104   let maxArea = maxWH * maxWH
105   const operaType = getOperaType()
106   switch (operaType) {
107     case 'Chrome':
108       // Maximum height/width: 32,767 pixels
109       // Maximum area: 268,435,456 pixels (e.g., 16,384 x 16,384)
110       maxWH = 32767
111       maxArea = 268435456
112       return getWH(eleW, eleH, maxWH, maxArea)
113     case 'FF':
114       // Maximum height/width: 32,767 pixels
115       // Maximum area: 472,907,776 pixels (e.g., 22,528 x 20,992)
116       maxWH = 32767
117       maxArea = 472907776
118       return getWH(eleW, eleH, maxWH, maxArea)
119     case 'IE':
120       // Maximum height/width: 8,192 pixels
121       // Maximum area: N/A
122       return getWH(eleW, eleH, maxWH, maxArea)
123     default:
124       return getWH(eleW, eleH, maxWH, maxArea)
125   }
126 }
127 export const screenShotImage = async (domId, opt) => {
128   const dom = cloneDom(domId)
129   const targetElement = document.querySelector(`#${domId}`)
130   if (dom && targetElement) {
131     const width = targetElement.offsetWidth
132     const height = targetElement.offsetHeight
133     const wrap = document.createElement('div')
134     wrap.style.position = 'absolute'
135     wrap.style.left = '-10000px'
136     wrap.style.top = '-10000px'
137     wrap.style.width = `${width}px`
138     wrap.style.height = `${height}px`
139     wrap.id = exportWrapDomId
140     wrap.appendChild(dom)
141     document.body.appendChild(wrap)
142     const targetDom = document.getElementById(exportWrapDomId)
143     Object.assign(opt, getMaxWidthHeight(width, height))
144     if (targetDom) {
145       removeCSS(targetDom, removeCssList)
146       return await new Promise((resolve) => {
147         html2canvas(targetDom, opt).then((canvas) => {
148           document.body.removeChild(wrap)
149           return resolve(canvas.toDataURL('image/png'))
150         })
151       })
152     }
153   } else {
154     return ''
155   }
156 }
html2canvas

页面调用

htmlToImage() {
      try {
        this.visibleDiv = true
        this.options.maxHeight = '100%'
        // canvas最大绘制高度 / 每条高度  得出最大条数
        const rowH = document.getElementsByClassName('gantt-elastic__task-list-item')[0].clientHeight
        this.options.maxRows = Math.floor(32767 / rowH)
        setTimeout(() => {
          screenShotImage('gantt-container', {}).then((url) => {
            this.visibleDiv = false
            const a = document.createElement('a')
            a.setAttribute('href', url)
            a.download = `${this.projectName}.png`
            document.body.appendChild(a)
            a.click()
            document.body.removeChild(a)
            this.$nextTick(() => this.setGanttViewHeight())
          })
        }, 1000)
      } catch (error) {
        this.$Message.warning('保存图片失败!')
      }
    },
View Code

 

posted @ 2025-04-25 15:57  剑气风尘  阅读(107)  评论(0)    收藏  举报