欧美日韩精品一区二区在线线,一级无码在线收看,精品国产高清91,久久精品欧美电影

# worklet 動畫

小程序采用雙線程架構(gòu),渲染線程(UI 線程)和邏輯線程(JS 線程)分離。JS 線程不會影響 UI 線程的動畫表現(xiàn),如滾動效果。但引入的問題是,UI 線程的事件發(fā)生后,需跨線程傳遞到 JS 線程,進(jìn)而觸發(fā)開發(fā)者回調(diào),當(dāng)做交互動畫(如拖動元素)時(shí),這種異步性會帶來較大的延遲和不穩(wěn)定。

worklet 動畫正是為解決這類問題而誕生的,使得小程序可以做到類原生動畫般的體驗(yàn)。

# 立即體驗(yàn)

使用 worklet 動畫能力時(shí)確保以下兩項(xiàng):

  • 確保開發(fā)者工具右上角 > 詳情 > 本地設(shè)置里的 將 JS 編譯成 ES5 選項(xiàng)被勾選上 (代碼包體積會少量增加)
  • worklet 動畫相關(guān)接口僅在 Skyline 渲染模式下才能使用

首先,我們需要了解一些相關(guān)概念。

# 概念一:worklet 函數(shù)

一種聲明在開發(fā)者代碼中,可運(yùn)行在 JS 線程或 UI 線程的函數(shù),函數(shù)體頂部有 'worklet' 指令聲明。

# worklet 函數(shù)定義

function someWorklet(greeting) {
  'worklet';
  console.log(greeting);
}

// 運(yùn)行在 JS 線程
someWorklet('hello') // print: hello

// 運(yùn)行在 UI 線程
wx.worklet.runOnUI(someWorklet)('hello') // print: [ui] hello

# worklet 函數(shù)間相互調(diào)用

const name = 'skyline'

function anotherWorklet() {
  'worklet';
  return 'hello ' + name;
}

// worklet 函數(shù)間可互相調(diào)用
function someWorklet() {
  'worklet';
  const greeting = anotherWorklet();
  console.log('another worklet says ', greeting);
}

wx.worklet.runOnUI(someWorklet)() // print: [ui] another worklet says hello skyline

# 從 UI 線程調(diào)回到 JS 線程

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

function someWorklet() {
  'worklet'
  // 訪問非 worklet 函數(shù)時(shí),需使用 runOnJS
  // someFunc 運(yùn)行在 JS 線程
  runOnJS(someFunc)('skyline')
}

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

# 概念二:共享變量

JS 線程創(chuàng)建,可在兩個(gè)線程間同步的變量。

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 函數(shù)創(chuàng)建的變量,我們稱為 sharedValue 共享變量。用法上可類比 vue3 中的 ref,對它的讀寫都需要通過 .value 屬性,但需注意的是它們并不是一個(gè)概念。sharedValue 的用途主要如下。

# 跨線程共享數(shù)據(jù)

worklet 函數(shù)捕獲的外部變量,實(shí)際上會被序列化后生成在 UI 線程的拷貝,如下代碼中, someWorklet 捕獲了 obj 變量,盡管我們修改了 objname 屬性,但在 someWorklet 聲明的位置,obj 已經(jīng)被序列化發(fā)送到了 UI 線程,因此后續(xù)的修改是無法同步的。

const obj = { name: 'skyline'}
function someWorklet() {
  'worklet'
  console.log(obj.name) // 輸出的仍舊是 skyline
}
obj.name = 'change name'

wx.worklet.runOnUI(someWorklet)() 

sharedValue 就是用來在線程間同步狀態(tài)變化的變量。

const { shared, runOnUI } = wx.worklet

const offset = shared(0)
function someWorklet() {
  'worklet'
  console.log(offset.value) // 輸出的是新值 1
}
offset.value = 1

runOnUI(someWorklet)() 

# 驅(qū)動動畫

worklet 函數(shù)和共享變量就是用來解決交互動畫問題的。相關(guān)接口 applyAnimatedStyle 可通過頁面/組件實(shí)例訪問,接口文檔參考

