@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.
Files changed (98) hide show
  1. package/lib/config.js +60 -0
  2. package/lib/file-loader.js +4 -1
  3. package/lib/global.d.ts +16 -0
  4. package/lib/index.js +22 -2
  5. package/lib/json-compiler/index.js +13 -4
  6. package/lib/platform/json/wx/index.js +6 -0
  7. package/lib/platform/style/wx/index.js +57 -33
  8. package/lib/platform/template/wx/component-config/ad.js +5 -0
  9. package/lib/platform/template/wx/component-config/button.js +9 -2
  10. package/lib/platform/template/wx/component-config/camera.js +25 -3
  11. package/lib/platform/template/wx/component-config/canvas.js +8 -1
  12. package/lib/platform/template/wx/component-config/cover-image.js +7 -2
  13. package/lib/platform/template/wx/component-config/cover-view.js +3 -1
  14. package/lib/platform/template/wx/component-config/form.js +27 -2
  15. package/lib/platform/template/wx/component-config/image.js +5 -0
  16. package/lib/platform/template/wx/component-config/input.js +10 -0
  17. package/lib/platform/template/wx/component-config/label.js +10 -2
  18. package/lib/platform/template/wx/component-config/map.js +11 -0
  19. package/lib/platform/template/wx/component-config/movable-area.js +4 -1
  20. package/lib/platform/template/wx/component-config/movable-view.js +17 -2
  21. package/lib/platform/template/wx/component-config/navigator.js +26 -0
  22. package/lib/platform/template/wx/component-config/picker-view.js +12 -0
  23. package/lib/platform/template/wx/component-config/picker.js +3 -1
  24. package/lib/platform/template/wx/component-config/progress.js +11 -1
  25. package/lib/platform/template/wx/component-config/rich-text.js +5 -0
  26. package/lib/platform/template/wx/component-config/scroll-view.js +12 -1
  27. package/lib/platform/template/wx/component-config/slider.js +8 -0
  28. package/lib/platform/template/wx/component-config/swiper-item.js +5 -2
  29. package/lib/platform/template/wx/component-config/swiper.js +10 -0
  30. package/lib/platform/template/wx/component-config/text.js +5 -0
  31. package/lib/platform/template/wx/component-config/textarea.js +19 -2
  32. package/lib/platform/template/wx/component-config/unsupported.js +10 -1
  33. package/lib/platform/template/wx/component-config/video.js +10 -0
  34. package/lib/platform/template/wx/index.js +21 -1
  35. package/lib/react/LoadAsyncChunkModule.js +1 -1
  36. package/lib/react/processStyles.js +21 -9
  37. package/lib/react/style-helper.js +76 -13
  38. package/lib/resolver/AddModePlugin.js +23 -8
  39. package/lib/runtime/components/react/animationHooks/index.ts +75 -0
  40. package/lib/runtime/components/react/animationHooks/useAnimationAPIHooks.ts +198 -0
  41. package/lib/runtime/components/react/animationHooks/useTransitionHooks.ts +297 -0
  42. package/lib/runtime/components/react/animationHooks/utils.ts +196 -0
  43. package/lib/runtime/components/react/context.ts +7 -1
  44. package/lib/runtime/components/react/dist/animationHooks/index.d.ts +16 -0
  45. package/lib/runtime/components/react/dist/animationHooks/index.d.ts.map +1 -0
  46. package/lib/runtime/components/react/dist/animationHooks/index.js +67 -0
  47. package/lib/runtime/components/react/dist/animationHooks/useAnimationAPIHooks.d.ts +4 -0
  48. package/lib/runtime/components/react/dist/animationHooks/useAnimationAPIHooks.d.ts.map +1 -0
  49. package/lib/runtime/components/react/dist/animationHooks/useAnimationAPIHooks.js +182 -0
  50. package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.d.ts +4 -0
  51. package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.d.ts.map +1 -0
  52. package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.js +274 -0
  53. package/lib/runtime/components/react/dist/animationHooks/utils.d.ts +110 -0
  54. package/lib/runtime/components/react/dist/animationHooks/utils.d.ts.map +1 -0
  55. package/lib/runtime/components/react/dist/animationHooks/utils.js +150 -0
  56. package/lib/runtime/components/react/dist/context.d.ts +6 -1
  57. package/lib/runtime/components/react/dist/context.d.ts.map +1 -1
  58. package/lib/runtime/components/react/dist/mpx-camera.d.ts +32 -0
  59. package/lib/runtime/components/react/dist/mpx-camera.d.ts.map +1 -0
  60. package/lib/runtime/components/react/dist/mpx-camera.jsx +236 -0
  61. package/lib/runtime/components/react/dist/mpx-input.d.ts +2 -0
  62. package/lib/runtime/components/react/dist/mpx-input.d.ts.map +1 -1
  63. package/lib/runtime/components/react/dist/mpx-input.jsx +21 -10
  64. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.d.ts.map +1 -1
  65. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +3 -0
  66. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +2 -2
  67. package/lib/runtime/components/react/dist/mpx-swiper.d.ts +10 -0
  68. package/lib/runtime/components/react/dist/mpx-swiper.d.ts.map +1 -1
  69. package/lib/runtime/components/react/dist/mpx-swiper.jsx +28 -16
  70. package/lib/runtime/components/react/dist/mpx-view.d.ts +3 -2
  71. package/lib/runtime/components/react/dist/mpx-view.d.ts.map +1 -1
  72. package/lib/runtime/components/react/dist/mpx-view.jsx +2 -2
  73. package/lib/runtime/components/react/dist/mpx-web-view.jsx +1 -1
  74. package/lib/runtime/components/react/dist/utils.d.ts +1 -0
  75. package/lib/runtime/components/react/dist/utils.d.ts.map +1 -1
  76. package/lib/runtime/components/react/dist/utils.jsx +34 -13
  77. package/lib/runtime/components/react/mpx-camera.tsx +327 -0
  78. package/lib/runtime/components/react/mpx-input.tsx +26 -10
  79. package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +3 -0
  80. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +2 -2
  81. package/lib/runtime/components/react/mpx-swiper.tsx +43 -15
  82. package/lib/runtime/components/react/mpx-view.tsx +4 -5
  83. package/lib/runtime/components/react/mpx-web-view.tsx +1 -1
  84. package/lib/runtime/components/react/types/global.d.ts +1 -0
  85. package/lib/runtime/components/react/utils.tsx +34 -16
  86. package/lib/runtime/optionProcessor.js +5 -0
  87. package/lib/runtime/optionProcessorReact.js +7 -0
  88. package/lib/runtime/stringify.wxs +2 -2
  89. package/lib/style-compiler/strip-conditional-loader/rebaseUrl.js +225 -0
  90. package/lib/style-compiler/strip-conditional-loader.js +55 -180
  91. package/lib/template-compiler/compiler.js +1 -3
  92. package/lib/utils/dom-tag-config.js +1 -1
  93. package/lib/utils/string.js +25 -1
  94. package/package.json +2 -1
  95. package/lib/runtime/components/react/dist/useAnimationHooks.d.ts +0 -33
  96. package/lib/runtime/components/react/dist/useAnimationHooks.d.ts.map +0 -1
  97. package/lib/runtime/components/react/dist/useAnimationHooks.js +0 -289
  98. 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
- }