TGideas-腾讯互动娱乐创意设计团队

图片合成类H5总结

最近越来越多图片合成类的H5需求,让用户通过H5页面的交互选择,最后根据用户的选择生成一张合成图片,让用户长按保存图片并引导用户分享该图片。这里我根据自己最近做的几个图片合成的H5需求,对这类的技术点做个简单总结,方便后面的同学少踩一些坑。

项目背景

最近越来越多图片合成类的H5需求,让用户通过H5页面的交互选择(拍照、选取相片),最后根据用户的选择生成一张合成图片,让用户长按保存图片并引导用户分享该图片。

这里先给大伙看几个最近使用图片合成技术做的几个H5:

h5图片合成

技术介绍

讲到图片合成,应该大家都能想到使用canvas来实现。这里也不外乎如此,因为这块的技术点其实跟设计师用的ps很类似,就是将分层的psd文件,导出一张合并后的jpg/png基本原理是一样的。但在实现的过程中,会遇到各种各样的问题,这里我根据自己做过的这类需求做简单的总结:

一、相对简单一些的布局

对于一些简单一些的合成方式,只需要将几张图片合成的,我们可以直接使用canvas的drawImage()方法即可,drawImage的语法也很简单:

关于drawImage的具体使用方法,大家可以点击上面的链接查看。这里给大家看个王者荣耀跟FIFA足球世界合作的H5:

h5图片合成

这里我们看下图片合成的关键代码:

       从代码中,可以看到,这个H5其实是由背景图、国旗、左下角的文字图片以及二维码四张图,调用drawImage方法绘制到canvas合并而成。

二、DOM元素绘制到Canvas

     由于安全机制的问题,我们如果需要将DOM内容(如HTML)直接绘制到canvas,需要使用先使用一个SVG图像包含起来,然后再将SVG图像绘制到canvas中。这里比较棘手的是创建SVG图像,你需要将各种样式通过内联的方式挂载到xml字符串的svg中,再去canvas上去绘制,这对比较复杂的布局(特别是需要很多样式来控制)来说是特别麻烦的一件事情。

简单步骤如下:

    1、blob对象的MIME应为"iamge/svg+xml"

    2、一个<svg>元素

    3、在SVG元素中包含<foreignObject>元素

    4、包裹到<foreignObject>中的(格式化好的)HTML

这种方式比较复杂,不推荐,有兴趣的可以看下如下资料:

    1、将DOM对象绘制到canvas中

    2、Drawimg DOM Content To Canvas

    3、rasterizeHTML.js

二、html2canvas

     基于上面复杂的情况,在一些复杂一些的布局情况下,需要将图片、文本等多个元素合并的情况下,如果我们直接用drawImage跟fillText()(如果字体还是非常规字体)下,所以这里推荐html2canvas,该插件是使用js让我们能快速生成屏幕快照的,简单的说就是让我们能将html元素方便地绘制到canvas中。且使用方法非常简单:

    只需要将要绘制的元素放到一个容器内,然后调用html2canvas实例,就能遍历整个容器,将容器内的元素绘制到canvas内,这里有多个配置项可以使用,具体的可以参考官方文档:

绕坑指南

一、图片跨越问题

     在html2canvas是有对图片跨域的配置项的(参考上面的配置项),但如果图片的头信息里没有相应设置的情况下,这个配置是无效的:

 
       在竞猜世界杯冠军的这个H5中,我们需要拉取用户的微信/手Q头像合并图片,我们如果直接将获取到的头像图片插入到img中,再调用html2canvas绘制的话会出现无法绘制,头像区域变成空白的情况,这个是因为图片跨域导致的问题,即使我们已经在html2canvas的配置项中设置了useCORS:true,还是无法绘制。
       在我的需求中,我先将微信头像赋值给一个img对象的src,并开启该img对象的crossOrigin为允许跨域,然后绘制改img对象到canvas中,再将绘制的canvas的dataURL插入到我们最终要合并的头像的img元素中,再去调用html2canvas进行绘制,就能解决这个问题了。而手Q头像由于他们服务器对跨域资源的设置问题,目前使用同样的方案,还是无法绘制。
       我们暂时在手Q是使用默认头像去替代,在其他项目中是有过相应的解决方案的,不过这个需要运维的支持,将拉取的头像图片用我们这边的服务器进行中转一下(该方案这边在跟运维讨论中,后续看能不能有个通用且方便的解决方案)

二、使用html2canvas的坑

      在使用html2canvas的时候,尽可能将需要绘制的元素绝对定位的方式去实现,使用margin、padding容易导致最后绘制的元素错位或不在可视区域的问题(我在其中一个需求中就踩到了这个坑,死活看不到某个元素,就是因为有margin值的其他元素将其顶出了画布区域)

三、合成图片模糊问题

       相信这个问题应该是很多人都会遇到的问题,网上也有一大堆相关的问题以及答案,在这里,我们需要除了需要将canvas的尺寸根据设备精度进行放大后,还需要在html2canvas的配置项中将canvas图片平滑属性设置为flase:

    还有一个点就是,使用html2canvas是合成的时候,尽量使用img图片,而不要使用背景图去合成(亲测两种方式最终合成的图片清晰度有明显区别

四、其他几个点

 在图片合成类的H5中,经常还会有拍照或选取用户相片进行合成的功能,在前面布冯H5中,我们也有这个功能。在这类需求中,我遇到了以下的问题:

1)拉取用户相机时,安卓跟ios表现不一致:

      默认情况下,我只设置capture="camera"属性就能正常拉起相机进行拍照以及选择用户相册的相片两个功能,而在ios部分系统版本设置了该属性会出现只能选取用户相册中的相片而无法拉取相机进行拍照的情况,所以在这里,只能区别安卓跟ios做处理:

2)在IOS下拍照的相片绘制到canvas的时候会被自动旋转90°:

    通过相机拍照的相片会带有EXIF数据,里面包含一个Orientation的角度属性值,下边是这个值跟其具体的呈现关系:

所以,为了让我们的相片能正常地绘制到canvas中,我们需要对其进行旋转处理:

图片生成

      最后我们需要将绘制到canvas中的图像生成图片,用户才能通过长按的方式保存图片,这里我们只需要调用canvas的toDataURL(type, encoderOptions)方法就可以获取到canvas的一个包含图片展示的data URI,我们可以通过type值指定图片格式为image/jpeg或者image/png,然后将改值赋值给img对象的src即可获取到一个包含合成图片信息的图片。

      也可以使用Canvas2Image 来快速地导出对应的canvas数据到图片对象中:

 

(【打赏二维码】https://shp.qpic.cn/cfwebcap/0/6dc8f068728c3cfa0f958e6cbfddb74a/0?width=200&height=200)

求经验
关闭

分享

返回顶部