前端生成海报图技术选型与问题解决

作者:vivo 互联网大前端团队 - Tian Yuhan

本篇文章主要聚焦海报图分享这个形式,探讨纯前端在H5&小程序内,合成海报到下载到本地、分享至社交平台整个流程中可能遇到的问题,以及如何解决。

一、引言

绝大多数的电商平台都会设计分享裂变的功能,激励用户进行分享,这是一种拉新促活的常见措施。提到分享裂变,就免不了需要生成用户专属的分享链接或者专属海报。当然分享推广的形式多种多样,有文本链接、网页链接、图片邀请码、小程序、音视频等等。

本篇文章主要聚焦海报图分享这个形式,探讨纯前端在H5&小程序内,合成海报到下载到本地、分享至社交平台整个流程中可能遇到的问题,以及如何解决。

二、选型参考

2.1 实现方式

根据海报图生成渠道的差异,可以将海报图生成分为:客户端生成、前端生成和服务端生成,以下是从技术实现、兼容性、生成速度和功能受限四个维度进行的对比:

图片

  • 对于排版复杂的场景: 建议使用服务端Node.js渲染的方式【如Puppeteer图片生成】;
  • 对于排版简单,但用户请求并发大的场景:建议使用前端或客户端生成海报图;

  • 对于需多端投放,样式动态更新的场景:建议使用前端生成海报图;

  • 对于叠加用户交互的场景:建议使用客户端原生绘制的方式生成。

本篇文章着重聚焦前端H5 & 小程序内生成海报图的技术实现,如果大家对服务端、客户端生成海报图的方案感兴趣,文末有相关链接可供参考。

2.2 H5 生成海报图技术选型

H5 生成海报图常推荐的方案有三种:

(1)html2canvas:基于页面中存在且可用的DOM结构、样式,构建“截图”,绘制到canvas画布中。

下面是html2canvas的技术解析,详细讲解可以参考文末链接。

图片

(2)dom-to-image:将DOM 节点序列化为XML,包装到SVG中,再转为图片。

下面是dom-to-image的技术解析,详细讲解可以参考文末链接。

图片

(3)canvas 纯原生绘制

以下是示例代码,这里不做赘述

图片

html2canvas和dom-to-image都是用于在canvas的基础上封装的库,由于底层实现方式的不同,它们各有优缺点,以下是从几个维度进行的优缺点对比。

图片

那么如何快速的确认当前的项目应该用哪个组件库呢,有没有必要逐个去验证,这里给到几点建议:

  • 如果需要处理的页面比较复杂或者需要支持SVG图片渲染,在html2canvas中可能更好一些。

  • 如果需要稳定的文字、图片渲染能力或者处理结构化数据的能力,在dom-to-image中可能更好一些。

  • 如果页面基本是图片资源,那么使用原生canvas性能是最好的。

2.3 小程序生成海报图技术选型

与H5类似,小程序生成海报图在项目中常用的也有三种方案:

(1)wxml-to-canvas:利用微信小程序提供的canvas组件,将指定的wxml转化成canvas绘图,然后再将其导出为图片

详见:微信小程序wxml-to-canvas官方文档

图片

(2)Painter:利用JSX语法,将绘图的操作转化成绘制命令,并生成对应的canvas绘图

下图来源于Painter 2.0官方介绍

图片

(3)Canvas/Canvas2D

以下是小程序2D Canvas 官方示例代码,详见微信小程序基础能力/画布

图片

  • 从技术实现上看,wxml-to-canvas 是基于2d类型的Canvas组件能力,对基础库有要求,需在2.7.0以上。在canvas2d的基础上,封装了绘制text、image和view的能力,提供wxml绘制到canvas和导出图片的方法。Painter不仅支持2d类型的canvas,同时兼容了低版本canvas组件。同时支持更多复杂样式的绘制,对于网络素材实现了一套LRU存储机制,无需重复下载图片,并增加了容错机制。

  • 从使用方法上看,wxml-to-canvas使用相对较简单,只需要在小程序中引入wxml-to-canvas库,然后传入需要生成海报图的wxml代码和画布的宽高即可。而Painter需要在小程序中使用类JSX的语法绘制所有图形,覆盖小程序页面中相应的canvas组件。