<view id="moved-box"></view>
<view id="btn" bind:tap="tap">點(diǎn)擊驅(qū)動小球移動</view>
Page({
  onLoad() {
    const offset = wx.worklet.shared(0)
    this.applyAnimatedStyle('#moved-box', () => {
      'worklet';
      return {
        transform: `translateX(${offset.value}px)`
      }
    })
    this._offset = offset
  },
  tap() {
    // 點(diǎn)擊時(shí)修改 sharedValue 值,驅(qū)動小球移動
    this._offset.value = Math.random()
  }
})

當(dāng)點(diǎn)擊按鈕 #btn 時(shí),我們用隨機(jī)數(shù)給 offset 進(jìn)行賦值,小球會隨之移動。

applyAnimatedStyle 接口的第二個(gè)參數(shù) updater 為一個(gè) worklet 函數(shù),其捕獲了共享變量 offset,當(dāng) offset 的值變化時(shí),updater 會重新執(zhí)行,并將返回的新 styleObject 應(yīng)用到選中節(jié)點(diǎn)上。

當(dāng)然,光看這個(gè)例子,跟用 setData 看好像沒有什么區(qū)別。但當(dāng) worklet 動畫和手勢結(jié)合時(shí),就產(chǎn)生了質(zhì)變。

# 示例用法

# 手勢處理

<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;
    }
  }
});

當(dāng)手指在 circle 節(jié)點(diǎn)上移動時(shí),會產(chǎn)生平滑的拖動效果。handlepan 回調(diào)觸發(fā)在 UI 線程,同時(shí)我們修改了 offset 的值,會在 UI 線程產(chǎn)生動畫,不必再繞回到 JS 線程。

查看更多手勢處理的示例代碼

# 自定義動畫曲線

<view id="moved-box"></view>
<view id="btn" bind:tap="tap">點(diǎn)擊驅(qū)動小球移動</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() {
    /**
     * 目標(biāo)值 300
     * 動畫時(shí)長 200ms
     * 動畫曲線 Easing.ease
     */
    this._offset.value = timing(300, {
      duration: 200,
      easing: Easing.ease
    })
  }
})

內(nèi)置如 timing、spring 等常見動畫方式的封裝方法,開發(fā)者可自定義動畫曲線,同時(shí)可對不同的動畫類型進(jìn)行組合、重復(fù),形成交織動畫。

查看更多不同動畫類型用法

查看更多緩動函數(shù)的示例代碼

# 相關(guān)接口

# 注意事項(xiàng)

worklet 函數(shù)內(nèi)部有一些調(diào)用上的限制需要留意

  1. 頁面/組件實(shí)例中定義的 worklet 類型回調(diào)函數(shù),內(nèi)部訪問 wx 上的接口,可按如下方式,通過 runOnJS 調(diào)回到 JS 線程。
  2. worklet 函數(shù)引用的外部變量,對象類型將被 Object.freeze 凍結(jié),使用時(shí)需直接訪問對象上具體的屬性。
<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'

    // 非常重要!?。?/span>
    // const { msg } = this.data
    // 通過解構(gòu) this.data 訪問 msg,此時(shí) this.data 將被 Object.freeze 凍結(jié),會導(dǎo)致 setData 無法生效
    // 而通過 this.data.msg 則不會凍結(jié) this.data
    const msg = `hello ${this.data.msg}`

    // 非常重要!?。?/span>
    // Page method 必須通過 this.methodName.bind(this) 訪問
    const showModal = this.showModal.bind(this)
    
    // 場景一:返回 JS 線程
    runOnJS(showModal)(msg)

    // 場景二:動畫完成回調(diào)里返回 JS 線程
    const toValue = 100
    timing(toValue, { duration: 300 }, () => {
      'worklet'
      runOnJS(showModal)(msg)
    })

    // 場景三:調(diào)用其它 worklet 函數(shù)
    this.doSomething()
  },
  doSomething() {
    'worklet'
  },
  showModal(msg) {
    wx.showModal({
      title: msg // title: hello skyline
    })
  },
})