Skyline简单介绍
Skyline是微信小程序的新的渲染引擎,其相比于Webview引擎拥有更好的性能,同时也支持更多原生的新特性,比如Worklet动画、路由跳转的转场动画、共享元素动画、全局工具栏等,这些新特性都可以让小程序拥有更加丝滑的操作,以及转场。小编在此非常推荐大家去使用Skyline进行自己的小程序开发。
Skyline目前支持按页面去升级,也就是部分页面可以继续使用Webview,部分可以使用Skyline,具体的配置方法可以参考官方文档:Skyline 渲染引擎 / 从 WebView 迁移 / 起步。
本文主要介绍如何使用Skyline的Worklet动画,让你的小程序交互性能更加流畅。
Skyline的线程模型
在讲Worklet动画之前,我们先了解下Skylilne的线程模型。不同于Webview的单线程模型,Skyline改成了双线程模型,将js逻辑、DOM树构建,CSS样式计算单独放到了AppService线程,简称JS线程,而应用样式、布局(Layout)和绘制(Paint)则放在一个渲染线程,简称UI线程,以下是其架构图:

分开成两个线程后,当有需要在JS线程计算动画的过程,而在UI线程进行展示,两者直接很难去协调同步,容易出现动画不流畅的问题。因此微信小程序就提供了Worklet动画来解决这个问题。
什么是Worklet动画
Worklet动画有两个核心概念,分别是Worklet函数,和Worklet共享变量。
Worklet函数
Worklet函数就是一种既可以在JS线程中运行,也可以在UI线程中运行的函数。
Worklet函数的定义就是在函数的顶部加上'worklet'字符串标志:
scss
function someWorklet(greeting) {
'worklet';
console.log(greeting);
}
// 运行在 JS 线程
someWorklet('hello') // print: hello
// 运行在 UI 线程
wx.worklet.runOnUI(someWorklet)('hello') // print: [ui] hello
Worklet函数间可以直接相互调用:
javascript
const name = 'worklet1'
function anotherWorklet() {
'worklet';
return 'hello ' + name;
}
// worklet 函数间可互相调用
function someWorklet() {
'worklet';
const greeting = anotherWorklet();
console.log('another worklet says ', greeting);
}
wx.worklet.runOnUI(someWorklet)() // print: [ui] another worklet says hello worklet1
Worklet函数通过wx.worklet.runOnJS来调用普通函数:
scss
function someFunc(greeting) {
console.log('hello', greting);
}
function someWorklet() {
'worklet'
// 访问非 worklet 函数时,需使用 runOnJS
// someFunc 运行在 JS 线程
wx.worklet.runOnJS(someFunc)('js function')
}
wx.worklet.runOnUI(someWorklet)() // print: hello js function
需要注意的是,Worklet函数不能直接调用普通的函数,需要通过wx.worklet.runOnJS来运行普通的函数,让其在JS线程中运行。
Worklet共享变量
Worklet共享变量就是在JS线程中创建,并且可以在JS线程和UI线程中共享使用的变量。
scss
const { shared, runOnUI } = wx.worklet
const offset = shared(0)
function someWorklet() {
'worklet'
console.log(offset.value) // print: 1
// 在 UI 线程修改
offset.value = 2
console.log(offset.value) // print: 2
}
// 在 JS 线程修改
offset.value = 1
runOnUI(someWorklet)()
Worklet的实际使用场景
我们结合下前两节的Worklet函数和Worklet共享变量,看下在实际的使用场景。我们开头提到过,Worklet动画解决的就是动画函数在JS线程和UI线程之间协调时导致的卡顿问题。那么我们来看看这个问题是如何使用Worklet动画去解决的。
驱动动画
bash
<view id="moved-box"></view>
<view id="btn" bind:tap="tap">点击驱动小球移动</view>
javascript
Page({
onLoad() {
const offset = wx.worklet.shared(0)
this.applyAnimatedStyle('#moved-box', () => {
'worklet';
return {
transform: `translateX(${offset.value}px)`
}
})
this._offset = offset
},
tap() {
// 点击时修改 sharedValue 值,驱动小球移动
this._offset.value = Math.random()
}
})
当点击按钮 #btn
时,我们用随机数给 offset
进行赋值,小球会随之移动。
applyAnimatedStyle
接口的第二个参数 updater
为一个 worklet
函数,其捕获了共享变量 offset
,当 offset
的值变化时,updater
会重新执行,并将返回的新 styleObject
应用到选中节点上。
当然,光看这个例子,跟用 setData
看好像没有什么区别。但当 worklet
动画和手势结合时,就产生了质变。
结合手势系统
ini
<pan-gesture-handler onGestureEvent="handlepan">
<view class="circle"></view>
</pan-gesture-handler>
ini
Page({
onLoad() {
const offset = wx.worklet.shared(0);
this.applyAnimatedStyle('.circle', () => {
'worklet';
return {
transform: `translateX(${offset.value}px)`
};
});
this._offset = offset;
},
handlepan(evt) {
'worklet';
if (evt.state === GestureState.ACTIVE) {
this._offset.value += evt.deltaX;
}
}
});
当手指在 circle
节点上移动时,会产生平滑的拖动效果。handlepan
回调触发在 UI
线程时修改了 offset
的值,而offset
值改变的时候会触发this.apppAnimatedStyle的updated函数,并修改.circle的style的transform的值,触发了circle
节点的移动,整个过程都是在UI线程中进行,没有JS线程和UI线程的切换,从而让动画更加的平滑顺畅。
手势系统中的回调handlepan必须是Worklet函数
关于手势系统,我们会在接下来的文章中详细介绍。