经过腾讯 Omi 团队的努力,今天你可以在小程序中使用 Cax 引擎高性能渲染 SVG!
SVG 是可缩放矢量图形(Scalable Vector Graphics),基于可扩展标记语言,用于描述二维矢量图形的一种图形格式。它由万维网联盟制定,是一个开放标准。SVG 的优势有很多:
而支持上面这些优秀特性的前提是 - 需要支持 SVG 标签。比如在小程序中直接写:
上面定义了 SVG 的结构、样式和点击行为。但是小程序目前不支持 SVG 标签,仅仅支持加载 SVG 之后 作为 background-image 进行展示,如 background-image: url("data:image/svg+xml.......)
,或者 base64 后作为 background-image 的 url。
那么怎么办呢?有没有办法让小程序支持 SVG? 答案是有的!需要下面这些东西(站在巨人的肩膀上):
这里稍微解释下 Hyperscript:
比如 JSX 中的
Hello {this.props.name}
或者 js 中的 htm:
html`
Hello {this.props.name}
`
最后都会被编译成:
h(
"div",
null,
"Hello ",
this.props.name
);
嵌套的 div 也会变编译成 h 嵌套 h,比如
abc
编译后:
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-a', this)
},
tapHandler: function () {
console.log('你点击了 rect')
}
})
其中的 svg-a 对应着 wxml 里 cax-element 的 id:
声明组件依赖
{
"usingComponents": {
"cax-element":"../../cax/index"
}
}
小程序中显示效果:
在来一个复杂的例子,用 SVG 绘制 Omi 的 logo:
renderSVG(html`
`, 'svg-a', this)
小程序种显示效果:
在 omip 和 mps 当中使用 cax 渲染 svg,你可以不用使用 htm。比如在 omip 中实现上面两个例子:
renderSVG(
, 'svg-a', this.$scope)
renderSVG(
, '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 :
会被 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"
})
);
所以总结一下:
这就完了?远没有,看 cax 在小程序中的这个例子:
详细代码:
renderSVG(html`
`, 'svg-c', this)
再试试著名的 SVG 老虎:
path 太长,就不贴代码了,可以点击这里查看
就这么多?未完待续...,后续补充:
import { html, renderSVG } from '../../cax/cax'
Page({
onLoad: function () {
const svg = renderSVG(html`
`, '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-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-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)
}
})
效果如下:
其他
内容
高亮