前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Web动态图片合成与分享——html2canvas方案实践

Web动态图片合成与分享——html2canvas方案实践

原创
作者头像
WendyGrandOrder
修改2018-10-19 20:23:37
8K2
修改2018-10-19 20:23:37
举报
文章被收录于专栏:RESTART POiNTERRESTART POiNTER

一、应用场景

在web侧运营活动中,分享传播是重要的一环。普通的h5链接/结构化消息分享已经不能满足产品越来越大的脑洞。在很多场景下,我们需要将个性化内容(如帐号信息,头像,用户输入等)固化为一张完整的图片,一秒分享到朋友圈&AIO&群,藉此提高传播效率。

如下图:

在传统场合,这类功能往往依赖后台合成图片,或依赖端上实现,但web侧本身也有独立的解决方案。 Web中具有图片生成功能的是canvas标签,我们可以使用canvas中的toDataUrl() API,得到当前画布内容的base64 data URI,即图片数据。

toDataUrl() api描述

所以,最直接的思路是,把个性化内容绘制在canvas上,使用api转成图片。 但这样还是太繁琐,要和大量的绘制api打交道,不直观,不便于复用。web侧最直观的就是dom内容,如果能把dom内容快速转换成canvas,由canvas再转成图片,就可以快速实现上述功能。 万幸的是,我们有一个强大的工具——html2canvas。

html2canvas是一套由个人开发的开源工具,用于把html标签绘制的dom内容转为canvas。

官网

于是,我们的解决路径变成了下图:

该项目文档较为简单,且历史版本存在着各种问题。比如部分css属性无法绘制、移动端绘制时图片模糊,图片错位等等问题。网上现存的资料较为混乱,众说纷纭。笔者借着开发运营活动的契机,对html2canvas的使用、以及和后续的保存/分享链路做了一个梳理,以供参考。

注意,本文所有的例子都基于html2canvas 1.0版本来实现。0.x版本bug较多,不建议再去蹚坑,如果你还在使用旧版本,请换成1.0版

二、基础使用

html2canvas的基本调用方式如下

代码语言:javascript
复制
            var shareContent = document.getElementById('my-dom');//需要截图的包裹的(原生的)DOM 对象
            var width = shareContent.offsetWidth; //获取dom 宽度
            var height = shareContent.offsetHeight; //获取dom 高度
            var canvas = document.createElement("canvas"); //创建一个canvas节点
            var scale = 2; //定义任意放大倍数 支持小数
            canvas.width = width * scale; //定义canvas 宽度 * 缩放
            canvas.height = height * scale; //定义canvas高度 *缩放
            canvas.getContext("2d").scale(scale,scale); //获取context,设置scale

            var opts = {
                scale:scale, // 添加的scale 参数
                canvas:canvas, //自定义 canvas
                logging: true, //日志开关
                width:width, //dom 原始宽度
                height:height, //dom 原始高度
                backgroundColor: 'transparent',
            };
            html2canvas(shareContent, opts).then(function (canvas) {
                IMAGE_URL = canvas.toDataURL("image/png");
                $('.myImage').attr('src',IMAGE_URL);
            })

通过一个异步的过程,将html图片转换为canvas,再调用canvas的api,得到dataURL,最后,把data URL赋给img标签的src属性,从而生成一张完整的图片。

我们关注调用参数

canvas

转换用的canvas容器,注意,该容器可以提前写入dom,也可以像上述代码所示,动态创建。两种调用方法并无区别,如果动态创建,不挂进dom树,则该容器全程是不可见的,所以对于单张一次性的图片生成,更推荐这种方式。

width、height

从待转换的源dom取得,如果源dom本身高度也处在动态变化中,请在源dom被正确绘制之后,再取宽高。

scale

一个影响表现的关键参数。如果不设置,在移动端会生成一张非常模糊的图片! 这也是使用html2canvas最常见的问题,这是由canvas本身的绘制原理导致的。因为移动端设备屏幕尺寸非常多,碎片化严重,所以我们常常使用rem等技术,在移动端使用比屏幕分辨率更大的素材图片,但canvas的绘制默认是按照屏幕分辨率来进行的,如果我们不对它做手工放大,素材图片就会被压缩。 scale参数就是用来做放大的,推荐设置为2,此时生成的分享图是屏幕绘制区域的两倍,如果对品质要求较高,需要适配三倍屏的情况,也可以动态切换为3。