针对两种不同库,wxml-to-canvas更适合将已有的wxml结构转换为图片,对于复杂的定制化设计需要在wxml中拼接、排版的海报来说,并不是最佳选择。Painter则更适合在小程序中进行复杂绘制操作。

但从另一个角度看,如果对绘制性能要求比较高的业务来说,wxml-to-canvas 相比Painter更符合诉求。当然,如果仅涉及图片间的快速组合,也可以使用canvas原生绘制的能力,只是要关注图像绘制和canvas导出的实际问题。

前面两个章节,主要解决在绘制选型上的疑虑,下一步面对的就是如何解决实现过程中出现的问题。

三、H5 常见问题

3.1  设计还原效果

设计还原除了关注最基础的字体样式、排版,同时也要兼顾深色模式适配、字号大小适配、页面缩放、浏览器兼容性。这些因素无疑给前端生成海报图带来巨大的压力,下面我也将从提到的这些方面进行展开,也可以作为大家选型参考或者解决方案的参考。

当业务需求涉及复杂的文本排版,那么建议直接放弃canvas原生绘制,难度大风险大耗时长。第三方库处理逻辑大同小异,本质上都是基于DOM 转为 canvas,但并不是真正的浏览器截图,都会存在一些共性问题。

3.1.1 字体样式

如下图所示,当前设备生效的是特殊字体,那么生成海报图得到的有可能是特殊字体,出现与设计不符,不同用户生成的海报图效果不一致的情况。

图片

如何规避这个问题呢?复制DOM并隐藏,对复制的DOM嵌入网络字体包,保证设计效果。如果是固定文案,也可以采用嵌入图片的方式。

3.1.2 排版

排版常遇到的问题是文本换行、超长字符打点,通常也会叠加系统字号大小、页面缩放一起出现。

图片

问题1: 文本换行打点怎么办?

  • 第三方库中一般会有文本换行相关的处理

    类似html-to-canvas它的处理基于"CanvasRenderingContext2D.measureText()",在绘制时测量每个字符的宽度,进行渲染位置的更新。基于这个处理方案,超长字符打点的问题是很难解决的。

    而dom-to-canvas是基于svg转图片的方式,天然的具有文本排版处理的能力,这也是这个库的优势所在。

问题2: 那如果叠加页面缩放呢?

一般h5都具有页面缩放适配的能力,复制DOM也会继承该处理,不会有太大的影响。

问题3: 如果页面有设置大字号模式,DOM在不同字号大小下展示样式有区别,但是需要导出图片样式保持一致呢?

那么就需要在复制DOM时,针对字号大小进行逆向处理,比如系统字体将字号放大1.25倍,在该场景下,复制DOM时就需要对字号进行缩小1.25倍。

3.1.3 适配

深色模式适配,分为两种:

  • 一种是系统级别的反色:系统级别的反色不会侵入到DOM,复制的DOM仍然与浅色模式下一致,导出的图片也不会受到影响。

  • 一种是自定义样式的反色:自定义的反色对DOM样式有侵入,复制DOM及其样式,导出的图片可能受影响,如下图所示。只需要在复制DOM时将侵入的样式或者控制深色模式的判断条件去除即可。

图片

兼容性适配,与canvas和foreignObject的兼容性相关,一般来说,不建议在Android低版本中使用,性能较差。

3.2   跨域问题

只要使用canvas绘制图片,必然绕不过的就是跨域的问题,解决跨域问题有以下几种思路:

  1. 代理服务器:通过配置服务器代理,将原本跨域的图片请求转为同域的图片请求。从根源上解决跨域问题。针对图片来源单一可控的场景、具备代理配置的能力的业务场景,建议采用该方案。

  2. 解决资源跨域:前提需要有权限访问被请求的跨域服务器,可以通过设置服务器上的 CORS头信息“Access-Control-Allow-Origin: *  或者特定域名”来实现,然后再解决资源的跨域问题。下面就聚焦这个场景,展开介绍下:

3.2.1 crossOrigin

在请求图片时,需要增加属性 img.crossOrigin = 'anonymous' ,告知跨域服务器,本次请求可以匿名访问,不需要提供凭证。代码示例如下图所示:

图片

