Mpx 动画

前言

对于 Mpx 的动画方案,要实现多端兼容的目的(微信小程序、Android、iOS...)

  1. 最初方案:条件编译 ------ 在 Mpx 代码中嵌 RN 代码,借助 RN 动画库写一套动画,然后 CSS 写一套以适配多端

  2. 现在方案:Mpx 动画 API (即微信小程序动画API)

在 Mpx 中嵌RN动画

  1. 如果你的组件是 setup 语法,那不建议用这种写法,这种实现方式的前提是选项式写法。
  2. 引入 REACTHOOKSEXEC ,它允许我们在 mpx 嵌套 RN

组件使用的值需要通过 REACTHOOKSEXEC方法的返回值的方式进行声明。

(1) 导入必要的组件和函数

js 复制代码
import Animated, { useSharedValue, withTiming, useAnimatedStyle } from 'react-native-reanimated';

(2) 定义共享值

使用 useSharedValue 创建一个可以驱动动画的共享值。你可以把它想象成一个 React 状态,它会自动在应用的 JavaScript 端 和 原生端 之间保持同步(因此得名)。

js 复制代码
const translateX = useSharedValue(0); // 初始值为 0

(3) 定义动画样式

使用 useAnimatedStyle 将共享值应用到组件的样式上。通过 useAnimatedStyle,你可以将 SharedValue 的值映射到样式属性。

js 复制代码
const transformStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: translateX.value }],
}));

(4) 触发动画

通过修改共享值来触发动画。可以使用 withSpring、withTiming 等用于创建平滑过渡动画的函数。

js 复制代码
const onClick = () => {
  translateX.value = withTiming(2100); // 向右移动 200 个单位
};

完整代码:

js 复制代码
<template>
  <button bindtap="onClick">开始</button>
  <AnimatedView style="{{transformStyle}}">
    <view class="item" />
  </AnimatedView>
</template>

<script lang="ts">
import { createComponent, REACTHOOKSEXEC } from '@mpxjs/core'
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming
} from 'react-native-reanimated'

createComponent({
  properties: {},
  components: {
    AnimatedView: Animated.View
  },
  [REACTHOOKSEXEC](props: any) {
    const translateValue = useSharedValue(0)
    const transformStyle = useAnimatedStyle(() => ({
      transform: [{ translateX: translateValue.value }]
    }))
    const onClick = () => {
      translateValue.value = withTiming(200)
    }
    return {
      transformStyle,
      onClick
    }
  }
})
</script>

Mpx 动画API

1. mpx.createAnimation

创建一个动画实例 animation,调用实例的方法来描述动画。最后通过动画实例的 export 方法导出动画数据传递给组件的 animation 属性。

属性 类型 默认值 必填 说明
duration number 400 动画持续时间,单位 ms
timingFunction string 'linear' 动画的效果
delay number 0 动画延迟时间,单位 ms
transformOrigin string '50% 50% 0' 动画执行时变换的原点

timingFunction 可配置的值

合法值 说明 备注
'linear' 动画从头到尾的速度是相同的
'ease' 动画以低速开始,然后加快,在结束前变慢
'ease-in' 动画以低速开始
'ease-in-out' 动画以低速开始和结束
'ease-out' 动画以低速结束
'step-start' 动画第一帧就跳至结束状态直到结束 RN不支持
'step-end' 动画一直保持开始状态,最后一帧跳到结束状态 RN不支持
js 复制代码
<template>
  <view class="container">
    <view animation="{{animationData}}" />
  </view>
</template>

<script setup>
  import mpx, { ref } from '@mpxjs/core'
  const animation = mpx.createAnimation({
    duration: 1000,
    timingFunction: 'ease',
    delay: 0,
    transformOrigin: '50% 50% 0'
  })
  const animationData = ref({})

  defineExpose({
    animationData
  })
</script>

2. Animation.step(Object object)

表示一组动画完成。可以在一组动画中调用任意多个动画方法,一组动画中的所有动画会同时开始,一组动画完成后才会进行下一组动画。

  • 用于表示一个动画步骤的结束
  • 每次调用 step() 都会生成一个新的动画关键帧
  • 可以设置动画的持续时间、延迟、时间函数、原点参数,与 wx.createAnimation 参数一致
  • 必须在调用 export() 之前调用

3. Animation.export()

导出动画队列。export 方法每次调用后会清掉之前的动画操作。

  • 用于导出当前动画的配置数据
  • 返回一个包含动画信息的对象
  • 这个对象可以直接绑定到模板的 animation 属性
  • 必须在调用 step() 之后调用

导出的数据通过数据绑定更新视图

🙋‍♀️🌰:

js 复制代码
<template>
  <view class="container">
    <button class="btn" bindtap="animateX">animateX</button>
    <view class="item" animation="{{animationData}}" />
  </view>