background

canvas绘制时的底色填充色,默认为白色。 采用默认值,对于矩形不透明dom没有任何影响,但如果源dom中使用了圆角,透明度等属性,反应在dom上,就会生成一张白底的图。

这样当然不是理想的效果,1.0以上版本通过传入参数,可以在初始化canvas的时候,用透明底色作为填充色,这样就可以愉快地生成圆角,半透明等图片了。

完整示例:

https://wendychengc.github.io/html2canvas-demo/demo/index.html

请使用pc浏览器打开。 该例子中,PC端在取到分享图后,通过Blob标签的方案,实现点击保存到本地功能。 (注意,因为html2canvas中使用了promise、assign等es6语法,故如需支持ie,需要构建时额外添加babel polyfill)

跨域问题:

如果dom使用的图片有跨域,在canvas执行toDataUrl()方法时,由于浏览器的安全机制。会抛出一个error。

若要使用跨域图片,一方面图片存储服务器需要配置Access-Control-Allow-Origin,支持来自页面所在域的跨域请求,另一方面,需要手动指定图片的crossOrigin属性。

代码语言:javascript
复制
img.crossOrigin = '*';

再奉上一个移动端的例子,请使用QQ/微信/TIM扫码打开

注意,移动端对于h5直接下载图片,常有一些安全限制,好在微信/QQ/TIM客户端上,对img标签都进行了长按事件的绑定,我们不需要额外的代码,只要引导用户长按img标签区域,就可以唤起端上的保存/分享功能,完成后续链路。

三、常见“谣言”

网上繁杂的祖传代码里,有一些常见的误区,先总结如下

1、能转成图片的必须是web上的可见区域?

No。

其实这个限制并不在于“可见”,而是在于dom引擎“渲染”,即使渲染后的结果通过某种方式被隐藏起来,一样可以画进canvas。比如下面的例子:

外层容器高度比内层更小,且overflow属性为hidden,此时,就可以转换一个比视觉区域更高的图。

这里笔者也提供了一个例子:

https://wendychengc.github.io/html2canvas-demo/demo/partial.html

由于没有美术大佬的协助,例子的效果比较简陋,但在实际应用中,这是一个很灵活的特性,比如我们可以在dom中把“长按二维码扫描”等等引导语隐藏起来,在用户保存或分享时,才在图片中展示。或者由web侧生成超过一屏的长图,分享到朋友圈等等。

请抱紧美术大佬的大腿,灵活使用。

2、只能使用img标签,不能使用background?

No。 这个限制在1.0版本中并不存在,源dom中,不论是img标签,还是空div的background-image,都可以正确绘制。 html2canvas的实现原理并不深奥,就是递归遍历每个dom,并且把每个html元素和css属性均转换为canvas api,所以确实有一些高级属性不支持,比如box-shadow。 如需支持这些属性,只能深入源代码修改了。

四、体验优化

在实际项目中,上述过程对用户是无感知的,并不需要在界面上同时展示dom和img两份相同的内容,所以我们通常会选择把dom放在img下方。 这时,因为html2canvas是异步过程,所以页面会有一次闪动,图片越大越明显,是令人难受的体验。

如果我们把dom设为不可见,则转换出的是一张空白图。 如果把图片设为不可见,则无法愉快地在移动端使用长按保存&分享等能力。 这里的关键还是上面说过的,“不可见”则“不渲染”的矛盾。如果我们给一个dom元素设置display:none、visiblity:hidden属性,都有这个问题。

那么有没有视觉不可见,但逻辑上会渲染的属性,有,opacity:0 我们只要把img盖在dom上方,同时img的opacity设置为0,用户就看不见这张图,但浏览器仍会识别它。

我们的完整dom结构如下图

消灭闪动、用户无感知,不模糊,且支持长按分享√

下面提供一个运营活动中的例子,完成电影台词测试,根据用户答案合成不同的结果图,并将用户昵称也包含在图上。

支持微信/QQ/TIM三端的昵称和测试结果动态合成。

全文完,感谢阅读,欢迎参考,祝大家bug越来越少。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、应用场景
  • 二、基础使用
    • canvas
      • width、height
        • scale
          • background
            • 完整示例:
              • 跨域问题:
              • 三、常见“谣言”
                • 1、能转成图片的必须是web上的可见区域?
                  • 2、只能使用img标签,不能使用background?
                  • 四、体验优化
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档