设置这个就可以一劳永逸了吗,并不是,还可能出现图片缓存引起的请求跨域问题:在同一个页面中,如果canvas需要绘制的图片已经加载过了,浏览器会默认缓存下来,当canvas使用这张图片时,浏览器会直接返回缓存的图片。这时被缓存后的图片就不展示CORS请求响应头携带Access-Control-Allow-Origin的要求了,就会导致报错。

解决方案最简单的是给跨域请求追加时间戳,但这个方案会造成资源重复请求,也可能会有缓存击穿的风险。

3.2.2 ajax+Blob/base64

通过额外发一次请求,将图片转为blob或者直接用base64的图片格式,避免单张图片频繁请求。但如果涉及到的图片比较多,也会带来内存压力问题,需要注意资源及时释放。代码示例如下图所示:

图片

3.3  生成性能

  1. 控制复制DOM的数量 原因:海报图的生成耗时与复制dom的数量成正比。

  2. 如果采用的是iframe启用的方式,减少iframe启动次数。

  3. 一般组件库是基于一个DOM生成一张海报图,如果一次需要生成多张海报图,可以合并DOM,启用一次iframe,批量生成多张图片。

  4. 分割操作,将文本相关耗时的操作前置,最后简单的图片+图片的合并,使用canvas原生绘制,也是一种提升性能的方案。

四、小程序常见问题

4.1 元素绘制

不论是使用哪种方案,在小程序绘制海报图时,需要提前设定好绘制的尺寸,甚至wxml-to-canvas要求为每个元素设置固定的宽度和高度,否则会出现布局错误。所以自适应高度在小程序端是不存在的,对于排版样式就进行了初步限定。

同时,元素之间是存在层级关系的,与书写的结构强关联,这个要尤为注意。

对于元素及其样式的支持是有限的,不同库的支持程度也是不一致的,可根据业务进行选择,这里简单举例对比下:

图片

4.2 存储问题

以下是wxml-to-canvas的源码,在绘制图片时,往往是绘制网络图片,需要从远程拉取图片,转为本地临时文件存储【wx.downloadFile】,canvas绘制取临时文件地址【tempFilePath】,然后进行绘制。保存图片时再将canvas导出为临时文件【wx.canvasToTempFilePath】。

图片

若在小程序内触发保存图片到本地【wx.saveImageToPhotosAlbum】,需要进行权限校验,这部分需要业务自行处理,如下所示。

图片

如果选择使用Painter库绘制海报图,且为了提升性能开启了LRU存储机制,这时就要关注缓存文件的有效性,官方也在小程序 LRU 存储设计中进行了重点讲解。

4.3 生成性能

研究过Painter源码的同学可能关注到在canvas导出图片的函数中,存在一个300ms的延迟,这个是为什么呢?300ms这个延迟可以去掉吗?

图片

答案是:这个逻辑下不可以省略!否则可能出现图片缺失的问题。

Painter提供了canvas2d 接口和canvas旧接口两套逻辑,而延迟300ms出现在旧接口逻辑体系中。这是因为旧接口使用的是downloadFile将网络图片缓存到本地后再进行drawImage,而整个绘制-导出过程中没有监听图片加载完成的步骤,因此有概率出现图片未加载完成就直接绘制的情况,进而出现内容缺失。虽然加入了定时器延长时间,但不同的机器不同的图片绘制时间不同,设定单一的延迟等待并不能从根本上解决问题,反而影响生成图片的性能。

相应的,在canvas2d的体系中则可以通过 Canvas.createImage 创建图片对象,并监听图片的load事件,在回调之后执行ctx.drawImage绘制到canvas中,这样就可以有效的解决上述问题。

针对这种场景,如果有其他比较好的解决方案,欢迎读者在评论区留言。

ps: 微信官方正在推出Skyline/snapshot的截图组件,也可以实现wxml/wxss导出图片的功能,详见微信小程序Skyline /snapshot

五、思考总结

本文主要聚焦前端生成海报图的技术选型,不同选型可能出现的问题及其解决思路。期望通过本篇文章,能够让读者在接收到相关需求时,对于一些容易踩坑的点做好前期评估。如果当前正在开发相关的需求,遇到相似的问题能够提供一些解决思路。

