前言
对于 Mpx 的动画方案,要实现多端兼容的目的(微信小程序、Android、iOS...)
-
最初方案:条件编译 ------ 在 Mpx 代码中嵌 RN 代码,借助 RN 动画库写一套动画,然后 CSS 写一套以适配多端
-
现在方案:Mpx 动画 API (即微信小程序动画API)
在 Mpx 中嵌RN动画
- 如果你的组件是 setup 语法,那不建议用这种写法,这种实现方式的前提是选项式写法。
- 引入
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 支持的语法。
主要阶段可以划分为:
- 当使用 mpx-view 标签时,钩子会检测入参是否含有 animation 这个属性,有则用 Animated.view 来代替 View 组件。
- 使用 useAnimationHooks 来将入参进行转义,最终适配 react-native-reanimated 语法
总结
- mpx.createAnimation()基于小程序,通过转义也实现了 RN 的适配
- 使用 mpx.createAnimation() 来实现动画,避免开发多套代码
- 不足之处在于事件处理时并未做对应抹平