美文网首页
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 应用到选中节点上。

    相关文章

      网友评论

          本文标题:worklet动画

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