在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 }
页面调用
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('保存图片失败!') } },

浙公网安备 33010602011771号