
Skyline Worklet
- 714 installs
- 48 repo stars
- Updated June 3, 2026
- wechat-miniprogram/skyline-skills
skyline-worklet is a Claude Code skill that guides implementation of high-performance gesture-driven animations in WeChat Mini Programs using the Skyline rendering engine Worklet system for developers who need UI-thread
About
skyline-worklet is a Claude Code skill for the WeChat Mini Program Skyline rendering engine Worklet animation system. It covers worklet functions, SharedValue shared variables, timing, spring, and decay animation types, Easing curves, composed animations, and UI/JS thread communication via runOnUI and runOnJS. The skill addresses dual-thread latency by running animation logic on the UI thread for drag, gesture-follow, and spring-bounce interactions with applyAnimatedStyle. Developers reach for skyline-worklet when building WeChat mini programs that need native-feel interactive animations instead of cross-thread JS-driven updates.
- Teaches worklet functions that run directly on the UI thread for buttery-smooth interactions
- Covers SharedValue, derived(), timing, spring, decay, and Easing functions
- Explains runOnUI and runOnJS for seamless thread communication
- Demonstrates applyAnimatedStyle to bind SharedValues to node styles
- Provides ready-to-use patterns for drag, gesture following, spring rebound, and sequenced animations
Skyline Worklet by the numbers
- 714 all-time installs (skills.sh)
- +29 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #483 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/wechat-miniprogram/skyline-skills --skill skyline-workletAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 714 |
|---|---|
| repo stars | ★ 48 |
| Last updated | June 3, 2026 |
| Repository | wechat-miniprogram/skyline-skills ↗ |
How do you build gesture animations in WeChat Skyline?
Get expert guidance when implementing high-performance, gesture-driven animations in WeChat Mini Programs using the Skyline rendering engine.
Who is it for?
WeChat Mini Program developers using Skyline who need UI-thread worklet animations for drag, gesture follow, and spring interactions.
Skip if: Web React animation projects, legacy mini program WebView rendering, or backend API integration without Skyline Worklet requirements.
When should I use this skill?
User mentions Skyline Worklet, SharedValue, runOnUI, runOnJS, gesture animations, or UI-thread animation in WeChat Mini Programs.
What you get
UI-thread Worklet animation code with SharedValue bindings, composed timing or spring sequences, and runOnUI or runOnJS thread bridges.
- Worklet animation implementations
- SharedValue style bindings
- gesture handler code
Files
Worklet 动画系统
适用场景
- 实现手势跟随、拖拽等交互动画
- 使用 timing/spring/decay 创建动画效果
- 通过 SharedValue 驱动节点样式变化
- 组合多段动画(序列、重复、延迟)
- 在 UI 线程和 JS 线程间传递数据
核心概念
双线程架构与 Worklet 的意义
小程序双线程架构中,UI 事件需跨线程传递到 JS 线程再回传,交互动画会有明显延迟。Worklet 动画让动画逻辑直接运行在 UI 线程,实现类原生动画体验。
三大核心概念
| 概念 | 说明 | 关键 API |
|---|---|---|
| worklet 函数 | 可运行在 JS 或 UI 线程的函数,顶部声明 'worklet' 指令 | runOnUI(), runOnJS() |
| 共享变量 | 跨线程同步的变量,通过 .value 读写 | shared(), derived() |
| 动画驱动 | 将 SharedValue 绑定到节点样式 | applyAnimatedStyle() |
基本流程
const { shared, timing } = wx.worklet
// 1. 创建共享变量
const offset = shared(0)
// 2. 绑定到节点样式(updater 为 worklet 函数)
this.applyAnimatedStyle('#box', () => {
'worklet'
return { transform: `translateX(${offset.value}px)` }
})
// 3. 修改值驱动动画
offset.value = timing(300, { duration: 200 })文档索引
根据需求快速定位(路径相对于 references/):
| 我想要... | 查阅文档 |
|---|---|
| 了解 worklet 架构和完整概念 | core/worklet-overview.md |
| 使用 SharedValue 和 DerivedValue | base/shared-derived.md |
| 在 worklet 中操作 scroll-view | base/scroll-view-context.md |
| 使用 timing/spring/decay 动画 | animation/timing-spring-decay.md |
| 查看 Easing 缓动函数 | animation/easing.md |
| 使用序列/重复/延迟组合动画 | animation/combine-animation.md |
| 了解 runOnUI/runOnJS 线程通信 | tool/thread-communication.md |
强制规则
MUST: worklet 函数必须声明 'worklet' 指令
// ✅ Correct
function handleGesture(evt) {
'worklet'
offset.value += evt.deltaX
}
// ❌ Incorrect - 缺少 'worklet' 指令,无法在 UI 线程执行
function handleGesture(evt) {
offset.value += evt.deltaX
}MUST: SharedValue 必须通过 .value 读写
// ✅ Correct
const offset = shared(0)
offset.value = 100
// ❌ Incorrect - 直接赋值会替换整个 SharedValue 对象
const offset = shared(0)
offset = 100MUST: 访问非 worklet 函数必须使用 runOnJS
// ✅ Correct
function showModal(msg) {
wx.showModal({ title: msg })
}
function handleTap() {
'worklet'
const fn = this.showModal.bind(this)
runOnJS(fn)('hello')
}
// ❌ Incorrect - worklet 中直接调用普通函数
function handleTap() {
'worklet'
this.showModal('hello')
}MUST: 页面方法必须通过 this.methodName.bind(this) 访问
// ✅ Correct
handleTap() {
'worklet'
const showModal = this.showModal.bind(this)
runOnJS(showModal)(msg)
}
// ❌ Incorrect - 未 bind(this),this 指向丢失
handleTap() {
'worklet'
runOnJS(this.showModal)(msg)
}MUST: Worklet 动画仅在 Skyline 渲染模式下可用
- 确保 app.json 配置
"renderer": "skyline" - 确保开发者工具勾选「将 JS 编译成 ES5」
NEVER: 在 worklet 函数中直接调用 wx API
必须通过 runOnJS 回到 JS 线程。
NEVER: 通过解构 this.data 访问属性
会导致 Object.freeze 冻结 this.data,setData 将失效。
// ✅ Correct
handleTap() {
'worklet'
const msg = this.data.msg
}
// ❌ Incorrect - 解构会冻结整个 this.data
handleTap() {
'worklet'
const { msg } = this.data
}Quick Reference
API 速查表
| 分类 | API | 说明 |
|---|---|---|
| 基础 | shared(initialValue) | 创建 SharedValue |
| 基础 | derived(updaterWorklet) | 创建衍生值(类比 computed) |
| 基础 | cancelAnimation(sharedValue) | 取消动画 |
| 动画 | timing(toValue, options?, callback?) | 时间曲线动画(默认 300ms) |
| 动画 | spring(toValue, options?, callback?) | 弹簧物理动画 |
| 动画 | decay(options?, callback?) | 滚动衰减动画 |
| 组合 | sequence(anim1, anim2, ...) | 依次执行 |
| 组合 | repeat(anim, reps, reverse?, callback?) | 重复(负值=无限) |
| 组合 | delay(ms, anim) | 延迟执行 |
| 工具 | runOnUI(workletFn) | 在 UI 线程执行 |
| 工具 | runOnJS(normalFn) | 回调 JS 线程 |
场景 → 方案映射
| 场景 | 推荐方案 |
|---|---|
| 点击后平滑移动 | timing + Easing |
| 手势松开回弹 | spring |
| 手势松开惯性滑动 | decay + velocity |
| 先移动再弹回 | sequence(timing, spring) |
| 循环脉动效果 | repeat(timing, -1, true) |
| 延迟后开始动画 | delay(ms, timing/spring) |
相关技能
| 场景 | 推荐技能 | 说明 |
|---|---|---|
| 手势组件 | skyline-components | pan/tap/long-press 手势处理 |
| 渲染引擎概览 | skyline-overview | Skyline 配置和迁移 |
| 样式开发 | skyline-wxss | WXSS 支持与差异 |
| 路由转场 | skyline-route | 自定义路由动画 |
References 目录结构
references/
├── animation/
│ ├── combine-animation.md
│ ├── easing.md
│ └── timing-spring-decay.md
├── base/
│ ├── scroll-view-context.md
│ └── shared-derived.md
├── core/
│ └── worklet-overview.md
└── tool/
└── thread-communication.md组合动画
worklet 动画系统提供三种组合动画方式,可对基础动画(timing/spring/decay)进行编排。
sequence — 序列动画
依次执行传入的动画。
签名
AnimationObject worklet.sequence(AnimationObject ...animationN)参数
| 参数 | 类型 | 说明 |
|---|---|---|
| animationN | AnimationObject | 一个或多个动画对象(可变参数) |
返回值
AnimationObject,可直接赋值给 SharedValue。
示例
const { shared, sequence, timing, spring } = wx.worklet
const offset = shared(0)
// 先 timing 到 100,再 spring 回到 0
offset.value = sequence(timing(100), spring(0))
// 多段序列
offset.value = sequence(
timing(100, { duration: 200 }),
timing(200, { duration: 300 }),
spring(0)
)---
repeat — 重复动画
重复执行动画。
签名
AnimationObject worklet.repeat(
AnimationObject animation,
number numberOfReps,
boolean reverse,
function callback
)参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| animation | AnimationObject | 是 | 要重复的动画 |
| numberOfReps | number | 是 | 重复次数。负值时一直循环,直到被取消 |
| reverse | boolean | 否 | 反向运行,每周期结束动画由尾到头运行。仅对 timing 和 spring 生效 |
| callback | function | 否 | 完成回调(worklet 函数) |
返回值
AnimationObject,可直接赋值给 SharedValue。
示例
const { shared, repeat, timing } = wx.worklet
const offset = shared(0)
// 重复 2 次,带反向
offset.value = repeat(timing(70), 2, true)
// 无限循环
offset.value = repeat(timing(100, { duration: 500 }), -1, true)
// 带完成回调
offset.value = repeat(timing(70), 3, true, (finished) => {
'worklet'
if (finished) {
console.log('所有重复完成')
}
})停止无限循环
const { cancelAnimation } = wx.worklet
// 取消动画即可停止无限循环
cancelAnimation(offset)---
delay — 延迟动画
延迟执行动画。
签名
AnimationObject worklet.delay(number delayMS, AnimationObject delayedAnimation)参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| delayMS | number | 是 | 等待时间(毫秒) |
| delayedAnimation | AnimationObject | 是 | 要延迟执行的动画 |
返回值
AnimationObject,可直接赋值给 SharedValue。
示例
const { shared, delay, timing } = wx.worklet
const offset = shared(0)
// 延迟 1 秒后执行动画
offset.value = delay(1000, timing(70))---
组合使用示例
先延迟,再序列执行,然后重复
const { shared, delay, sequence, repeat, timing, spring } = wx.worklet
const offset = shared(0)
// 延迟 500ms → timing 到 100 → spring 回 0
offset.value = delay(500, sequence(
timing(100, { duration: 200 }),
spring(0)
))
// 无限循环的脉动效果
offset.value = repeat(
sequence(
timing(1, { duration: 500 }),
timing(0, { duration: 500 })
),
-1,
false
)交错动画(多个元素)
Page({
onLoad() {
const items = [this._offset1, this._offset2, this._offset3]
items.forEach((offset, i) => {
// 每个元素间隔 100ms
offset.value = delay(i * 100, timing(100, { duration: 300 }))
})
}
})Easing 缓动函数
Easing 模块实现了常见的动画缓动函数,可从 wx.worklet 对象中读取。动画效果参考:https://easings.net/
const { Easing, timing } = wx.worklet
// 使用 Easing 配合 timing
offset.value = timing(100, {
duration: 300,
easing: Easing.ease
})预置动画函数
Easing.bounce
简单的反弹效果。
Easing.bounce(t)Easing.ease
简单的惯性动画。
Easing.ease(t)Easing.elastic
简单的弹性动画,类似弹簧来回摆动,高阶函数。默认弹性为 1,会稍微超出一次。弹性为 0 时不会过冲。
Easing.elastic(bounciness = 1)标准缓动函数
Easing.linear
线性函数,f(t) = t
Easing.linear(t)Easing.quad
二次方函数,f(t) = t * t
Easing.quad(t)Easing.cubic
立方函数,f(t) = t * t * t
Easing.cubic(t)Easing.poly
高阶函数,返回幂函数。
Easing.poly(n)poly(4)→ 四次方(easeInQuart)poly(5)→ 五次方(easeInQuint)
其它数学函数
Easing.bezier
三次贝塞尔曲线,效果同 CSS transition-timing-function。调试参数可借助可视化工具:https://cubic-bezier.com/
Easing.bezier(x1, y1, x2, y2)
// 等价于 CSS cubic-bezier(0.25, 0.1, 0.25, 1)
Easing.bezier(0.25, 0.1, 0.25, 1)Easing.circle
圆形曲线。
Easing.circle(t)Easing.sin
正弦函数。
Easing.sin(t)Easing.exp
指数函数。
Easing.exp(t)缓动方式
以上效果均有三种缓动方式,都是高阶函数:
Easing.in(easing)
正向运行缓动函数。
Easing.in(Easing.sin) // 等价于直接使用 Easing.sinEasing.out(easing)
反向运行缓动函数。
Easing.out(Easing.sin)Easing.inOut(easing)
前半程正向,后半程反向。
Easing.inOut(Easing.sin)缓动方式示例(以 sin 为例)
| 方式 | 代码 | 效果 |
|---|---|---|
| 正向(默认) | Easing.in(Easing.sin) | easeInSine |
| 反向 | Easing.out(Easing.sin) | easeOutSine |
| 正反 | Easing.inOut(Easing.sin) | easeInOutSine |
完整函数速查表
| 函数 | 类型 | 参数 | 说明 |
|---|---|---|---|
linear(t) | 直接调用 | t: number | 线性 |
quad(t) | 直接调用 | t: number | 二次方 |
cubic(t) | 直接调用 | t: number | 三次方 |
poly(n) | 高阶函数 | n: number | n 次方 |
sin(t) | 直接调用 | t: number | 正弦 |
circle(t) | 直接调用 | t: number | 圆形 |
exp(t) | 直接调用 | t: number | 指数 |
bounce(t) | 直接调用 | t: number | 反弹 |
ease(t) | 直接调用 | t: number | 惯性 |
elastic(b) | 高阶函数 | b: number (默认1) | 弹性 |
bezier(x1,y1,x2,y2) | 高阶函数 | 四个控制点 | 贝塞尔曲线 |
in(easing) | 高阶函数 | easing: function | 正向缓动 |
out(easing) | 高阶函数 | easing: function | 反向缓动 |
inOut(easing) | 高阶函数 | easing: function | 正反缓动 |
常用组合
const { Easing } = wx.worklet
// timing 默认曲线
Easing.inOut(Easing.quad)
// 平滑加减速
Easing.inOut(Easing.cubic)
// 自定义贝塞尔
Easing.bezier(0.25, 0.1, 0.25, 1)
// 弹性效果
Easing.out(Easing.elastic(1))
// 反弹效果
Easing.out(Easing.bounce)基础动画类型
worklet 动画系统提供三种基础动画类型,都返回 AnimationObject,可直接赋值给 SharedValue。
timing — 基于时间的动画
签名
AnimationObject worklet.timing(number toValue, Object options, function callback)参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| toValue | number | 是 | 目标值 |
| options | Object | 否 | 动画配置 |
| callback | function | 否 | 完成回调(取消返回 false,完成返回 true) |
options 配置
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| duration | number | 300 | 动画时长(毫秒) |
| easing | function | Easing.inOut(Easing.quad) | 动画曲线,参考 Easing 模块 |
示例
<view id="moved-box"></view>
<view id="btn" bind:tap="tap">点击驱动小球移动</view>const { shared, timing, Easing } = wx.worklet
Page({
onLoad() {
const offset = shared(0)
this.applyAnimatedStyle('#moved-box', () => {
'worklet'
return {
transform: `translateX(${offset.value}px)`
}
})
this._offset = offset
},
tap() {
this._offset.value = timing(300, {
duration: 500,
easing: Easing.bezier(0.25, 0.1, 0.25, 1)
})
}
})---
spring — 基于物理的弹簧动画
签名
AnimationObject worklet.spring(number|string toValue, Object options, function callback)参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| toValue | number \ | string | 是 |
| options | Object | 否 | 动画配置 |
| callback | function | 否 | 完成回调 |
options 配置
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| damping | number | 10 | 阻尼系数 |
| mass | number | 1 | 重量系数,值越大移动越慢 |
| stiffness | number | 100 | 弹性系数 |
| overshootClamping | boolean | false | 动画是否可以在指定值上反弹 |
| restDisplacementThreshold | number | 0.01 | 弹簧静止时的位移 |
| restSpeedThreshold | number | 2 | 弹簧静止的速度 |
| velocity | number | 0 | 初速度 |
示例:手势松开回弹
<pan-gesture-handler onGestureEvent="handlepan">
<view class="circle"></view>
</pan-gesture-handler>const { shared, spring } = wx.worklet
Page({
onLoad() {
const offset = 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
} else if (evt.state === GestureState.END) {
this._offset.value = spring(0)
}
}
})调参建议
| 效果 | damping | stiffness | mass |
|---|---|---|---|
| 轻快弹跳 | 5 | 200 | 0.5 |
| 默认弹簧 | 10 | 100 | 1 |
| 厚重缓慢 | 20 | 50 | 2 |
| 紧绷无弹跳 | 20 | 200 | 1 |
---
decay — 基于滚动衰减的动画
签名
AnimationObject worklet.decay(Object options, function callback)注意:decay 不需要 toValue 参数,目标值由初速度和衰减速率决定。参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| options | Object | 否 | 动画配置 |
| callback | function | 否 | 完成回调 |
options 配置
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| velocity | number | 0 | 初速度 |
| deceleration | number | 0.998 | 衰减速率(0-1 之间,越大衰减越慢) |
| clamp | Array | [] | 边界值,长度为 2 的数组 [min, max] |
示例:手势松开惯性滑动
<pan-gesture-handler onGestureEvent="handlepan">
<view class="circle"></view>
</pan-gesture-handler>const { shared, decay } = wx.worklet
Page({
onLoad() {
this._offset = shared(0)
this.applyAnimatedStyle('.circle', () => {
'worklet'
return {
transform: `translateX(${this._offset.value}px)`
}
})
},
handlepan(evt) {
'worklet'
if (evt.state === GestureState.ACTIVE) {
this._offset.value += evt.deltaX
} else if (evt.state === GestureState.END) {
this._offset.value = decay(
{
velocity: evt.velocityX,
clamp: [-200, 200]
},
() => {
'worklet'
console.info('decay finish')
}
)
}
}
})---
动画回调
三种动画类型都支持 callback 回调参数(worklet 函数):
offset.value = timing(100, { duration: 300 }, (finished) => {
'worklet'
if (finished) {
console.log('动画正常完成')
} else {
console.log('动画被取消')
}
})finished === true:动画正常完成finished === false:动画被cancelAnimation或新动画中断
三种动画对比
| 特性 | timing | spring | decay |
|---|---|---|---|
| 需要 toValue | ✅ | ✅ | ❌ |
| 持续时间 | 固定(duration) | 由物理参数决定 | 由初速度和衰减决定 |
| 可自定义曲线 | ✅(Easing) | ❌(物理模拟) | ❌(指数衰减) |
| 典型场景 | 平滑过渡 | 弹性回弹 | 惯性滑动 |
| 可设边界 | ❌ | ❌ | ✅(clamp) |
ScrollViewContext
基础库 3.3.0+
概述
worklet.scrollViewContext 提供在 worklet 函数内操作 scroll-view 组件的能力。通过 NodesRef.ref 获取 scroll-view 的引用,即可在 UI 线程中直接控制滚动。
scrollViewContext.scrollTo(object)
滚动至指定位置。
签名:worklet.scrollViewContext.scrollTo(ref, Object object)
参数
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| top | number | - | 否 | 顶部距离 |
| left | number | - | 否 | 左边界距离 |
| duration | number | - | 否 | 滚动动画时长(毫秒) |
| animated | boolean | - | 否 | 是否启用滚动动画 |
| easingFunction | string | - | 否 | 动画曲线 |
用法示例
const { shared, scrollViewContext } = wx.worklet
Page({
onLoad() {
this.scrollRef = shared()
this.createSelectorQuery()
.select('.scrollable')
.ref((res) => {
this.scrollRef.value = res.ref
})
.exec()
},
onTap() {
'worklet'
scrollViewContext.scrollTo(this.scrollRef.value, {
top: 200,
duration: 2000,
animated: true,
easingFunction: 'ease'
})
}
})要点
- 需要先通过
createSelectorQuery().select().ref()获取 scroll-view 的引用 - 引用存储在 SharedValue 中,以便在 worklet 函数内访问
- 可配合手势事件在 UI 线程中实现自定义滚动控制
SharedValue 与 DerivedValue
SharedValue
worklet.shared(initialValue)
创建共享变量 SharedValue,用于跨线程共享数据和驱动动画。
签名:SharedValue worklet.shared(any initialValue)
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| initialValue | `number \ | string \ |
返回值:SharedValue 类型值,可被 worklet 函数捕获。
基本用法
const { shared } = wx.worklet
// 创建共享变量
const offset = shared(0)
// 读取值
console.log(offset.value) // 0
// 修改值
offset.value = 100在 worklet 函数中使用
const offset = wx.worklet.shared(0)
const someWorkletFn = () => {
'worklet'
console.log('offset: ', offset.value)
}跨线程同步
SharedValue 最重要的能力是跨线程同步数据。普通变量被 worklet 函数捕获后会被序列化拷贝,后续修改无法同步。SharedValue 则可以:
const { shared, runOnUI } = wx.worklet
const offset = shared(0)
function someWorklet() {
'worklet'
console.log(offset.value) // 输出新值 1
}
offset.value = 1
runOnUI(someWorklet)()驱动动画
SharedValue 可驱动节点样式变化,配合 applyAnimatedStyle 使用:
Page({
onLoad() {
const offset = wx.worklet.shared(0)
this.applyAnimatedStyle('#box', () => {
'worklet'
return {
transform: `translateX(${offset.value}px)`
}
})
this._offset = offset
},
tap() {
this._offset.value = 200 // 直接赋值
// 或使用动画
this._offset.value = wx.worklet.timing(200)
}
})DerivedValue
worklet.derived(updaterWorklet)
基于已有 SharedValue 生成衍生共享变量,类比 computed 计算属性。
签名:DerivedValue worklet.derived(WorkletFunction updaterWorklet)
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| updaterWorklet | WorkletFunction | worklet 函数,被立即执行,返回值作为初始值。当捕获的 SharedValue 变化时自动重新执行 |
返回值:DerivedValue 类型值(也是 SharedValue 类型),可被 worklet 函数捕获。
基本用法
const { shared, derived } = wx.worklet
const progress = shared(0)
const offset = derived(() => {
'worklet'
return progress.value * 255
})
// progress.value 变化时,offset 自动更新
progress.value = 0.5
// offset.value 自动变为 127.5多 SharedValue 依赖
DerivedValue 可依赖多个 SharedValue,任一变化都会触发重新计算:
const { shared, derived } = wx.worklet
const x = shared(0)
const y = shared(0)
const distance = derived(() => {
'worklet'
return Math.sqrt(x.value * x.value + y.value * y.value)
})cancelAnimation
worklet.cancelAnimation(sharedValue)
取消由 SharedValue 驱动的动画。
签名:worklet.cancelAnimation(SharedValue sharedValue)
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| sharedValue | SharedValue | 需要取消动画的共享变量 |
用法示例
const { shared, timing, cancelAnimation } = wx.worklet
const offset = shared(0)
// 启动动画
offset.value = timing(100)
// 取消动画(offset 停留在当前值)
cancelAnimation(offset)常见场景:手势中断动画
handlepan(evt) {
'worklet'
if (evt.state === GestureState.BEGAN) {
// 手指按下时取消正在进行的动画
cancelAnimation(this._offset)
} else if (evt.state === GestureState.ACTIVE) {
this._offset.value += evt.deltaX
} else if (evt.state === GestureState.END) {
// 手指松开时启动弹簧动画
this._offset.value = spring(0)
}
}Worklet 动画概览
双线程架构与动画问题
小程序采用双线程架构,渲染线程(UI 线程)和逻辑线程(JS 线程)分离。JS 线程不会影响 UI 线程的动画表现(如滚动效果),但引入了新问题:UI 线程的事件发生后,需跨线程传递到 JS 线程,进而触发开发者回调。当做交互动画(如拖动元素)时,这种异步性会带来较大的延迟和不稳定。
Worklet 动画正是为解决这类问题而诞生的,使小程序可以做到类原生动画般的体验。
前置条件
- 确保开发者工具右上角 > 详情 > 本地设置里的「将 JS 编译成 ES5」选项被勾选(代码包体积会少量增加)
- Worklet 动画相关接口仅在 Skyline 渲染模式下才能使用
- 基础库版本要求:2.29.2+
概念一:Worklet 函数
一种声明在开发者代码中,可运行在 JS 线程或 UI 线程的函数,函数体顶部有 'worklet' 指令声明。
定义 worklet 函数
function someWorklet(greeting) {
'worklet'
console.log(greeting)
}
// 运行在 JS 线程
someWorklet('hello') // print: hello
// 运行在 UI 线程
wx.worklet.runOnUI(someWorklet)('hello') // print: [ui] helloworklet 函数间相互调用
const name = 'skyline'
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 skyline从 UI 线程调回 JS 线程
当需要在 worklet 函数中调用非 worklet 的普通函数时,必须使用 runOnJS:
function someFunc(greeting) {
console.log('hello', greeting)
}
function someWorklet() {
'worklet'
// 访问非 worklet 函数时,需使用 runOnJS
runOnJS(someFunc)('skyline')
}
wx.worklet.runOnUI(someWorklet)() // print: hello skyline概念二:共享变量(SharedValue)
在 JS 线程创建,可在两个线程间同步的变量。
const { shared, runOnUI } = wx.worklet
const offset = shared(0)
function someWorklet() {
'worklet'
console.log(offset.value) // print: 1
offset.value = 2 // 在 UI 线程修改
console.log(offset.value) // print: 2
}
offset.value = 1 // 在 JS 线程修改
runOnUI(someWorklet)()由 shared 函数创建的变量称为 SharedValue 共享变量。用法上类比 Vue3 中的 ref,读写都需要通过 .value 属性。
跨线程共享数据
worklet 函数捕获的外部变量会被序列化后拷贝到 UI 线程,后续修改无法同步:
const obj = { name: 'skyline' }
function someWorklet() {
'worklet'
console.log(obj.name) // 输出仍是 skyline(序列化时的值)
}
obj.name = 'change name'
wx.worklet.runOnUI(someWorklet)()使用 SharedValue 可在线程间同步状态变化:
const { shared, runOnUI } = wx.worklet
const offset = shared(0)
function someWorklet() {
'worklet'
console.log(offset.value) // 输出新值 1
}
offset.value = 1
runOnUI(someWorklet)()驱动动画:applyAnimatedStyle
通过 applyAnimatedStyle 将共享变量绑定到节点样式,实现动画驱动。该方法可通过页面/组件实例访问。
<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() {
this._offset.value = Math.random()
}
})applyAnimatedStyle 的第二个参数 updater 为 worklet 函数,捕获了共享变量 offset。当 offset 的值变化时,updater 重新执行,将返回的新 styleObject 应用到选中节点。
配套接口:clearAnimatedStyle 可清除已绑定的动画样式。示例:手势 + 动画
当 worklet 动画和手势结合时,可实现真正流畅的交互动画:
<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
}
}
})手指在 circle 节点上移动时产生平滑拖动效果。handlepan 回调触发在 UI 线程,修改 offset 值直接在 UI 线程产生动画,无需绕回 JS 线程。
示例:自定义动画曲线
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() {
this._offset.value = timing(300, {
duration: 200,
easing: Easing.ease
})
}
})注意事项(重要)
1. worklet 函数内调用 wx API
页面/组件实例中定义的 worklet 类型回调函数,内部访问 wx 上的接口,必须通过 runOnJS 调回 JS 线程:
const { runOnJS, timing } = wx.worklet
Page({
handleTap() {
'worklet'
const showModal = this.showModal.bind(this)
// 场景一:直接返回 JS 线程
runOnJS(showModal)(msg)
// 场景二:动画完成回调里返回 JS 线程
const toValue = 100
timing(toValue, { duration: 300 }, () => {
'worklet'
runOnJS(showModal)(msg)
})
// 场景三:调用其它 worklet 函数(同步调用,无需 runOnJS)
this.doSomething()
},
doSomething() {
'worklet'
},
showModal(msg) {
wx.showModal({ title: msg })
}
})2. Object.freeze 冻结问题
worklet 函数引用的外部变量,对象类型将被 Object.freeze 冻结。使用时需直接访问对象上具体的属性,不要解构:
handleTap() {
'worklet'
// ✅ Correct - 直接访问属性,不会冻结 this.data
const msg = `hello ${this.data.msg}`
// ❌ Incorrect - 解构 this.data 会导致 this.data 被 Object.freeze 冻结
// const { msg } = this.data // setData 将失效!
}3. 页面方法访问
Page method 必须通过 this.methodName.bind(this) 访问:
handleTap() {
'worklet'
// ✅ 必须 bind(this)
const showModal = this.showModal.bind(this)
runOnJS(showModal)('hello')
}与 WXS 响应事件的对比
| 对比项 | WXS 响应事件 | Worklet 动画 |
|---|---|---|
| 适用引擎 | WebView | Skyline |
| 线程模型 | WXS 在视图层执行 | worklet 在 UI 线程执行 |
| 动画能力 | 仅样式和类操作 | 完整动画系统(timing/spring/decay) |
| 手势支持 | 基础 touch 事件 | 完整手势系统(pan/tap/long-press 等) |
| 基础库要求 | 2.4.4+ | 2.29.2+ |
相关接口汇总
- 基础类型:
shared、derived、cancelAnimation - 工具函数:
runOnUI、runOnJS - 动画类型:
timing、spring、decay - 组合动画:
sequence、repeat、delay - 缓动函数:
Easing - 页面实例方法:
applyAnimatedStyle、clearAnimatedStyle
线程通信:runOnUI 与 runOnJS
worklet 动画系统的核心机制是在 UI 线程执行动画逻辑。runOnUI 和 runOnJS 是在两个线程间切换执行上下文的关键工具。
runOnUI — 在 UI 线程执行
签名
function worklet.runOnUI(function fn)参数
| 参数 | 类型 | 说明 |
|---|---|---|
| fn | function | worklet 类型函数 |
返回值
function — runOnUI 为高阶函数,返回一个函数,执行时运行在 UI 线程。
基本用法
function someWorklet(greeting) {
'worklet'
console.log('hello', greeting) // print: [ui] hello Skyline
}
// runOnUI 返回新函数,调用时传参
wx.worklet.runOnUI(someWorklet)('Skyline')使用场景
1. 从 JS 线程手动触发 UI 线程逻辑
const { shared, runOnUI } = wx.worklet
const offset = shared(0)
function startAnimation() {
'worklet'
offset.value = timing(100)
}
// 在某个时机触发
runOnUI(startAnimation)()2. 传递参数到 UI 线程
function updatePosition(x, y) {
'worklet'
offsetX.value = x
offsetY.value = y
}
// 传递多个参数
runOnUI(updatePosition)(100, 200)注意:手势回调(如onGestureEvent)和applyAnimatedStyle的 updater 函数已自动在 UI 线程执行,无需额外使用runOnUI。
---
runOnJS — 回调 JS 线程
签名
function worklet.runOnJS(function fn)参数
| 参数 | 类型 | 说明 |
|---|---|---|
| fn | function | 未声明为 worklet 类型的普通函数 |
返回值
function — runOnJS 为高阶函数,返回一个函数,执行时运行在 JS 线程。
为什么需要 runOnJS?
worklet 函数运行在 UI 线程时,捕获的外部函数可能为 worklet 类型或普通函数。为了明确区分:
- 调用其它 worklet 函数时是同步调用
- 在 UI 线程执行 JS 线程的函数只能是异步
开发者容易混淆,试图同步获取 JS 线程的返回值,因此强制要求使用 runOnJS。
基本用法
function someFunc(greeting) {
console.log('hello', greeting)
}
function someWorklet() {
'worklet'
runOnJS(someFunc)('Skyline')
}
wx.worklet.runOnUI(someWorklet)()---
页面/组件中的三种调用场景
const { runOnJS, timing } = wx.worklet
Page({
data: { msg: 'Skyline' },
handleTap() {
'worklet'
// ⚠️ 直接访问属性,不要解构 this.data
const msg = `hello ${this.data.msg}`
// ⚠️ 页面方法必须 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 函数(同步,无需 runOnJS)
this.doSomething()
},
doSomething() {
'worklet'
},
showModal(msg) {
wx.showModal({ title: msg })
}
})关键规则总结
| 规则 | 说明 |
|---|---|
| MUST bind(this) | 页面方法在 worklet 中使用时必须 this.method.bind(this) |
| MUST runOnJS | worklet 中调用普通函数必须用 runOnJS 包裹 |
| NEVER 解构 this.data | const { msg } = this.data 会触发 Object.freeze 冻结 |
| NEVER 直接调用 wx API | worklet 中不能直接调用 wx.showModal 等,需 runOnJS |
| worklet 间调用是同步的 | worklet 函数调用另一个 worklet 函数是同步的,不需要 runOnJS |
| runOnJS 调用是异步的 | 不能依赖 runOnJS 的返回值 |
数据流向图
JS 线程 UI 线程
───────── ─────────
│ │
│ runOnUI(workletFn)(args) │
│ ─────────────────────────────> │
│ │ workletFn 执行
│ │ 修改 SharedValue
│ │ 驱动 applyAnimatedStyle
│ │
│ runOnJS(normalFn)(args) │
│ <───────────────────────────── │
│ │
│ normalFn 执行(异步) │
│ 可调用 wx API │Related skills
FAQ
Why use Skyline Worklet instead of JS-thread animations?
WeChat Mini Programs use a dual-thread architecture where UI events cross to JS and back, causing animation lag. Skyline Worklet runs animation logic on the UI thread via worklet functions and SharedValue for native-feel gesture and drag interactions.
Which animation APIs does skyline-worklet cover?
skyline-worklet covers worklet functions, SharedValue, timing, spring, decay, Easing curves, composed sequences, applyAnimatedStyle, and thread bridges runOnUI and runOnJS for interactive Skyline Mini Program animations.