dom还能转成图片?
前言 最近工作中需要将网页上的内容转换为图片,供用户保存再发到朋友圈里,也就是说对一部分dom做“截图”处理,然后生成img文件,下载到本地。
方法 在网上收集了几种方案,这里我们列出来:
如果是新的内容,那么用canvas来绘制,如果是以前就有的 DOM 内容,那么将 DOM 改写为 canvas ,然后利用 canvas 的 toDataURL 方法实现将 DOM 输出为包含图片展示的 data URI,下载到本地。
使用 html2canvas.js 将 DOM 转换为canvas,然后利用 canvas 的 toDataURL 方法实现将 DOM 输出为包含图片展示的 data URI,下载到本地。
后端服务器生成,如果后端小哥是你小弟的话…
实践 我们要实现一个简单的 demo ,这个 demo 有一张图片和几段文字,我们依次按照上面两种方案来实现,通过对比其实现过程和效果来总结出最佳实践
方案一:手撸canvas 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class App extends React .Component { componentDidMount() { const canvas = document .getElementById("myCanvas" ); const ctx = canvas.getContext("2d" ); const img = new Image(); img.src = "http://osutuwgm1.bkt.clouddn.com/testcat.png" ; img.onload = function ( ) { ctx.drawImage(img, 70 , 10 , 260 , 200 ); ctx.font = "30px Courier New" ; ctx.fillStyle = "skyblue" ; ctx.fillText("Canvas中文测试" , 90 , 270 ); }; } handleDownLoad = () => { const canvas = document .getElementById("myCanvas" ); const imgUri = canvas.toDataURL("image/png" ).replace("image/png" , "image/octet-stream" ); window .location.href= imgUri; } render() { return ( <div> <canvas id="myCanvas" width="400px" height="300px" style={{border : '1px solid red' }} /> <button onClick={this .handleDownLoad}>Click</button> </ div> ) } }
通过上面一大串代码,我们好不容易得到下面的canvas图:
当一切就绪后,点download,发现页面直接报错了:
这是为什么呢?
canvas的图片必须要同源才能被我们转换为baseData文件,难道我们需要将这些图片都放到同一个服务器下吗?显然不合理,我们找到只要加这么一行代码,就能实现跨域加载图片:
1 2 3 4 5 6 ... const img = new Image();img.src = "http://osutuwgm1.bkt.clouddn.com/testcat.png" ; img.setAttribute('crossOrigin' ,'anonymous' ); img.onload = function ( ) { ...
现在点击download后,便能下载到canvas中的图像。
我们思考,假设我们要实现的样式很复杂,难道也要用canvas来手撸出来吗?显然会很复杂,并且对于样式的维护也会变得很庞大,有没有更好的方式呢?
html2canvas 自动化 html2canvas 中的2是啥意思呢? 也就是谐音 to 的意思。。。(好无聊啊hhhhh),字面上的意思就是将html自动转换为canvas,我们又知道canvas能够转换为图片格式的文件,那么我们岂不是只要写html就能随意的生成我们想要的样式,再也不要写那么复杂的canvas了,岂不美哉,show me code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class App extends React .Component { handleClick = () => { const dom = this .refs.xixix; const opts = { useCORS : true }; html2canvas(dom, opts).then(function (canvas ) { var imgUri = canvas.toDataURL("image/png" ).replace("image/png" , "image/octet-stream" ); window .location.href= imgUri; }); } render() { return ( <div> <div ref="xixix" className="container" > <img src="http://osutuwgm1.bkt.clouddn.com/testcat.png" alt="" /> <p>HTML 中文测试</p> </ div> <button onClick={this .handleClick}>click</button> </ div> ) } }
如图:
当我们点击Click后,下载的图片和上面canvas的一样,但是相比起来,用 html CSS 来绘我们的样式,会更加简单高效些。也方便后期的更新与维护。
当然,html2canvas 并不是完美的,他有一下限制:
不支持iframe
不支持跨域图片
不能在浏览器插件中使用
部分浏览器上不支持SVG图片
不支持Flash
不支持古代浏览器和IE
部分新的CSS未支持,如 box-shadow
其实从最后一点,我们可以看出,这并不是一个非常完美的工具,因为要去去支持新的CSS,还需要等待作者来更新维护,假设一个CSS对你的项目非常重要,但是确一直未维护,那么就非常尴尬了,这就需要你手动来写canvas了。
彩蛋 两种方法都使用过了,这里我再给大家一个非常实用的函数,将文件重命名后再下载:
1 2 3 4 5 6 7 8 const saveFile = function (data, filename ) { const save_link = document .createElementNS('http://www.w3.org/1999/xhtml' , 'a' ); save_link.href = data; save_link.download = filename; const event = document .createEvent('MouseEvents' ); event.initMouseEvent('click' , true , false , window , 0 , 0 , 0 , 0 , 0 , false , false , false , false , 0 , null ); save_link.dispatchEvent(event); };
Last 我们使用两种方式尝试去解决我们的问题,但最后都发现没有完美的解决方案,canvas可以实现任何效果,只不过会很复杂不易维护,html2canvas 非常便捷,但是不支持所有CSS,针对比较简单的样式还是游刃有余的。具体使用哪种,就需要看实际情况~
It is not easy to meet each other in such a big world. :)