@mpxjs/webpack-plugin 2.10.17-beta.2 → 2.10.17-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/config.js +60 -0
- package/lib/file-loader.js +4 -1
- package/lib/global.d.ts +16 -0
- package/lib/index.js +22 -2
- package/lib/json-compiler/index.js +13 -4
- package/lib/platform/json/wx/index.js +6 -0
- package/lib/platform/style/wx/index.js +57 -33
- package/lib/platform/template/wx/component-config/ad.js +5 -0
- package/lib/platform/template/wx/component-config/button.js +9 -2
- package/lib/platform/template/wx/component-config/camera.js +25 -3
- package/lib/platform/template/wx/component-config/canvas.js +8 -1
- package/lib/platform/template/wx/component-config/cover-image.js +7 -2
- package/lib/platform/template/wx/component-config/cover-view.js +3 -1
- package/lib/platform/template/wx/component-config/form.js +27 -2
- package/lib/platform/template/wx/component-config/image.js +5 -0
- package/lib/platform/template/wx/component-config/input.js +10 -0
- package/lib/platform/template/wx/component-config/label.js +10 -2
- package/lib/platform/template/wx/component-config/map.js +11 -0
- package/lib/platform/template/wx/component-config/movable-area.js +4 -1
- package/lib/platform/template/wx/component-config/movable-view.js +17 -2
- package/lib/platform/template/wx/component-config/navigator.js +26 -0
- package/lib/platform/template/wx/component-config/picker-view.js +12 -0
- package/lib/platform/template/wx/component-config/picker.js +3 -1
- package/lib/platform/template/wx/component-config/progress.js +11 -1
- package/lib/platform/template/wx/component-config/rich-text.js +5 -0
- package/lib/platform/template/wx/component-config/scroll-view.js +12 -1
- package/lib/platform/template/wx/component-config/slider.js +8 -0
- package/lib/platform/template/wx/component-config/swiper-item.js +5 -2
- package/lib/platform/template/wx/component-config/swiper.js +10 -0
- package/lib/platform/template/wx/component-config/text.js +5 -0
- package/lib/platform/template/wx/component-config/textarea.js +19 -2
- package/lib/platform/template/wx/component-config/unsupported.js +10 -1
- package/lib/platform/template/wx/component-config/video.js +10 -0
- package/lib/platform/template/wx/index.js +21 -1
- package/lib/react/LoadAsyncChunkModule.js +1 -1
- package/lib/react/processStyles.js +21 -9
- package/lib/react/style-helper.js +76 -13
- package/lib/resolver/AddModePlugin.js +23 -8
- package/lib/runtime/components/react/animationHooks/index.ts +75 -0
- package/lib/runtime/components/react/animationHooks/useAnimationAPIHooks.ts +198 -0
- package/lib/runtime/components/react/animationHooks/useTransitionHooks.ts +297 -0
- package/lib/runtime/components/react/animationHooks/utils.ts +196 -0
- package/lib/runtime/components/react/context.ts +7 -1
- package/lib/runtime/components/react/dist/animationHooks/index.d.ts +16 -0
- package/lib/runtime/components/react/dist/animationHooks/index.d.ts.map +1 -0
- package/lib/runtime/components/react/dist/animationHooks/index.js +67 -0
- package/lib/runtime/components/react/dist/animationHooks/useAnimationAPIHooks.d.ts +4 -0
- package/lib/runtime/components/react/dist/animationHooks/useAnimationAPIHooks.d.ts.map +1 -0
- package/lib/runtime/components/react/dist/animationHooks/useAnimationAPIHooks.js +182 -0
- package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.d.ts +4 -0
- package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.d.ts.map +1 -0
- package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.js +274 -0
- package/lib/runtime/components/react/dist/animationHooks/utils.d.ts +110 -0
- package/lib/runtime/components/react/dist/animationHooks/utils.d.ts.map +1 -0
- package/lib/runtime/components/react/dist/animationHooks/utils.js +150 -0
- package/lib/runtime/components/react/dist/context.d.ts +6 -1
- package/lib/runtime/components/react/dist/context.d.ts.map +1 -1
- package/lib/runtime/components/react/dist/mpx-camera.d.ts +32 -0
- package/lib/runtime/components/react/dist/mpx-camera.d.ts.map +1 -0
- package/lib/runtime/components/react/dist/mpx-camera.jsx +236 -0
- package/lib/runtime/components/react/dist/mpx-input.d.ts +2 -0
- package/lib/runtime/components/react/dist/mpx-input.d.ts.map +1 -1
- package/lib/runtime/components/react/dist/mpx-input.jsx +21 -10
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.d.ts.map +1 -1
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +3 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-swiper.d.ts +10 -0
- package/lib/runtime/components/react/dist/mpx-swiper.d.ts.map +1 -1
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +28 -16
- package/lib/runtime/components/react/dist/mpx-view.d.ts +3 -2
- package/lib/runtime/components/react/dist/mpx-view.d.ts.map +1 -1
- package/lib/runtime/components/react/dist/mpx-view.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +1 -1
- package/lib/runtime/components/react/dist/utils.d.ts +1 -0
- package/lib/runtime/components/react/dist/utils.d.ts.map +1 -1
- package/lib/runtime/components/react/dist/utils.jsx +34 -13
- package/lib/runtime/components/react/mpx-camera.tsx +327 -0
- package/lib/runtime/components/react/mpx-input.tsx +26 -10
- package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +3 -0
- package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +2 -2
- package/lib/runtime/components/react/mpx-swiper.tsx +43 -15
- package/lib/runtime/components/react/mpx-view.tsx +4 -5
- package/lib/runtime/components/react/mpx-web-view.tsx +1 -1
- package/lib/runtime/components/react/types/global.d.ts +1 -0
- package/lib/runtime/components/react/utils.tsx +34 -16
- package/lib/runtime/optionProcessor.js +5 -0
- package/lib/runtime/optionProcessorReact.js +7 -0
- package/lib/runtime/stringify.wxs +2 -2
- package/lib/style-compiler/strip-conditional-loader/rebaseUrl.js +225 -0
- package/lib/style-compiler/strip-conditional-loader.js +55 -180
- package/lib/template-compiler/compiler.js +1 -3
- package/lib/utils/dom-tag-config.js +1 -1
- package/lib/utils/string.js +25 -1
- package/package.json +2 -1
- package/lib/runtime/components/react/dist/useAnimationHooks.d.ts +0 -33
- package/lib/runtime/components/react/dist/useAnimationHooks.d.ts.map +0 -1
- package/lib/runtime/components/react/dist/useAnimationHooks.js +0 -289
- package/lib/runtime/components/react/useAnimationHooks.ts +0 -320
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef } from 'react';
|
|
2
|
-
import { Easing, useSharedValue, withTiming, useAnimatedStyle, withSequence, withDelay, makeMutable, cancelAnimation, runOnJS } from 'react-native-reanimated';
|
|
3
|
-
import { error, hasOwn, collectDataset } from '@mpxjs/utils';
|
|
4
|
-
import { useRunOnJSCallback } from './utils';
|
|
5
|
-
// 微信 timingFunction 和 RN Easing 对应关系
|
|
6
|
-
const EasingKey = {
|
|
7
|
-
linear: Easing.linear,
|
|
8
|
-
ease: Easing.inOut(Easing.ease),
|
|
9
|
-
'ease-in': Easing.in(Easing.poly(3)),
|
|
10
|
-
'ease-in-out': Easing.inOut(Easing.poly(3)),
|
|
11
|
-
'ease-out': Easing.out(Easing.poly(3))
|
|
12
|
-
// 'step-start': '',
|
|
13
|
-
// 'step-end': ''
|
|
14
|
-
};
|
|
15
|
-
const TransformInitial = {
|
|
16
|
-
// matrix: 0,
|
|
17
|
-
// matrix3d: 0,
|
|
18
|
-
// rotate: '0deg',
|
|
19
|
-
rotateX: '0deg',
|
|
20
|
-
rotateY: '0deg',
|
|
21
|
-
rotateZ: '0deg',
|
|
22
|
-
// rotate3d:[0,0,0]
|
|
23
|
-
// scale: 1,
|
|
24
|
-
// scale3d: [1, 1, 1],
|
|
25
|
-
scaleX: 1,
|
|
26
|
-
scaleY: 1,
|
|
27
|
-
// scaleZ: 1,
|
|
28
|
-
// skew: 0,
|
|
29
|
-
skewX: '0deg',
|
|
30
|
-
skewY: '0deg',
|
|
31
|
-
// translate: 0,
|
|
32
|
-
// translate3d: 0,
|
|
33
|
-
translateX: 0,
|
|
34
|
-
translateY: 0
|
|
35
|
-
// translateZ: 0,
|
|
36
|
-
};
|
|
37
|
-
// 动画默认初始值
|
|
38
|
-
const InitialValue = Object.assign({
|
|
39
|
-
opacity: 1,
|
|
40
|
-
backgroundColor: 'transparent',
|
|
41
|
-
width: 0,
|
|
42
|
-
height: 0,
|
|
43
|
-
top: 0,
|
|
44
|
-
right: 0,
|
|
45
|
-
bottom: 0,
|
|
46
|
-
left: 0,
|
|
47
|
-
transformOrigin: ['50%', '50%', 0]
|
|
48
|
-
}, TransformInitial);
|
|
49
|
-
const TransformOrigin = 'transformOrigin';
|
|
50
|
-
// transform
|
|
51
|
-
const isTransform = (key) => Object.keys(TransformInitial).includes(key);
|
|
52
|
-
// transform 数组转对象
|
|
53
|
-
function getTransformObj(transforms) {
|
|
54
|
-
'worklet';
|
|
55
|
-
return transforms.reduce((transformObj, item) => {
|
|
56
|
-
return Object.assign(transformObj, item);
|
|
57
|
-
}, {});
|
|
58
|
-
}
|
|
59
|
-
export default function useAnimationHooks(props) {
|
|
60
|
-
const { style: originalStyle = {}, animation, enableAnimation, transitionend, layoutRef } = props;
|
|
61
|
-
const enableStyleAnimation = enableAnimation || !!animation;
|
|
62
|
-
const enableAnimationRef = useRef(enableStyleAnimation);
|
|
63
|
-
if (enableAnimationRef.current !== enableStyleAnimation) {
|
|
64
|
-
error('[Mpx runtime error]: animation use should be stable in the component lifecycle, or you can set [enable-animation] with true.');
|
|
65
|
-
}
|
|
66
|
-
if (!enableAnimationRef.current)
|
|
67
|
-
return { enableStyleAnimation: false };
|
|
68
|
-
// id 标识
|
|
69
|
-
const id = animation?.id || -1;
|
|
70
|
-
// 有动画样式的 style key
|
|
71
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
72
|
-
const animatedStyleKeys = useSharedValue([]);
|
|
73
|
-
// 记录动画key的style样式值 没有的话设置为false
|
|
74
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
75
|
-
const animatedKeys = useRef({});
|
|
76
|
-
// 记录上次style map
|
|
77
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
78
|
-
const lastStyleRef = useRef({});
|
|
79
|
-
// ** 全量 style prop sharedValue
|
|
80
|
-
// 不能做增量的原因:
|
|
81
|
-
// 1 尝试用 useRef,但 useAnimatedStyle 访问后的 ref 不能在增加新的值,被冻结
|
|
82
|
-
// 2 尝试用 useSharedValue,因为实际触发的 style prop 需要是 sharedValue 才能驱动动画,若外层 shareValMap 也是 sharedValue,动画无法驱动。
|
|
83
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
84
|
-
const shareValMap = useMemo(() => {
|
|
85
|
-
return Object.keys(InitialValue).reduce((valMap, key) => {
|
|
86
|
-
const defaultVal = getInitialVal(key, isTransform(key));
|
|
87
|
-
valMap[key] = makeMutable(defaultVal);
|
|
88
|
-
return valMap;
|
|
89
|
-
}, {});
|
|
90
|
-
}, []);
|
|
91
|
-
// ** style更新同步
|
|
92
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
// style 更新后同步更新 lastStyleRef & shareValMap
|
|
95
|
-
updateStyleVal();
|
|
96
|
-
}, [originalStyle]);
|
|
97
|
-
// ** 获取动画样式prop & 驱动动画
|
|
98
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
99
|
-
useEffect(() => {
|
|
100
|
-
if (id === -1)
|
|
101
|
-
return;
|
|
102
|
-
// 更新动画样式 key map
|
|
103
|
-
animatedKeys.current = getAnimatedStyleKeys();
|
|
104
|
-
const keys = Object.keys(animatedKeys.current);
|
|
105
|
-
animatedStyleKeys.value = formatAnimatedKeys([TransformOrigin, ...keys]);
|
|
106
|
-
// 驱动动画
|
|
107
|
-
createAnimation(keys);
|
|
108
|
-
}, [id]);
|
|
109
|
-
// ** 清空动画
|
|
110
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
111
|
-
useEffect(() => {
|
|
112
|
-
return () => {
|
|
113
|
-
Object.values(shareValMap).forEach((value) => {
|
|
114
|
-
cancelAnimation(value);
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
}, []);
|
|
118
|
-
// 根据 animation action 创建&驱动动画
|
|
119
|
-
function createAnimation(animatedKeys = []) {
|
|
120
|
-
const actions = animation?.actions || [];
|
|
121
|
-
const sequence = {};
|
|
122
|
-
const lastValueMap = {};
|
|
123
|
-
actions.forEach(({ animatedOption, rules, transform }, index) => {
|
|
124
|
-
const { delay, duration, timingFunction, transformOrigin } = animatedOption;
|
|
125
|
-
const easing = EasingKey[timingFunction] || Easing.inOut(Easing.quad);
|
|
126
|
-
let needSetCallback = true;
|
|
127
|
-
const setTransformOrigin = (finished) => {
|
|
128
|
-
'worklet';
|
|
129
|
-
// 动画结束后设置下一次transformOrigin
|
|
130
|
-
if (finished) {
|
|
131
|
-
if (index < actions.length - 1) {
|
|
132
|
-
const transformOrigin = actions[index + 1].animatedOption?.transformOrigin;
|
|
133
|
-
transformOrigin && (shareValMap[TransformOrigin].value = transformOrigin);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
if (index === 0) {
|
|
138
|
-
// 设置当次中心
|
|
139
|
-
shareValMap[TransformOrigin].value = transformOrigin;
|
|
140
|
-
}
|
|
141
|
-
// 添加每个key的多次step动画
|
|
142
|
-
animatedKeys.forEach(key => {
|
|
143
|
-
const ruleV = isTransform(key) ? transform.get(key) : rules.get(key);
|
|
144
|
-
// key不存在,第一轮取shareValMap[key]value,非第一轮取上一轮的
|
|
145
|
-
const toVal = ruleV !== undefined
|
|
146
|
-
? ruleV
|
|
147
|
-
: index > 0
|
|
148
|
-
? lastValueMap[key]
|
|
149
|
-
: shareValMap[key].value;
|
|
150
|
-
const animation = getAnimation({ key, value: toVal }, { delay, duration, easing }, needSetCallback ? setTransformOrigin : undefined);
|
|
151
|
-
needSetCallback = false;
|
|
152
|
-
if (!sequence[key]) {
|
|
153
|
-
sequence[key] = [animation];
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
sequence[key].push(animation);
|
|
157
|
-
}
|
|
158
|
-
// 更新一下 lastValueMap
|
|
159
|
-
lastValueMap[key] = toVal;
|
|
160
|
-
});
|
|
161
|
-
// 赋值驱动动画
|
|
162
|
-
animatedKeys.forEach((key) => {
|
|
163
|
-
const animations = sequence[key];
|
|
164
|
-
shareValMap[key].value = withSequence(...animations);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
function withTimingCallback(finished, current, duration) {
|
|
169
|
-
if (!transitionend)
|
|
170
|
-
return;
|
|
171
|
-
const target = {
|
|
172
|
-
id: animation?.id || -1,
|
|
173
|
-
dataset: collectDataset(props),
|
|
174
|
-
offsetLeft: layoutRef?.current?.offsetLeft || 0,
|
|
175
|
-
offsetTop: layoutRef?.current?.offsetTop || 0
|
|
176
|
-
};
|
|
177
|
-
transitionend({
|
|
178
|
-
type: 'transitionend',
|
|
179
|
-
// elapsedTime 对齐wx 单位s
|
|
180
|
-
detail: { elapsedTime: duration ? duration / 1000 : 0, finished, current },
|
|
181
|
-
target,
|
|
182
|
-
currentTarget: target,
|
|
183
|
-
timeStamp: Date.now()
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
187
|
-
const runOnJSCallbackRef = useRef({
|
|
188
|
-
withTimingCallback
|
|
189
|
-
});
|
|
190
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
191
|
-
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef);
|
|
192
|
-
// 创建单个animation
|
|
193
|
-
function getAnimation({ key, value }, { delay, duration, easing }, callback) {
|
|
194
|
-
const animation = typeof callback === 'function'
|
|
195
|
-
? withTiming(value, { duration, easing }, (finished, current) => {
|
|
196
|
-
callback(finished, current);
|
|
197
|
-
if (transitionend && finished) {
|
|
198
|
-
runOnJS(runOnJSCallback)('withTimingCallback', finished, current, duration);
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
: withTiming(value, { duration, easing });
|
|
202
|
-
return delay ? withDelay(delay, animation) : animation;
|
|
203
|
-
}
|
|
204
|
-
// 获取样式初始值(prop style or 默认值)
|
|
205
|
-
function getInitialVal(key, isTransform = false) {
|
|
206
|
-
if (isTransform && Array.isArray(originalStyle.transform)) {
|
|
207
|
-
let initialVal = InitialValue[key];
|
|
208
|
-
// 仅支持 { transform: [{rotateX: '45deg'}, {rotateZ: '0.785398rad'}] } 格式的初始样式
|
|
209
|
-
originalStyle.transform.forEach(item => {
|
|
210
|
-
if (item[key] !== undefined)
|
|
211
|
-
initialVal = item[key];
|
|
212
|
-
});
|
|
213
|
-
return initialVal;
|
|
214
|
-
}
|
|
215
|
-
return originalStyle[key] === undefined ? InitialValue[key] : originalStyle[key];
|
|
216
|
-
}
|
|
217
|
-
// 循环 animation actions 获取所有有动画的 style prop name
|
|
218
|
-
function getAnimatedStyleKeys() {
|
|
219
|
-
return (animation?.actions || []).reduce((keyMap, action) => {
|
|
220
|
-
const { rules, transform } = action;
|
|
221
|
-
const ruleArr = [...rules.keys(), ...transform.keys()];
|
|
222
|
-
ruleArr.forEach(key => {
|
|
223
|
-
if (!keyMap[key])
|
|
224
|
-
keyMap[key] = true;
|
|
225
|
-
});
|
|
226
|
-
return keyMap;
|
|
227
|
-
}, animatedKeys.current);
|
|
228
|
-
}
|
|
229
|
-
// animated key transform 格式化
|
|
230
|
-
function formatAnimatedKeys(keys) {
|
|
231
|
-
const animatedKeys = [];
|
|
232
|
-
const transforms = [];
|
|
233
|
-
keys.forEach(key => {
|
|
234
|
-
if (isTransform(key)) {
|
|
235
|
-
transforms.push(key);
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
animatedKeys.push(key);
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
if (transforms.length)
|
|
242
|
-
animatedKeys.push(transforms);
|
|
243
|
-
return animatedKeys;
|
|
244
|
-
}
|
|
245
|
-
// 设置 lastShareValRef & shareValMap
|
|
246
|
-
function updateStyleVal() {
|
|
247
|
-
Object.entries(originalStyle).forEach(([key, value]) => {
|
|
248
|
-
if (key === 'transform') {
|
|
249
|
-
Object.entries(getTransformObj(value)).forEach(([key, value]) => {
|
|
250
|
-
if (value !== lastStyleRef.current[key]) {
|
|
251
|
-
lastStyleRef.current[key] = value;
|
|
252
|
-
shareValMap[key].value = value;
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
else if (hasOwn(shareValMap, key)) {
|
|
257
|
-
if (value !== lastStyleRef.current[key]) {
|
|
258
|
-
lastStyleRef.current[key] = value;
|
|
259
|
-
shareValMap[key].value = value;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
// ** 生成动画样式
|
|
265
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
266
|
-
const animationStyle = useAnimatedStyle(() => {
|
|
267
|
-
// console.info(`useAnimatedStyle styles=`, originalStyle)
|
|
268
|
-
return animatedStyleKeys.value.reduce((styles, key) => {
|
|
269
|
-
// console.info('getAnimationStyles', key, shareValMap[key].value)
|
|
270
|
-
if (Array.isArray(key)) {
|
|
271
|
-
const transformStyle = getTransformObj(originalStyle.transform || []);
|
|
272
|
-
key.forEach((transformKey) => {
|
|
273
|
-
transformStyle[transformKey] = shareValMap[transformKey].value;
|
|
274
|
-
});
|
|
275
|
-
styles.transform = Object.entries(transformStyle).map(([key, value]) => {
|
|
276
|
-
return { [key]: value };
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
styles[key] = shareValMap[key].value;
|
|
281
|
-
}
|
|
282
|
-
return styles;
|
|
283
|
-
}, {});
|
|
284
|
-
});
|
|
285
|
-
return {
|
|
286
|
-
enableStyleAnimation: enableAnimationRef.current,
|
|
287
|
-
animationStyle
|
|
288
|
-
};
|
|
289
|
-
}
|
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef } from 'react'
|
|
2
|
-
import type { MutableRefObject } from 'react'
|
|
3
|
-
import type { NativeSyntheticEvent, TransformsStyle } from 'react-native'
|
|
4
|
-
import {
|
|
5
|
-
Easing,
|
|
6
|
-
useSharedValue,
|
|
7
|
-
withTiming,
|
|
8
|
-
useAnimatedStyle,
|
|
9
|
-
withSequence,
|
|
10
|
-
withDelay,
|
|
11
|
-
makeMutable,
|
|
12
|
-
cancelAnimation,
|
|
13
|
-
runOnJS
|
|
14
|
-
} from 'react-native-reanimated'
|
|
15
|
-
import type { AnimationCallback, WithTimingConfig, SharedValue, AnimatableValue } from 'react-native-reanimated'
|
|
16
|
-
import { error, hasOwn, collectDataset } from '@mpxjs/utils'
|
|
17
|
-
import { useRunOnJSCallback } from './utils'
|
|
18
|
-
import { ExtendedViewStyle } from './types/common'
|
|
19
|
-
import type { _ViewProps } from './mpx-view'
|
|
20
|
-
|
|
21
|
-
type AnimatedOption = {
|
|
22
|
-
duration: number
|
|
23
|
-
delay: number
|
|
24
|
-
useNativeDriver: boolean
|
|
25
|
-
timingFunction: 'linear' | 'ease' | 'ease-in' | 'ease-in-out'| 'ease-out'
|
|
26
|
-
transformOrigin: string
|
|
27
|
-
}
|
|
28
|
-
type ExtendWithTimingConfig = WithTimingConfig & {
|
|
29
|
-
delay: number
|
|
30
|
-
}
|
|
31
|
-
export type AnimationStepItem = {
|
|
32
|
-
animatedOption: AnimatedOption
|
|
33
|
-
rules: Map<string, number | string>
|
|
34
|
-
transform: Map<string, number>
|
|
35
|
-
}
|
|
36
|
-
export type AnimationProp = {
|
|
37
|
-
id: number,
|
|
38
|
-
actions: AnimationStepItem[]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// 微信 timingFunction 和 RN Easing 对应关系
|
|
42
|
-
const EasingKey = {
|
|
43
|
-
linear: Easing.linear,
|
|
44
|
-
ease: Easing.inOut(Easing.ease),
|
|
45
|
-
'ease-in': Easing.in(Easing.poly(3)),
|
|
46
|
-
'ease-in-out': Easing.inOut(Easing.poly(3)),
|
|
47
|
-
'ease-out': Easing.out(Easing.poly(3))
|
|
48
|
-
// 'step-start': '',
|
|
49
|
-
// 'step-end': ''
|
|
50
|
-
}
|
|
51
|
-
const TransformInitial: ExtendedViewStyle = {
|
|
52
|
-
// matrix: 0,
|
|
53
|
-
// matrix3d: 0,
|
|
54
|
-
// rotate: '0deg',
|
|
55
|
-
rotateX: '0deg',
|
|
56
|
-
rotateY: '0deg',
|
|
57
|
-
rotateZ: '0deg',
|
|
58
|
-
// rotate3d:[0,0,0]
|
|
59
|
-
// scale: 1,
|
|
60
|
-
// scale3d: [1, 1, 1],
|
|
61
|
-
scaleX: 1,
|
|
62
|
-
scaleY: 1,
|
|
63
|
-
// scaleZ: 1,
|
|
64
|
-
// skew: 0,
|
|
65
|
-
skewX: '0deg',
|
|
66
|
-
skewY: '0deg',
|
|
67
|
-
// translate: 0,
|
|
68
|
-
// translate3d: 0,
|
|
69
|
-
translateX: 0,
|
|
70
|
-
translateY: 0
|
|
71
|
-
// translateZ: 0,
|
|
72
|
-
}
|
|
73
|
-
// 动画默认初始值
|
|
74
|
-
const InitialValue: ExtendedViewStyle = Object.assign({
|
|
75
|
-
opacity: 1,
|
|
76
|
-
backgroundColor: 'transparent',
|
|
77
|
-
width: 0,
|
|
78
|
-
height: 0,
|
|
79
|
-
top: 0,
|
|
80
|
-
right: 0,
|
|
81
|
-
bottom: 0,
|
|
82
|
-
left: 0,
|
|
83
|
-
transformOrigin: ['50%', '50%', 0]
|
|
84
|
-
}, TransformInitial)
|
|
85
|
-
const TransformOrigin = 'transformOrigin'
|
|
86
|
-
// transform
|
|
87
|
-
const isTransform = (key: string) => Object.keys(TransformInitial).includes(key)
|
|
88
|
-
|
|
89
|
-
// transform 数组转对象
|
|
90
|
-
function getTransformObj (transforms: { [propName: string]: string | number }[]) {
|
|
91
|
-
'worklet'
|
|
92
|
-
return transforms.reduce((transformObj, item) => {
|
|
93
|
-
return Object.assign(transformObj, item)
|
|
94
|
-
}, {} as { [propName: string]: string | number })
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export default function useAnimationHooks<T, P> (props: _ViewProps & { enableAnimation?: boolean, layoutRef: MutableRefObject<any>, transitionend?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void }) {
|
|
98
|
-
const { style: originalStyle = {}, animation, enableAnimation, transitionend, layoutRef } = props
|
|
99
|
-
const enableStyleAnimation = enableAnimation || !!animation
|
|
100
|
-
const enableAnimationRef = useRef(enableStyleAnimation)
|
|
101
|
-
if (enableAnimationRef.current !== enableStyleAnimation) {
|
|
102
|
-
error('[Mpx runtime error]: animation use should be stable in the component lifecycle, or you can set [enable-animation] with true.')
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (!enableAnimationRef.current) return { enableStyleAnimation: false }
|
|
106
|
-
|
|
107
|
-
// id 标识
|
|
108
|
-
const id = animation?.id || -1
|
|
109
|
-
// 有动画样式的 style key
|
|
110
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
111
|
-
const animatedStyleKeys = useSharedValue([] as (string|string[])[])
|
|
112
|
-
// 记录动画key的style样式值 没有的话设置为false
|
|
113
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
114
|
-
const animatedKeys = useRef({} as {[propName: keyof ExtendedViewStyle]: boolean})
|
|
115
|
-
// 记录上次style map
|
|
116
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
117
|
-
const lastStyleRef = useRef({} as {[propName: keyof ExtendedViewStyle]: number|string})
|
|
118
|
-
// ** 全量 style prop sharedValue
|
|
119
|
-
// 不能做增量的原因:
|
|
120
|
-
// 1 尝试用 useRef,但 useAnimatedStyle 访问后的 ref 不能在增加新的值,被冻结
|
|
121
|
-
// 2 尝试用 useSharedValue,因为实际触发的 style prop 需要是 sharedValue 才能驱动动画,若外层 shareValMap 也是 sharedValue,动画无法驱动。
|
|
122
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
123
|
-
const shareValMap = useMemo(() => {
|
|
124
|
-
return Object.keys(InitialValue).reduce((valMap, key) => {
|
|
125
|
-
const defaultVal = getInitialVal(key, isTransform(key))
|
|
126
|
-
valMap[key] = makeMutable(defaultVal)
|
|
127
|
-
return valMap
|
|
128
|
-
}, {} as { [propName: keyof ExtendedViewStyle]: SharedValue<string|number> })
|
|
129
|
-
}, [])
|
|
130
|
-
// ** style更新同步
|
|
131
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
132
|
-
useEffect(() => {
|
|
133
|
-
// style 更新后同步更新 lastStyleRef & shareValMap
|
|
134
|
-
updateStyleVal()
|
|
135
|
-
}, [originalStyle])
|
|
136
|
-
// ** 获取动画样式prop & 驱动动画
|
|
137
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
138
|
-
useEffect(() => {
|
|
139
|
-
if (id === -1) return
|
|
140
|
-
// 更新动画样式 key map
|
|
141
|
-
animatedKeys.current = getAnimatedStyleKeys()
|
|
142
|
-
const keys = Object.keys(animatedKeys.current)
|
|
143
|
-
animatedStyleKeys.value = formatAnimatedKeys([TransformOrigin, ...keys])
|
|
144
|
-
// 驱动动画
|
|
145
|
-
createAnimation(keys)
|
|
146
|
-
}, [id])
|
|
147
|
-
// ** 清空动画
|
|
148
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
return () => {
|
|
151
|
-
Object.values(shareValMap).forEach((value) => {
|
|
152
|
-
cancelAnimation(value)
|
|
153
|
-
})
|
|
154
|
-
}
|
|
155
|
-
}, [])
|
|
156
|
-
// 根据 animation action 创建&驱动动画
|
|
157
|
-
function createAnimation (animatedKeys: string[] = []) {
|
|
158
|
-
const actions = animation?.actions || []
|
|
159
|
-
const sequence = {} as { [propName: keyof ExtendedViewStyle]: (string|number)[] }
|
|
160
|
-
const lastValueMap = {} as { [propName: keyof ExtendedViewStyle]: string|number }
|
|
161
|
-
actions.forEach(({ animatedOption, rules, transform }, index) => {
|
|
162
|
-
const { delay, duration, timingFunction, transformOrigin } = animatedOption
|
|
163
|
-
const easing = EasingKey[timingFunction] || Easing.inOut(Easing.quad)
|
|
164
|
-
let needSetCallback = true
|
|
165
|
-
const setTransformOrigin: AnimationCallback = (finished: boolean) => {
|
|
166
|
-
'worklet'
|
|
167
|
-
// 动画结束后设置下一次transformOrigin
|
|
168
|
-
if (finished) {
|
|
169
|
-
if (index < actions.length - 1) {
|
|
170
|
-
const transformOrigin = actions[index + 1].animatedOption?.transformOrigin
|
|
171
|
-
transformOrigin && (shareValMap[TransformOrigin].value = transformOrigin)
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (index === 0) {
|
|
176
|
-
// 设置当次中心
|
|
177
|
-
shareValMap[TransformOrigin].value = transformOrigin
|
|
178
|
-
}
|
|
179
|
-
// 添加每个key的多次step动画
|
|
180
|
-
animatedKeys.forEach(key => {
|
|
181
|
-
const ruleV = isTransform(key) ? transform.get(key) : rules.get(key)
|
|
182
|
-
// key不存在,第一轮取shareValMap[key]value,非第一轮取上一轮的
|
|
183
|
-
const toVal = ruleV !== undefined
|
|
184
|
-
? ruleV
|
|
185
|
-
: index > 0
|
|
186
|
-
? lastValueMap[key]
|
|
187
|
-
: shareValMap[key].value
|
|
188
|
-
const animation = getAnimation({ key, value: toVal! }, { delay, duration, easing }, needSetCallback ? setTransformOrigin : undefined)
|
|
189
|
-
needSetCallback = false
|
|
190
|
-
if (!sequence[key]) {
|
|
191
|
-
sequence[key] = [animation]
|
|
192
|
-
} else {
|
|
193
|
-
sequence[key].push(animation)
|
|
194
|
-
}
|
|
195
|
-
// 更新一下 lastValueMap
|
|
196
|
-
lastValueMap[key] = toVal!
|
|
197
|
-
})
|
|
198
|
-
// 赋值驱动动画
|
|
199
|
-
animatedKeys.forEach((key) => {
|
|
200
|
-
const animations = sequence[key]
|
|
201
|
-
shareValMap[key].value = withSequence(...animations)
|
|
202
|
-
})
|
|
203
|
-
})
|
|
204
|
-
}
|
|
205
|
-
function withTimingCallback (finished?: boolean, current?: AnimatableValue, duration?: number) {
|
|
206
|
-
if (!transitionend) return
|
|
207
|
-
const target = {
|
|
208
|
-
id: animation?.id || -1,
|
|
209
|
-
dataset: collectDataset(props),
|
|
210
|
-
offsetLeft: layoutRef?.current?.offsetLeft || 0,
|
|
211
|
-
offsetTop: layoutRef?.current?.offsetTop || 0
|
|
212
|
-
}
|
|
213
|
-
transitionend({
|
|
214
|
-
type: 'transitionend',
|
|
215
|
-
// elapsedTime 对齐wx 单位s
|
|
216
|
-
detail: { elapsedTime: duration ? duration / 1000 : 0, finished, current },
|
|
217
|
-
target,
|
|
218
|
-
currentTarget: target,
|
|
219
|
-
timeStamp: Date.now()
|
|
220
|
-
})
|
|
221
|
-
}
|
|
222
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
223
|
-
const runOnJSCallbackRef = useRef({
|
|
224
|
-
withTimingCallback
|
|
225
|
-
})
|
|
226
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
227
|
-
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
|
|
228
|
-
// 创建单个animation
|
|
229
|
-
function getAnimation ({ key, value }: { key: string, value: string|number }, { delay, duration, easing }: ExtendWithTimingConfig, callback?: AnimationCallback) {
|
|
230
|
-
const animation = typeof callback === 'function'
|
|
231
|
-
? withTiming(value, { duration, easing }, (finished, current) => {
|
|
232
|
-
callback(finished, current)
|
|
233
|
-
if (transitionend && finished) {
|
|
234
|
-
runOnJS(runOnJSCallback)('withTimingCallback', finished, current, duration)
|
|
235
|
-
}
|
|
236
|
-
})
|
|
237
|
-
: withTiming(value, { duration, easing })
|
|
238
|
-
return delay ? withDelay(delay, animation) : animation
|
|
239
|
-
}
|
|
240
|
-
// 获取样式初始值(prop style or 默认值)
|
|
241
|
-
function getInitialVal (key: keyof ExtendedViewStyle, isTransform = false) {
|
|
242
|
-
if (isTransform && Array.isArray(originalStyle.transform)) {
|
|
243
|
-
let initialVal = InitialValue[key]
|
|
244
|
-
// 仅支持 { transform: [{rotateX: '45deg'}, {rotateZ: '0.785398rad'}] } 格式的初始样式
|
|
245
|
-
originalStyle.transform.forEach(item => {
|
|
246
|
-
if (item[key] !== undefined) initialVal = item[key]
|
|
247
|
-
})
|
|
248
|
-
return initialVal
|
|
249
|
-
}
|
|
250
|
-
return originalStyle[key] === undefined ? InitialValue[key] : originalStyle[key]
|
|
251
|
-
}
|
|
252
|
-
// 循环 animation actions 获取所有有动画的 style prop name
|
|
253
|
-
function getAnimatedStyleKeys () {
|
|
254
|
-
return (animation?.actions || []).reduce((keyMap, action) => {
|
|
255
|
-
const { rules, transform } = action
|
|
256
|
-
const ruleArr = [...rules.keys(), ...transform.keys()]
|
|
257
|
-
ruleArr.forEach(key => {
|
|
258
|
-
if (!keyMap[key]) keyMap[key] = true
|
|
259
|
-
})
|
|
260
|
-
return keyMap
|
|
261
|
-
}, animatedKeys.current)
|
|
262
|
-
}
|
|
263
|
-
// animated key transform 格式化
|
|
264
|
-
function formatAnimatedKeys (keys: string[]) {
|
|
265
|
-
const animatedKeys = [] as (string|string[])[]
|
|
266
|
-
const transforms = [] as string[]
|
|
267
|
-
keys.forEach(key => {
|
|
268
|
-
if (isTransform(key)) {
|
|
269
|
-
transforms.push(key)
|
|
270
|
-
} else {
|
|
271
|
-
animatedKeys.push(key)
|
|
272
|
-
}
|
|
273
|
-
})
|
|
274
|
-
if (transforms.length) animatedKeys.push(transforms)
|
|
275
|
-
return animatedKeys
|
|
276
|
-
}
|
|
277
|
-
// 设置 lastShareValRef & shareValMap
|
|
278
|
-
function updateStyleVal () {
|
|
279
|
-
Object.entries(originalStyle).forEach(([key, value]) => {
|
|
280
|
-
if (key === 'transform') {
|
|
281
|
-
Object.entries(getTransformObj(value)).forEach(([key, value]) => {
|
|
282
|
-
if (value !== lastStyleRef.current[key]) {
|
|
283
|
-
lastStyleRef.current[key] = value
|
|
284
|
-
shareValMap[key].value = value
|
|
285
|
-
}
|
|
286
|
-
})
|
|
287
|
-
} else if (hasOwn(shareValMap, key)) {
|
|
288
|
-
if (value !== lastStyleRef.current[key]) {
|
|
289
|
-
lastStyleRef.current[key] = value
|
|
290
|
-
shareValMap[key].value = value
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
})
|
|
294
|
-
}
|
|
295
|
-
// ** 生成动画样式
|
|
296
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
297
|
-
const animationStyle = useAnimatedStyle(() => {
|
|
298
|
-
// console.info(`useAnimatedStyle styles=`, originalStyle)
|
|
299
|
-
return animatedStyleKeys.value.reduce((styles, key) => {
|
|
300
|
-
// console.info('getAnimationStyles', key, shareValMap[key].value)
|
|
301
|
-
if (Array.isArray(key)) {
|
|
302
|
-
const transformStyle = getTransformObj(originalStyle.transform || [])
|
|
303
|
-
key.forEach((transformKey) => {
|
|
304
|
-
transformStyle[transformKey] = shareValMap[transformKey].value
|
|
305
|
-
})
|
|
306
|
-
styles.transform = Object.entries(transformStyle).map(([key, value]) => {
|
|
307
|
-
return { [key]: value }
|
|
308
|
-
}) as Extract<'transform', TransformsStyle>
|
|
309
|
-
} else {
|
|
310
|
-
styles[key] = shareValMap[key].value
|
|
311
|
-
}
|
|
312
|
-
return styles
|
|
313
|
-
}, {} as ExtendedViewStyle)
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
return {
|
|
317
|
-
enableStyleAnimation: enableAnimationRef.current,
|
|
318
|
-
animationStyle
|
|
319
|
-
}
|
|
320
|
-
}
|