</template>

<script setup>
  import mpx, { ref } from '@mpxjs/core'
  const animation = mpx.createAnimation({
    duration: 1000,
    timingFunction: 'ease'
  })
  const animationData = ref({})

  const animateX = () => {
    animationData.value = animation.translateX(100).step().export() // 水平移动 100px
  }

  defineExpose({
    animationData,
    animateX,
  })
</script>

这样就能实现一个简单的水平动画

4. 变换方法

方法 介绍 备注
translate, translateX, translateY, translateZ, translate3d 用于设置平移变换 RN 目前不支持 translateZ 和 translate3d
matrix , matrix3d 用于设置矩阵变换 RN 目前不支持
rotate, rotateX, rotateY, rotateZ, rotate3d 用于设置旋转变换
scale, scaleX, scaleY, scaleZ, scale3d 用于设置缩放变换 RN 目前不支持 scaleZ 和 scale3d
skew, skewX, skewY 用于设置倾斜变换 Android skewX 变换偏差

5. 样式属性

这些方法用于设置动画的样式属性。

  • opacity
  • backgroundColor
  • width
  • height
  • top
  • right
  • bottom
  • left

基本使用步骤

1. 定义动画

  • 使用 mpx.createAnimation 初始化动画实例。
  • 设置动画的变换和样式属性。
  • 使用 step 方法定义关键帧,并为每个关键帧设置持续时间、延迟、时间函数等。
js 复制代码
const animation = mpx.createAnimation({
  duration: 1000,
  timingFunction: 'ease',
  delay: 0,
  transformOrigin: '50% 50% 0'
})

const animationData = ref({})

const handleClick = () => {
  animationData.value = animation.translateX(100).step().export() // 水平移动 100px
}

2. 导出动画数据

  • 使用 export 方法导出动画数据

3. 在模板中使用动画

js 复制代码
<view class="item" animation="{{animationData}}" />

4. 触发动画

  • 手动触发
    • 事件绑定
  • 自动触发
    • 生命周期
    • 回调

动画事件

使用 createAnimation 创建动画过程中,可以使用下述事件来监听动画事件。

但是目前 RN 支持该事件,在小程序中可以,如果只是在小程序中做一些回调可以使用。

事件名 含义 备注
transitionend wx.createAnimation 结束一个阶段 RN 不支持

还需要注意的是,这个事件不是冒泡事件,需要绑定在真正发生了动画的节点上才会生效。

js 复制代码
<view class="item" bindtransitionend="animationEnd" animation="{{animationData}}

进阶

mpx.createAnimation源码

RN 部分

源码创建了一个 Animated 类,定义和管理动画。声明哪些方法 RN 还未支持,提供了报错提示,通过 step 和 export 方法,生成动画数据并导出供其他组件使用

编译阶段,mpx 使用 useAnimationHooks() 这个钩子来将小程序语法转成 drn 语法,即 RN 动画库 react-native-reanimated 支持的语法。

主要阶段可以划分为:

  1. 当使用 mpx-view 标签时,钩子会检测入参是否含有 animation 这个属性,有则用 Animated.view 来代替 View 组件。
  2. 使用 useAnimationHooks 来将入参进行转义,最终适配 react-native-reanimated 语法

总结

  • mpx.createAnimation()基于小程序,通过转义也实现了 RN 的适配
  • 使用 mpx.createAnimation() 来实现动画,避免开发多套代码
  • 不足之处在于事件处理时并未做对应抹平

参考文档

相关推荐
baozj4 分钟前
🚀 手动改 500 个文件?不存在的!我用 AST 撸了个 Vue 国际化神器
前端·javascript·vue.js
用户40993225021212 分钟前
为什么Vue 3的计算属性能解决模板臃肿、性能优化和双向同步三大痛点?
前端·ai编程·trae
海云前端113 分钟前
Vue首屏加速秘籍 组件按需加载真能省一半时间
前端
蛋仔聊测试14 分钟前
Playwright 中route 方法模拟测试数据(Mocking)详解
前端·python·测试
零号机25 分钟前
使用TRAE 30分钟极速开发一款划词中英互译浏览器插件
前端·人工智能
疯狂踩坑人1 小时前
结合400行mini-react代码,图文解说React原理
前端·react.js·面试
Mintopia1 小时前
🚀 共绩算力:3分钟拥有自己的文生图AI服务-容器化部署 StableDiffusion1.5-WebUI 应用
前端·人工智能·aigc
街尾杂货店&1 小时前
CSS - transition 过渡属性及使用方法(示例代码)
前端·css
CH_X_M1 小时前
为什么在AI对话中选择用sse而不是web socket?
前端
Mintopia1 小时前
🧠 量子计算对AIGC的潜在影响:Web技术的未来可能性
前端·javascript·aigc