今天,小程序正式支持 SVG

今天,小程序正式支持 SVG

写在前面

经过腾讯 Omi 团队的努力,今天你可以在小程序中使用 Cax 引擎高性能渲染 SVG!

SVG 是可缩放矢量图形(Scalable Vector Graphics),基于可扩展标记语言,用于描述二维矢量图形的一种图形格式。它由万维网联盟制定,是一个开放标准。SVG 的优势有很多:

  1. SVG 使用 XML 格式定义图形,可通过文本编辑器来创建和修改
  2. SVG 图像可被搜索、索引、脚本化或压缩
  3. SVG 是可伸缩的,且放大图片质量不下降
  4. SVG 图像可在任何的分辨率下被高质量地打印
  5. SVG 可被非常多的工具读取和修改(比如记事本)
  6. SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性、可编程星更强
  7. SVG 完全支持 DOM 编程,具有交互性和动态性

而支持上面这些优秀特性的前提是 - 需要支持 SVG 标签。比如在小程序中直接写:

<svg width="300" height="150">
 <rect
 bindtap="tapHandler" height="100" width="100"
 style="stroke:#ff0000; fill: #0000ff">
 </rect>
</svg>

上面定义了 SVG 的结构、样式和点击行为。但是小程序目前不支持 SVG 标签,仅仅支持加载 SVG 之后 作为 background-image 进行展示,如 background-image: url("data:image/svg+xml.......),或者 base64 后作为 background-image 的 url。

那么怎么办呢?有没有办法让小程序支持 SVG? 答案是有的!需要下面这些东西(站在巨人的肩膀上):

  1. JSX,史上最强 UI 表达式,支持书写 XML-Hyperscript 互转的 JS 语言
  2. 小程序内置 Canvas 渲染器
  3. Cax 最新渲染引擎
  4. HTM,Hyperscript Tagged Markup,可能是 JSX 的替代品或者另一种选择,使用ES标准的模板语法实现的 Hyperscript 运行时/编译时生成,preact 作者(也是google工程师)打造

这里稍微解释下 Hyperscript:

比如 JSX 中的

<div>
 Hello {this.props.name}
</div>

或者 js 中的 htm:

html`<div>
 Hello {this.props.name}
</div>`

最后都会被编译成:

h(
 "div",
 null,
 "Hello ",
 this.props.name
);

嵌套的 div 也会变编译成 h 嵌套 h,比如

<div>
 <div>abc</div>
</div>

编译后:

h(
 "div",
 null,
 h(
 "div",
 null,
 "abc"
 )
)

而 h 函数的定义也是相当简洁:

function h(type, props, ...children) {
 return { type, props, children }
}

通过 h 的执行可以 js 中拿到结构化的数据,也就是所谓的虚拟 dom。需要注意的是 htm 有轻微的运行时开销,jsx 没有。

一句话总结:

使用小程序内置的 Canvas 渲染器, 在 Cax 中实现 SVG 标准的子集,使用 JSX 或者 HTM 描述 SVG 结构行为表现

直接看在小程序种使用案例:

import { html, renderSVG } from '../../cax/cax'

Page({
 onLoad: function () {

 renderSVG(html`
<svg width="300" height="220">
 <rect bindtap="tapHandler"
 height="110" width="110"
 style="stroke:#ff0000; fill: #ccccff"
 transform="translate(100 50) rotate(45 50 50)">
 </rect>
</svg>`, 'svg-a', this)

 },

 tapHandler: function () {
 console.log('你点击了 rect')
 }
})

其中的 svg-a 对应着 wxml 里 cax-element 的 id:

<view class="container">
 <cax-element id="svg-c"></cax-element>
</view>

声明组件依赖

{
 "usingComponents": {
 "cax-element":"../../cax/index"
 }
}

小程序中显示效果:

可以使用 width,height,bounds-x 和 bounds-y 设置绑定事件的范围,比如:

<path width="100" height="100" bounds-x="50" bounds-y="50" />

需要注意的是,元素的事件触发的包围盒受自身或者父节点的 transform 影响,所以不是绝对坐标的 rect 触发区域。

再来一个复杂的例子,用 SVG 绘制 Omi 的 logo:

renderSVG(html`
<svg width="300" height="220">
 <g transform="translate(50,10) scale(0.2 0.2)">
 <circle fill="#07C160" cx="512" cy="512" r="512"/>
 <polygon fill="white" points="159.97,807.8 338.71,532.42 509.9,829.62 519.41,829.62 678.85,536.47 864.03,807.8 739.83,194.38 729.2,194.38 517.73,581.23 293.54,194.38 283.33,194.38 "/>
 <circle fill="white" cx="839.36" cy="242.47" r="50"/>
 </g>
</svg>`, 'svg-a', this)

小程序种显示效果:

在 omip 和 mps 当中使用 cax 渲染 svg,你可以不用使用 htm。比如在 omip 中实现上面两个例子:

 renderSVG(
<svg width="300" height="220">
 <rect bindtap="tapHandler"
 height="110" width="110"
 style="stroke:#ff0000; fill: #ccccff"
 transform="translate(100 50) rotate(45 50 50)">
 </rect>
</svg>, 'svg-a', this.$scope)
renderSVG(
<svg width="300" height="220">
 <g transform="translate(50,10) scale(0.2 0.2)">
 <circle fill="#07C160" cx="512" cy="512" r="512"/>
 <polygon fill="white" points="159.97,807.8 338.71,532.42 509.9,829.62 519.41,829.62 678.85,536.47 864.03,807.8 739.83,194.38 729.2,194.38 517.73,581.23 293.54,194.38 283.33,194.38 "/>
 <circle fill="white" cx="839.36" cy="242.47" r="50"/>
 </g>
</svg>, 'svg-a', this.$scope)

需要注意的是在 omip 中传递的最后一个参数不是 this,而是 this.$scope。

在 mps 中,更加彻底,你可以单独创建 svg 文件,通过 import 导入。

//注意这里不能写 test.svg,因为 mps 会把 test.svg 编译成 test.js
import testSVG from '../../svg/test'
import { renderSVG } from '../../cax/cax'

Page({
 tapHandler: function(){
 this.pause = !this.pause
 },
 onLoad: function () {
 renderSVG(testSVG, 'svg-a', this)
 }
})

比如 test.svg :

<svg width="300" height="300">
 <rect bindtap="tapHandler" x="0" y="0" height="110" width="110"
   style="stroke:#ff0000; fill: #0000ff" />
</svg>

会被 mps 编译成:

const h = (type, props, ...children) => ({ type, props, children });
export default h(
 "svg",
 { width: "300", height: "300" },
 h("rect", {
 bindtap: "tapHandler",
 x: "0",
 y: "0",
 height: "110",
 width: "110",
 style: "stroke:#ff0000; fill: #0000ff"
 })
);

。

所以总结一下:

  1. 你可以在 mps 中直接使用 import 的 SVG 文件的方式使用 SVG
  2. 你可以直接在 omip 中使用 JSX 的使用 SVG
  3. 你可以直接在原生小程序当中使用 htm 的方式使用 SVG

这就完了?远没有,看 cax 在小程序中的这个例子:

详细代码:

renderSVG(html`
<svg width="300" height="200">
 <path d="M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z" style="stroke:#ff0000; fill: black">
 <animate dur="32s" repeatCount="indefinite" attributeName="d" values="......太长,这里省略 paths........" />
 </path>
</svg>`, 'svg-c', this)

再试试著名的 SVG 老虎:

path 太长,就不贴代码了,可以点击这里查看

就这么多?未完待续...,后续补充:

pasiton 标签

import { html, renderSVG } from '../../cax/cax'

Page({
 onLoad: function () {

 const svg = renderSVG(html`
<svg width="200" height="200">
 <pasition duration="200" bindtap=${this.changePath} width="100" height="100" from="M28.228,23.986L47.092,5.122c1.172-1.171,1.172-3.071,0-4.242c-1.172-1.172-3.07-1.172-4.242,0L23.986,19.744L5.121,0.88
		c-1.172-1.172-3.07-1.172-4.242,0c-1.172,1.171-1.172,3.071,0,4.242l18.865,18.864L0.879,42.85c-1.172,1.171-1.172,3.071,0,4.242
		C1.465,47.677,2.233,47.97,3,47.97s1.535-0.293,2.121-0.879l18.865-18.864L42.85,47.091c0.586,0.586,1.354,0.879,2.121,0.879
		s1.535-0.293,2.121-0.879c1.172-1.171,1.172-3.071,0-4.242L28.228,23.986z"
 to="M49.1 23.5H2.1C0.9 23.5 0 24.5 0 25.6s0.9 2.1 2.1 2.1h47c1.1 0 2.1-0.9 2.1-2.1C51.2 24.5 50.3 23.5 49.1 23.5zM49.1 7.8H2.1C0.9 7.8 0 8.8 0 9.9c0 1.1 0.9 2.1 2.1 2.1h47c1.1 0 2.1-0.9 2.1-2.1C51.2 8.8 50.3 7.8 49.1 7.8zM49.1 39.2H2.1C0.9 39.2 0 40.1 0 41.3s0.9 2.1 2.1 2.1h47c1.1 0 2.1-0.9 2.1-2.1S50.3 39.2 49.1 39.2z"
 from-stroke="red" to-stroke="green" from-fill="blue" to-fill="red" stroke-width="2" />
</svg>`, 'svg-c', this)

 this.pasitionElement = svg.children[0]

 },

 changePath: function () {
 this.pasitionElement.toggle()
 }
})

pasiton 提供了两个 path 和 颜色 相互切换的能力,最常见的场景比如 menu 按钮和 close 按钮点击后 path 的变形。

举个例子,看颜色和 path 同时变化:

线性运动

这里举一个在 mps 中使用 SVG 的案例:

import { renderSVG, To } from '../../cax/cax'

Page({
 tapHandler: function(){
 this.pause = !this.pause
 },

 onLoad: function () {
 const svg = renderSVG(html`
 <svg width="300" height="300">
  <rect bindtap="tapHandler" x="0" y="0" height="110" width="110"
   style="stroke:#ff0000; fill: #0000ff" />
 </svg>`
 , 'svg-a', this)
 const rect = svg.children[0]
 rect.originX = rect.width/2
 rect.originY = rect.height/2
 rect.x = svg.stage.width/2
 rect.y = svg.stage.height/2
 this.pause = false
 this.interval = setInterval(()=>{
  if(!this.pause){
  rect.rotation++
  svg.stage.update()
  }
 },15)
})

效果如下:

组合运动

import { renderSVG, To } from '../../cax/cax'

Page({
 onLoad: function () {

  const svg = renderSVG(html`
  <svg width="300" height="300">
   <rect bindtap="tapHandler" x="0" y="0" height="110" width="110"
      style="stroke:#ff0000; fill: #0000ff" />
  </svg>`
  ,'svg-a', this)
  const rect = svg.children[0]
  rect.originX = rect.width/2
  rect.originY = rect.height
  rect.x = svg.stage.width/2
  rect.y = svg.stage.height/2

  var sineInOut = To.easing.sinusoidalInOut
  To.get(rect)
    .to().scaleY(0.8, 450, sineInOut).skewX(20, 900, sineInOut)
    .wait(900)
    .cycle().start()
  To.get(rect)
    .wait(450)
    .to().scaleY(1, 450, sineInOut)
    .wait(900)
    .cycle().start()
  To.get(rect)
    .wait(900)
    .to().scaleY(0.8, 450, sineInOut).skewX(-20, 900, sineInOut)
    .cycle()
    .start()
  To.get(rect)
    .wait(1350)
    .to().scaleY(1, 450, sineInOut)
    .cycle()
    .start()

   setInterval(() => {
     rect.stage.update()
   }, 16)
 }
})

效果如下:

其他

vscode 安装 lit-html 插件使 htm 的 html内容 高亮

还希望小程序 SVG 提供什么功能可以开issues告诉我们,评估后通过,我们去实现!

Cax SVG Github

参考文档

(0)

相关推荐

  • 微信小程序里使用SVG矢量图标方法详解

    在微信小程序开发过程中需要在小程序里使用SVG矢量图标,至于为什么要使用SVG图标相信看到这篇文章的你应该明白,如果你不明白请百度一下 微信小程序里使用SVG矢量图标有2种引入方法: 一.SVG图标转换为BASE64编码 使用 http://tools.jb51.net/transcoding/img2base64 工具把需要引入的SVG图标转换成BASE64编码 注意:生成BASE64编码时需要把开头的 data:image/svg; 修改成 data:image/svg+xml; 这个在线工

  • 微信小程序中的店铺评分组件及vue中用svg实现的评分显示组件

    在微信小程序中,有遇到要展示店铺评分,或者是订单完成后对商品进行评价,用到了星星展示,查了下,在微信中无法使用svg实现图片,微信中只能将svg图片转成base64来显示,所以是在vue中使用的svg来实现评分 1.效果图 微信中的可以点击及显示,但是,显示的话,在4.2分,4点多分的时候,显示的是半颗星 vue中用的是svg实现,所以用的是占比的形式,可以有一点点的星 2.微信实现店铺评分显示及商品评价星星展示 子组件index.wxml,可以动态的控制星星的大小 <!-- (size * s

  • 今天,小程序正式支持 SVG

    今天,小程序正式支持 SVG 写在前面 经过腾讯 Omi 团队的努力,今天你可以在小程序中使用 Cax 引擎高性能渲染 SVG! SVG 是可缩放矢量图形(Scalable Vector Graphics),基于可扩展标记语言,用于描述二维矢量图形的一种图形格式.它由万维网联盟制定,是一个开放标准.SVG 的优势有很多: SVG 使用 XML 格式定义图形,可通过文本编辑器来创建和修改 SVG 图像可被搜索.索引.脚本化或压缩 SVG 是可伸缩的,且放大图片质量不下降 SVG 图像可在任何的分辨

  • 详解如何愉快的在微信小程序中使用SVG图标

    SVG近几年因各种优势被大量的应用,遗憾的是到目前为止微信小程序并不支持以XML的形式使用SVG,这使得SVG的灵活性大大下降,大多数人选择放弃在微信小程序中使用SVG图标方案. 那么,真的就没有办法在微信小程序中愉快的使用SVG图标了吗?我们先来分析一下,对于使用SVG图标我们有哪些需求: 能够引入使用 能够调整颜色 首先第一点是没有问题的,微信小程序支持以Image.src的形式引入SVG.接下来就是本文的重点部分了,如何让使Image形式的SVG可以改变颜色. 在最近对CSS的学习中,我发

  • 小程序如何支持使用 async/await详解

    前言 小程序本身是不支持async/await语法的,但有些应用场景,我们使用async/await会使得代码更简洁,也更易于维护,用过都知道是有多爽的.既然小程序不支持,那我们可以借助 fackbook 开源的 regenerator 来完成这一功能. 前面我也百度过一些方法,但很多方法都行不通,只能从其中找到一些线索,再加上实践来验证,最后在这里记录下成功的解决方法. 准备工作 1. 小程序目录下,新建一个packpage.json,通过命令行执行 npm init -y 可以快速创建 2.

  • 微信小程序缓存支持二次开发封装实现解析

    这篇文章主要介绍了微信小程序缓存支持二次开发封装实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 简介 微信小程序提供了缓存的api,包括同步和异步两种,具体api不多说明,可自行查看官方文档 现在微信小程序缓存api存在一个问题就是没有设定过期时间,下面给大家介绍一下对小程序缓存的二次封装,使其支持设定过期时间 实现方法 先声明,这个并非原创方法,只是查找资料的时候看到的,觉得挺有用,就记录下来,也算转播给大家 原作链接:https:/

  • 微信小程序自定义支持图片的弹窗

    本文实例为大家分享了微信小程序自定义支持图片的弹窗,供大家参考,具体内容如下 为index.wxml添加如下图代码: (微信小程序 - canvas层级最高问题,如何超越canvas的层级,只能使用cover-view标签) <!--index.wxml--> <button class="show-btn" bindtap="showDialogBtn">弹窗</button> <!--弹窗--> <cover

  • 微信小程序里引入SVG矢量图标的方法

    引言 因为微信小程序的限制,引入外部图片或者矢量图,只能通过设置背景图片background-image : url("base64转码后的代码");的方式来进行操作.同时还是因为微信小程序的限制,我们要先把svg的xml编码转码为base64编码 首先,说明以下我们常见的svg矢量图是什么?下面引用百度百科的话: svg是基于可扩展标记语言(标准通用标记语言的子集),用于描述二维矢量图形的一种图形格式 可能还是比较迷糊,那我们来看看,用记事本打开一个svg,里面的编码是什么: <

  • 微信小程序中如何实现动态改变SVG颜色和尺寸

    目录 前言 1. 创建一个微信小程序项目,准备好SVG素材 2. 封装修改svg颜色的工厂函数 3. 封装一个自定义组件,方便使用svg图标 4. 注册组件,使用 结语 总结 前言 最近在做项目的时候总是因为组件库的图标无法满足需求而烦恼,而每次需要新的图标又要去找字体图标.生成新的css代码而苦恼. 所以想到用svg来代替,体积也小,主要就是方便一点,不用每次还重新生成代码.但是一个最重要的问题也随之出现,小程序中不支持svg代码....但是也有曲线救国的方式,image可以显示svg,还好没

  • 微信小程序web-view不支持打开非业务域名https //XXXX 请重新配置的解决办法

    目录 发现问题 解决方法 总结 发现问题 微信小程序提供web-view,可以在小程序中加载网页 我在微信公众平台中的开发管理—>开发设置中对于服务器域名中的request合法域名进行了正确设置,但是在微信开发者工具中仍然无法跳转网页,显示不支持打开非业务域名https //XXXX 请重新配置 解决方法 网上查找了很久资料,后来在官方文档中发现了问题所在,配置服务器域名是不够的,需要管理后台中配置业务域名 官方介绍文档在这里 但是还有一个最大的问题,个人类型的小程序暂不支持使用 orz 我的小

  • 微信小程序开发的四十个技术窍门总结(推荐)

    前言 微信"小程序"正式上线一周时间,相关话题持续升温.支付宝开发"小程序"的消息也随即曝出,互联网巨头们摩拳擦掌,不少网友调侃说,这势头感觉要挑战现有的"APP帝国"了.那么,面对如此炙手可热的小程序,WeX5移动开发云也没有闲着,经过全网搜索,为大家筛选出一条小程序开发者的必读文章,内容涵盖小程序开发实战需要注意的40个技术点,供大家参考! Q:为什么脚本内不能使用window等对象 A:页面的脚本逻辑是在JsCore中运行,JsCore是一

随机推荐