美文网首页
worklet动画

worklet动画

作者: BoxJing | 来源:发表于2024-06-23 16:50 被阅读0次

小程序采用双线程架构,渲染线程(UI 线程)和逻辑线程(JS 线程)分离。JS 线程不会影响 UI 线程的动画表现,如滚动效果。但引入的问题是,UI 线程的事件发生后,需跨线程传递到 JS 线程,进而触发开发者回调,当做交互动画(如拖动元素)时,这种异步性会带来较大的延迟和不稳定。worklet动画正是为解决这类问题而诞生的,使得小程序可以做到类原生动画般的体验,有一点需要注意,worklet动画相关接口仅在 Skyline 渲染模式下才能使用。
下面直接举例一个简单的循环动画效果来展示下worklet动画的便捷性,封装了一个Animation-View的组件:

const { shared, Easing, timing, runOnUI, runOnJS, repeat } = wx.worklet
Component({
  data: {

  },
  methods: {
    startMoveAnimation() {
      console.log('开始MoveA动画')
      this._offset.value = repeat(timing(300,{
        duration: 1000,
        easing: Easing.ease
      }), -1, true);
    },
    startScaleAnimation() {
      this._scale.value = repeat(timing(1.5,{
        duration: 500,
        easing: Easing.ease
      }), -1, true);
    },
  },
  ready() {
    let that = this
    setTimeout(() => {
      console.log('ready了 开始动画')
      that.startMoveAnimation();
      that.startScaleAnimation();
    }, 2000);
    const offset = shared(0)
    this.applyAnimatedStyle('#moved-box', () => {
      'worklet'
      return {
        transform: `translateX(${offset.value}px)`
      }
    })
    this._offset = offset

    const scale = shared(1)
    this.applyAnimatedStyle('#scale-box', () => {
      'worklet'
      return {
        transform: `scale(${scale.value})`
      }
    })
    this._scale = scale
  }
});

下面是wxml文件:

<view class="container">
  <view id="moved-box" class="sliding-view"></view>
  <view id="scale-box" class="scale-view"></view>
</view>

下面是wxss文件:

.container {
  position: relative;
  width: 100%;
  height: 500px;
  overflow: hidden;
}

.sliding-view {
  width: 100px; /* 视图的宽度 */
  height: 120px; /* 视图的高度 */
  background-color: blue; /* 视图的背景色 */
}
.scale-view {
  margin-left: 60px;
  width: 200px; /* 视图的宽度 */
  height: 200px; /* 视图的高度 */
  background-color: red; /* 视图的背景色 */
}

我们在组件的ready中给2个小组件声明了移动和缩放的worklet动画,然后延迟2秒后执行,最外层是一个repeat,次数传的-1,代表这会一直循环执行动画。用起来的确非常的方便。

概念一: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] hello

worklet 函数间相互调用

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 线程

function someFunc(greeting) {
  console.log('hello', greeting);
}

function someWorklet() {
  'worklet'
  // 访问非 worklet 函数时,需使用 runOnJS
  // someFunc 运行在 JS 线程
  runOnJS(someFunc)('skyline')
}

wx.worklet.runOnUI(someWorklet)() // print: hello skyline

概念二:共享变量

JS 线程创建,可在两个线程间同步的变量。

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)()

shared 函数创建的变量,我们称为 sharedValue 共享变量。用法上可类比 vue3 中的 ref,对它的读写都需要通过 .value 属性,但需注意的是它们并不是一个概念。sharedValue 的用途主要如下。

跨线程共享数据

worklet 函数捕获的外部变量,实际上会被序列化后生成在 UI 线程的拷贝,如下代码中, someWorklet 捕获了 obj 变量,尽管我们修改了 objname 属性,但在 someWorklet 声明的位置,obj 已经被序列化发送到了 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)() 

驱动动画

worklet 函数和共享变量就是用来解决交互动画问题的。相关接口 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() {
    // 点击时修改 sharedValue 值,驱动小球移动
    this._offset.value = Math.random()
  }
})

当点击按钮 #btn 时,我们用随机数给 offset 进行赋值,小球会随之移动。

applyAnimatedStyle 接口的第二个参数 updater 为一个 worklet 函数,其捕获了共享变量 offset,当 offset 的值变化时,updater 会重新执行,并将返回的新 styleObject 应用到选中节点上。

相关文章

  • ERROR Error: Reanimated 2 failed

    ERROR Error: Reanimated 2 failed to create a worklet http...

  • 深入分析Web worker, Service worker,

    Web worker,service worker和worklet。所有这些就是我所说的“ Javascript ...

  • Android回顾--(十六) 动画简析

    动画: 补间动画(Tween动画) 帧动画(Frame动画) 属性动画(Property动画) 补间动画 特点: ...

  • 在山西太原,做个二维动画需要哪些制作流程?

    二维动画有哪些类型? flash动画,课件动画,mg动画,ae动画,GIF动画,手绘动画,网页动画,企业动画,宣传...

  • Android 动画

    【Android 动画】 动画分类补间动画(Tween动画)帧动画(Frame 动画)属性动画(Property ...

  • 动画学习

    动画 分为 组动画,属性动画,渐变动画,其中属性动画包括 普通动画和关键帧动画,其他动弹动画,动画层分为 pres...

  • Android动画

    Android动画分类: 视图动画:补间动画、逐帧动画 属性动画 视图动画 补间动画 可以在xml中定义动画,然后...

  • iOS动画

    iOS动画-从UIView动画说起iOS动画-Transform和KeyFrame动画iOS动画-layout动画...

  • Android动画之视图动画

    分类 Android动画主要包括视图动画和属性动画。视图动画包括Tween动画和Frame动画。Tween动画又包...

  • Android 动画

    android动画分为三种 帧动画,视图动画(补间动画),属性动画逐帧动画 视图动画 属性动画 Window和A...

网友评论

      本文标题:worklet动画

      本文链接:https://www.haomeiwen.com/subject/tvqrcjtx.html