小程序中canvas和svg的踩坑记录
自己总结的一些踩坑记录~
前言
我之前负责的是一款百度旗下财经类股票自选股的智能小程序。业务中经常会有图表类的需求。比如 k线图,分时图,各种财务报表,数据分析图等等..
图1
图2
canvas与svg
在这些绘制图表类的需求中,一般会在canvas与svg二者中选择技术实现方案(如图1为canvas, 图2为svg),且往往选择canvas居多。
小程序中,canvas和svg各有所长,亦各有所短,我们只有在掌握其各自的利弊,才能在项目中应用自如,少入坑。
canvas
小程序中使用canvas来实现绘制各种图表,还是比较常见了,也有一些优点。
优点:
- 支持动画
- 支持复杂的交互
- 可操作图片,保存图片等..
缺点:
1.层级问题:
组件分为原生组件和小程序组件,若没有解决同层渲染问题,就有出现层级的问题,比如, swiper组件(滑块组件)不能与canvas 一起使用。
这里对于常用的小程序: 微信小程序,百度智能小程序,支持宝小程序,三家对于canvas组件分析一下。
微信小程序:在2.9.0版本后支持了同层渲染(当前最新版本v2.14.3 (2021-01-05))
百度小程序:目前还没有支持同层渲染(所以图2中使用了svg方案)
支付宝小程序:在2.6.2基础库之前为非原生组件,后续版本会切换为原生组件(秀~~),但是切换为原生组件是否支持同层渲染呢?个人认为应该是不支持的,我没调研… (写到这里有点困了….)
2.复杂的交互容易造成卡顿,影响体验,需要降频处理
3.在高DPR(设备像素比)的情况保持细腻的画质,需要配套处理
1 2 3 4
| <!-- getSystemInfoSync().pixelRatio === 2 -->
<canvas width="200" height="200" style="width:100px;height:100px;"/>
|
svg
以微信,百度,支付宝三家小程序为例,小程序中使用svg的方式分为3种;
image 组件
image组件 支持svg格式图片
svg标签
微信小程序支持使用 Cax 引擎高性能渲染 SVG。
本人没有亲测调研(太困啦….)
但是看到了这句总结:
使用小程序内置的 Canvas 渲染器, 在 Cax 中实现 SVG 标准的子集,使用 JSX 或者 HTM 描述 SVG 结构行为表现
所以最后渲染出得还是canvas, 不过微信已经支持canvas(2d)同层渲染了,所以还是很哇塞的.
文档链接: https://developers.weixin.qq.com/community/develop/article/doc/000ca493bc09c0d03a8827b9b5b013
background-url
最后就是使用背景图片的方式,也是我在小程序中常用的方式.
这里先看一个例子
模版中使用元素的background-url属性动态绑定svg图片
1 2
| // .wxml | .swan | .axml <view style="width:148px;height:148px;background:url('{{ svgTpl }}') no-repeat center"></view>
|
js文件中赋值svgTpl变量
这里注意一下:常用的svg图片格式分为两种
- data:image/svg+xml;base64,….
- data:image/svg+xml;utf8,<svg…
至于为什么这里用base64编码的呢,因为要敲黑板了, 重点了! 稍后说
1 2 3 4 5 6
| Page({ data: { svgTpl:'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjE0OCIgaGVpZ2h0PSIxNDgiIHZpZXdCb3g9IjAgMCAxNDggMTQ4Ij48Y2lyY2xlIGN4PSI3NCIgY3k9Ijc0IiByPSI3MCIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0Y3NTM0RiIgc3Ryb2tlLWRhc2hhcnJheT0iMTE2LjExMzI2NDQ3NjY3ODc1IDQzOS44MjI5NzE1MDI1NzEiIHRyYW5zZm9ybT0icm90YXRlKDAsIDc0LCA3NCkiPjwvY2lyY2xlPjxjaXJjbGUgY3g9Ijc0IiBjeT0iNzQiIHI9IjcwIiBzdHJva2Utd2lkdGg9IjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkE5NzAwIiBzdHJva2UtZGFzaGFycmF5PSIxNTQuODE3Njg1OTY4OTA1IDQzOS44MjI5NzE1MDI1NzEiIHRyYW5zZm9ybT0icm90YXRlKDEwNS44NCwgNzQsIDc0KSI+PC9jaXJjbGU+PGNpcmNsZSBjeD0iNzQiIGN5PSI3NCIgcj0iNzAiIHN0cm9rZS13aWR0aD0iOCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBmaWxsPSJub25lIiBzdHJva2U9IiM0QUM4ODEiIHN0cm9rZS1kYXNoYXJyYXk9Ijc3LjQwODg0Mjk4NDQ1MjUgNDM5LjgyMjk3MTUwMjU3MSIgdHJhbnNmb3JtPSJyb3RhdGUoMjQzLjM2LCA3NCwgNzQpIj48L2NpcmNsZT48Y2lyY2xlIGN4PSI3NCIgY3k9Ijc0IiByPSI3MCIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzODhGRiIgc3Ryb2tlLWRhc2hhcnJheT0iMzguNzA0NDIxNDkyMjI2MjUgNDM5LjgyMjk3MTUwMjU3MSIgdHJhbnNmb3JtPSJyb3RhdGUoMzE3LjUyMDAwMDAwMDAwMDA0LCA3NCwgNzQpIj48L2NpcmNsZT48L3N2Zz4=' } })
|
效果图
小程序中的utf8与base64
现在在这里说一下为什么上面的示例中,采用了base64编码的svg图片而没有用utf8编码的图片
大家可以先复制如下代码在72版本以上的chrome浏览器与其他浏览器(比如Safari)
1 2 3 4 5 6 7 8 9 10 11
| data:image/svg+xml;utf8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="148" height="148" viewBox="0 0 148 148"> <circle cx="74" cy="74" r="70" stroke-width="8" stroke-linecap="round" fill="none" stroke="#F7534F" stroke-dasharray="116.11326447667875 439.822971502571" transform="rotate(0, 74, 74)"></circle> <circle cx="74" cy="74" r="70" stroke-width="8" stroke-linecap="round" fill="none" stroke="#FA9700" stroke-dasharray="154.817685968905 439.822971502571" transform="rotate(105.84, 74, 74)"></circle> <circle cx="74" cy="74" r="70" stroke-width="8" stroke-linecap="round" fill="none" stroke="#4AC881" stroke-dasharray="77.4088429844525 439.822971502571" transform="rotate(243.36, 74, 74)"></circle> <circle cx="74" cy="74" r="70" stroke-width="8" stroke-linecap="round" fill="none" stroke="#3388FF" stroke-dasharray="38.70442149222625 439.822971502571" transform="rotate(317.52000000000004, 74, 74)"></circle> </svg>
|
原因:
Chrome 71和更早版本在URI中支持#,但在72之后不支持, 在上述代码中我们的颜色使用的是带有#号的16进制。
补充一下:
微信,百度,支付宝小程序中js引擎
Android: V8
Ios: jsCore
所以如果在安卓端使用svg背景图时要特别注意一下。
解决方案:
A: 使用utf8编码时,颜色特别处理
避开#危险符号,颜色使用rgb.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const hexToRGB = (hex) => { if (typeof hex !== 'string') { return null; } if (!/^#[0-9ABCDEFabcdef]{1,6}$/.test(hex)) { return null } let pureStr = hex.slice(1); if (pureStr.length === 3) { pureStr = [].concat(...[...pureStr].map(v => [v]).map(v => v.concat(v))).join('') } let result = []; for (let i = 0; i < pureStr.length; i += 2) { result.push(parseInt((pureStr[i] + pureStr[i + 1]), 16)); } return `rgn(${result.join(', ')})` }; hexToRGB('#F0F0F0')
|
B: 采用base64编码
将计算好的svg字符串再编码成base64编码,
这里是我所用到的方法, 大佬们可以简单参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| * svg base64编码图片 * @param svgText {string} 拼接的svg字符串 * @returns imgStr {string} base64转码后的图片 */ function getBase64Url(svgText, w, h) { if (!svgText) { return ''; } const agreement = 'data:image/svg+xml;base64,'; svgText = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">` + svgText + '</svg>'; // 必须这样搭配,可以支持中文且可编码成base64, 别问为什么... 好困.... svgText = encodeURIComponent(svgText); const base64Url = btoa(unescape(svgText)); return agreement + base64Url; }
|
其他:
若svg中设计除颜色之外的地方用到#,或没办法替换#的情况,只能用B方案
最后
以上就是对小程序中使用canvas与svg的简单介绍了, 欢迎各位大佬赏阅。
最后再安利一波,上面引用的小程序案例是百度旗下财经类自选股小程序:百股经,欢迎各位大佬试用~