同时如果大家有更好的解决方案,欢迎在评论区留言。

当然,随着AI技术的发展,生成海报图可能不再需要通过代码逻辑实现。可以通过数据模型训练,更加智能化的快速生成营销海报图,那么以上的问题将不再是问题。我们也将持续关注在ToC业务中相关的实现方案,与大家一起探讨。

参考连接:

  1. html2canvas实现浏览器截图的原理(包含源码分析的通用方法)

  2. 前端关于html2canvas截图的问题?

  3. 使用html2canvas在前端生成图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/24244.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

30、matlab现代滤波:维纳滤波/LMS算法滤波/小波变换滤波

1、信号1和信号2的维纳滤波 实现代码 N 2000; %采样点数 Fs 2000; %采样频率 t 0:1 / Fs:1 - 1 / Fs; %时间序列 Signal1 sin(2*pi*20* t) sin(2*pi*40* t) sin(2*pi*60* t); Signal2[2*ones(1,50),zeros(1,50),-1*ones(1,100),zeros(1,50),-2*ones(1,50),zeros(1,50),1…

【C语言】05.数组

一、数组的概念 本文来介绍数组,首先我们需要了解数组是什么? 数组是⼀组相同类型元素的集合。 • 数组中存放的是1个或者多个数据,但是数组元素个数不能为0。 • 数组中存放的多个数据,类型是相同的。 数组分为⼀维数组和多维数组…

Go源码--sync库(2)

简介 这边文章主要讲解 Sync.Cond和Sync.Rwmutex Sync.Cond 简介 sync.Cond 经常用来处理 多个协程等待 一个协程通知 这种场景, 主要 是阻塞在某一协程中 等待被另一个协程唤醒 继续执行 这个协程后续的功能。cond经常被用来协调协程对某一资源的访问 ants协程池…

Win10 Edge提示兼容性问题打不开|解决浏览器兼容性问题

Edge有时候会与某些安全软件不兼容,导致报错 报错代码:STATUS_INVALID_IMAGE_HASH 解决Edge浏览器兼容性问题方法/步骤: 1、按 Win R 组合键,打开运行,并输入 regedit 命令,确定或回车,可以…

SAP ERP系统主要模块简介

SAP系统通过提供一系列高度灵活的模块,满足企业在不同业务领域的需求。这些模块不仅功能齐全且相对独立,但它们之间又能紧密协作,共同构筑一个协同高效的工作环境。 财务会计(FI)模块 它涵盖了总账、应收账款、应付账…

DexCap——斯坦福李飞飞团队泡茶机器人:更好数据收集系统的原理解析、源码剖析

前言 2023年7月,我司组建大模型项目开发团队,从最开始的论文审稿,演变成目前的两大赋能方向 大模型应用方面,以微调和RAG为代表 除了论文审稿微调之外,目前我司内部正在逐一开发论文翻译、论文对话、论文idea提炼、论…

k8s:优雅关闭pod的简单例子

先通过Dockerfile创建一个image vim Dockerfie <<<< 内容如下&#xff1a; FROM centosRUN sed -i -e "s|mirrorlist|#mirrorlist|g" /etc/yum.repos.d/CentOS-* RUN sed -i -e "s|#baseurlhttp://mirror.centos.org|baseurlhttp://vault.centos.o…

Qsemaphore

Qsemaphore 实现 给while循环阻塞延时 基本思路就是&#xff1a; whlie循环里面 通过m&#xff3f;bthreadFlag&m_bStatus这两个标志位&#xff0c;判断是否进入while循环&#xff0c;再根据40行的acquire&#xff08;&#xff09;来阻塞循环&#xff0c;因为定时器的槽函数…

SQL Server数据库xp_cmdshell提权笔记

文章目录 一、简介二、搭建环境三、利用条件1、查询 xp_cmdshell 是否开启&#xff0c;返回为1则证明存在2、判断权限是不是sa&#xff0c;回是1说明是sa3、开启xp_cmdshell4、关闭xp_cmdshell 四、获取数据库权限1、成功获取sqlserver&#xff0c;进行登陆2、开启xp_cmdshell权…

代码随想录算法训练营第31天(py)| 贪心 | 455.分发饼干、376. 摆动序列、53. 最大子序和

455.分发饼干 力扣链接 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff1b;并且每块饼干 j&#…

Docker|了解容器镜像层(1)

引言 容器非常神奇。它们允许简单的进程表现得像虚拟机。在这种优雅的底层是一组模式和实践&#xff0c;最终使一切运作起来。在设计的根本是层。层是存储和分发容器化文件系统内容的基本方式。这种设计既出人意料地简单&#xff0c;同时又非常强大。在今天的帖子[1]中&#xf…

29网课交单平台 epay.php SQL注入漏洞复现

0x01 产品简介 29网课交单平台是一个专注于在线教育和知识付费领域的交单平台。该平台基于PHP开发,通过全开源修复和优化,为用户提供了高效、稳定、安全的在线学习和交易环境。作为知识付费系统的重要组成部分,充分利用了互联网的优势,为用户提供了便捷的支付方式、高效的…

继承-进阶

父子类成员共享 普通成员对象/父子间不共享&#xff0c; 成员独立 函数成员共享&#xff08;函数不存储在对象中&#xff09; 子类由两部分构成&#xff1a;父类中继承的成员和子类中新定义成员 继承方式 子类中存在父类private成员但不可直接访问&#xff08;及时在类中&am…

微信如何防止被对方拉黑删除?一招教你解决!文末附软件!

你一定不知道&#xff0c;微信可以防止被对方拉黑删除&#xff0c;秒变无敌。只需一招就能解决&#xff01;赶快来学&#xff01;文末有惊喜&#xff01; 惹到某些重要人物&#xff08;比如女朋友&#xff09;&#xff0c;被删除拉黑一条龙&#xff0c;那真的是太令人沮丧了&a…

加密经济浪潮:探索Web3对金融体系的颠覆

随着区块链技术的快速发展&#xff0c;加密经济正在成为全球金融领域的一股新的浪潮。而Web3作为下一代互联网的代表&#xff0c;以其去中心化、可编程的特性&#xff0c;正深刻影响着传统金融体系的格局和运作方式。本文将深入探讨加密经济对金融体系的颠覆&#xff0c;探索We…

机器学习实验----支持向量机(SVM)实现二分类

目录 一、介绍 (1)解释算法 (2)数据集解释 二、算法实现和代码介绍 1.超平面 2.分类判别模型 3.点到超平面的距离 4.margin 间隔 5.拉格朗日乘数法KKT不等式 (1)介绍 (2)对偶问题 (3)惩罚参数 (4)求解 6.核函数解决非线性问题 7.SMO (1)更新w (2)更新b 三、代…

此表单不安全,因此系统已关闭自动填充功能

问题截图&#xff1a; 截图就不放了&#xff0c;公司的系统不方便&#xff0c;就是form表单会有个提示“此表单不安全&#xff0c;因此系统已关闭自动填充功能” 解决思路&#xff1a; 1、问题原因 使用https访问&#xff0c;但表单提交地址是http的 2、查看表单配置 表单…

MSP430单片机控制流水灯,Proteus仿真

作品功能 本项目利用MSP430单片机控制一个简单的流水灯&#xff0c;通过按键切换流水灯的模式。用户可以通过按键控制LED灯的方向&#xff0c;从左向右或从右向左依次点亮。 作品的硬件材料 MSP430单片机 具体型号&#xff1a;MSP430G2553 LED灯 数量&#xff1a;8个类型&…

文本审核纠错

探索高效文本审查利器&#xff1a;Word Checker-CSDN博客 GitHub - shibing624/pycorrector: pycorrector is a toolkit for text error correction. 文本纠错&#xff0c;实现了Kenlm&#xff0c;T5&#xff0c;MacBERT&#xff0c;ChatGLM3&#xff0c;LLaMA等模型应用在纠错…

如何设置vue3项目中默认的背景为白色

方法1&#xff1a;通过CSS全局样式 在全局CSS文件中设置&#xff1a; 如果你的项目中有全局的CSS文件&#xff08;如App.vue或专门的CSS文件&#xff09;&#xff0c;你可以直接设置body或html标签的背景颜色。 在src/assets文件夹中&#xff08;或者任何你存放CSS文件的地方&a…