# Worklet animation
Weixin Mini Program uses a two-threaded architecture, separating the rendering thread (UI thread) and the logic thread (JS thread).TheJSthread does not affect the animation of theUIthread, such as scrolling.But the problem is thatUIWhen a thread's event occurs, it must be passed across threads to theJSthread to trigger a developer callback. This asynchronity can cause significant latency and instability when interactive animations, such as dragging elements, occur.
The workletanimation was born to solve this kind of problem, so that Weixin Mini Program can achieve a native animation-like experience.
# Experience Now
When using theworkletanimation capabilities, ensure the following:
- Make sure that the
option in the upper right corner of the Developer Tools > Details > Local settings compiles the JS to ES5option is ticked (the package size increases slightly) - Worklet animation-related interfaces are available only in
Skylinerendering mode

First, we need to understand some relevant concepts.
# Concept 1:workletfunction
One declaration is in developer code and can run in.JSa thread orUIthreads functions with a'worklet'instruction declaration at the top of the function body.
# Worklet function definition
function someWorklet(greeting) {
'worklet';
console.log(greeting);
}
// Running in JS thread
someWorklet('hello') // print: hello
// Running in the UI thread
wx.worklet.runOnUI(someWorklet)('hello') // print: [ui] hello
# Call between worklet functions
const name = 'skyline'
function anotherWorklet() {
'worklet';
return 'hello ' + name;
}
// Worklet functions can be called to each other
function someWorklet() {
'worklet';
const greeting = anotherWorklet();
console.log('another worklet says ', greeting);
}
wx.worklet.runOnUI(someWorklet)() // print: [ui] another worklet says hello skyline
# From the UI thread to the JS thread
function someFunc(greeting) {
console.log('hello', greeting);
}
function someWorklet() {
'worklet'
// 访问非 worklet 函数时,需使用 runOnJS
// someFunc 运行在 JS 线程
runOnJS(someFunc)('skyline')
}
wx.worklet.runOnUI(someWorklet)() // print: hello skyline
# Concept 2: Shared Variables
A variable created in theJSthread that can be synchronized between two threads.
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
}
// Modify in JS thread
offset.value = 1
runOnUI(someWorklet)()
A variable created by thesharedfunction is called asharedValueshared variable.The usage is analogous tovue3refin,It is read and written through the.valueattribute, but it should be noted that they are not a concept.sharedValueis used mainly as follows.
# Shared data across threads
External variables captured by theworkletfunction,It is actually serialized and generated as a copy of theUIthread, in the following code,someWorkletcapturesobjThevariable, even though we modified thenameattribute ofobj, insomeWorkletThe location declared by,objhas been serialized to theUIthread, so subsequent modifications cannot be synchronized.
const obj = { name: 'skyline'}
function someWorklet() {
'worklet'
console.log(obj.name) // 输出的仍旧是 skyline
}
obj.name = 'change name'
wx.worklet.runOnUI(someWorklet)()
SharedValueis used to synchronize the change of state between threads.
const { shared, runOnUI } = wx.worklet
const offset = shared(0)
function someWorklet() {
'worklet'
console.log(offset.value) // 输出的是新值 1
}
offset.value = 1
runOnUI(someWorklet)()
# Drive animation
Workletfunctions and shared variables are used to solve interactive animation problems.Related interfacesapplyAnimatedStyleare accessible via page / component instances, Refer to the interface documentation .
<view id="moved-box"></view>
<view id="btn" bind:tap="tap">点击驱动小球移动</view>
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()
}
})
When the button# btnis clicked, we assign a random number tooffsetand the ball moves.
The second parameter of the applyAnimatedStyleinterface isupdaterfor aworkletfunction that captures the shared variable`` offset,When the value ofoffsetchanges,updaterreruns and applies the returned newstyleObject`to the selected node.
Of course, just looking at this example, there is no difference between usingsetData.But whenworkletanimation and gesture are combined, a qualitative change occurs.
# Example usage
# Gesture Processing
<pan-gesture-handler onGestureEvent="handlepan">
<view class="circle"></view>
</pan-gesture-handler>
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;
}
}
});
A smooth drag effect occurs when the finger moves over thecirclenode.handlepanCallback triggers theUIthread, and we modify theThe value of theoffsetgenerates animation in theUIthread without having to circle back to theJSthread.
See more example code for gesture processing
# Custom animated curves
<view id="moved-box"></view>
<view id="btn" bind:tap="tap">点击驱动小球移动</view>
const { shared, Easing, timing } = wx.worklet
Page({
onLoad() {
const offset = shared(0)
this.applyAnimatedStyle('#moved-box', () => {
'worklet';
return {
transform: `translateX(${offset.value}px)`
}
})
this._offset = offset
},
tap() {
/**
* 目标值 300
* 动画时长 200ms
* 动画曲线 Easing.ease
*/
this._offset.value = timing(300, {
duration: 200,
easing: Easing.ease
})
}
})
Built-in such astiming,springEncapsulation methods such as common animation methods allow developers to customize the animation curve, and at the same time, different animation types can be combined and repeated to form interwoven animations.
See more usage of different animation types
See more sample code for a cache function
# Related Interfaces
Base type shared , derived , [[TAG2-START]] cancelAnimation]((worklet.cancelAnimation))
Animation type timing , spring , [[TAG2-START]] decay]((worklet.decay))
Composite animation sequence , repeat , [[TAG2-START]] delay]((worklet.delay))
Easing function Easing
Page instance method: applyAnimatedStyle , clearAnimatedStyle
# Note
WorkletThere are some invocation restrictions inside the function to be aware of
worklettype callback defined in page / component instance, internal accessThe interface onwxcan be called back to theJSthread byrunOnJSas follows.The external variables referenced by the workletfunction are object types that are frozen byObject.freezeand need to be used directly to access specific properties on the object.
<tap-gesture-handler onGestureEvent="handleTap">
<view class="circle" >showModal</view>
</tap-gesture-handler>
const { runOnJS, timing } = wx.worklet
Page({
data: {
msg: 'Skyline'
},
onLoad() {
const toValue = 100
const showModal = this.showModal.bind(this)
timing(toValue, { duration: 300 }, () => {
'worklet'
runOnJS(showModal)(msg)
})
},
handleTap() {
'worklet'
// 非常重要!!!
// const { msg } = this.data
// 通过解构 this.data 访问 msg,此时 this.data 将被 Object.freeze 冻结,会导致 setData 无法生效
// 而通过 this.data.msg 则不会冻结 this.data
const msg = `hello ${this.data.msg}`
// 非常重要!!!
// Page method 必须通过 this.methodName.bind(this) 访问
const showModal = this.showModal.bind(this)
// 场景一:返回 JS 线程
runOnJS(showModal)(msg)
// 场景二:动画完成回调里返回 JS 线程
const toValue = 100
timing(toValue, { duration: 300 }, () => {
'worklet'
runOnJS(showModal)(msg)
})
// 场景三:调用其它 worklet 函数
this.doSomething()
},
doSomething() {
'worklet'
},
showModal(msg) {
wx.showModal({
title: msg // title: hello skyline
})
},
})