@mpxjs/webpack-plugin 2.10.1 → 2.10.3

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 (41) hide show
  1. package/lib/dependencies/RecordPageConfigsMapDependency.js +45 -0
  2. package/lib/index.js +23 -1
  3. package/lib/platform/style/wx/index.js +6 -4
  4. package/lib/platform/template/wx/component-config/input.js +1 -1
  5. package/lib/platform/template/wx/component-config/textarea.js +1 -1
  6. package/lib/platform/template/wx/component-config/view.js +12 -2
  7. package/lib/react/index.js +0 -1
  8. package/lib/react/processJSON.js +13 -2
  9. package/lib/react/processScript.js +7 -4
  10. package/lib/react/processTemplate.js +18 -3
  11. package/lib/react/script-helper.js +18 -4
  12. package/lib/runtime/components/react/context.ts +3 -4
  13. package/lib/runtime/components/react/dist/mpx-image.jsx +2 -2
  14. package/lib/runtime/components/react/dist/mpx-input.jsx +54 -54
  15. package/lib/runtime/components/react/dist/{KeyboardAvoidingView.jsx → mpx-keyboard-avoiding-view.jsx} +23 -12
  16. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +1 -2
  17. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +16 -8
  18. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +22 -0
  19. package/lib/runtime/components/react/dist/mpx-textarea.jsx +6 -6
  20. package/lib/runtime/components/react/dist/mpx-view.jsx +10 -5
  21. package/lib/runtime/components/react/dist/mpx-web-view.jsx +6 -5
  22. package/lib/runtime/components/react/dist/useAnimationHooks.js +46 -48
  23. package/lib/runtime/components/react/dist/utils.jsx +17 -21
  24. package/lib/runtime/components/react/mpx-image.tsx +2 -2
  25. package/lib/runtime/components/react/mpx-input.tsx +66 -72
  26. package/lib/runtime/components/react/{KeyboardAvoidingView.tsx → mpx-keyboard-avoiding-view.tsx} +32 -18
  27. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +1 -2
  28. package/lib/runtime/components/react/mpx-scroll-view.tsx +21 -8
  29. package/lib/runtime/components/react/mpx-simple-view.tsx +32 -0
  30. package/lib/runtime/components/react/mpx-textarea.tsx +10 -6
  31. package/lib/runtime/components/react/mpx-view.tsx +17 -10
  32. package/lib/runtime/components/react/mpx-web-view.tsx +12 -10
  33. package/lib/runtime/components/react/types/getInnerListeners.d.ts +1 -1
  34. package/lib/runtime/components/react/useAnimationHooks.ts +46 -48
  35. package/lib/runtime/components/react/utils.tsx +21 -24
  36. package/lib/runtime/optionProcessor.js +3 -2
  37. package/lib/style-compiler/index.js +8 -6
  38. package/lib/template-compiler/compiler.js +22 -14
  39. package/lib/utils/match-condition.js +14 -8
  40. package/lib/web/processJSON.js +1 -3
  41. package/package.json +4 -4
@@ -1,8 +1,8 @@
1
- import React, { useContext, useEffect } from 'react';
1
+ import React, { useContext, useEffect, useMemo } from 'react';
2
2
  import { Keyboard, Platform, View } from 'react-native';
3
3
  import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
4
+ import { GestureDetector, Gesture } from 'react-native-gesture-handler';
4
5
  import { KeyboardAvoidContext } from './context';
5
- import { extendObject } from './utils';
6
6
  const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
7
7
  const isIOS = Platform.OS === 'ios';
8
8
  const duration = isIOS ? 250 : 300;
@@ -10,16 +10,24 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
10
10
  const offset = useSharedValue(0);
11
11
  const basic = useSharedValue('auto');
12
12
  const keyboardAvoid = useContext(KeyboardAvoidContext);
13
+ const dismiss = () => {
14
+ Keyboard.isVisible() && Keyboard.dismiss();
15
+ };
16
+ const gesture = useMemo(() => {
17
+ return Gesture.Tap()
18
+ .onEnd(() => {
19
+ dismiss();
20
+ }).runOnJS(true);
21
+ }, []);
13
22
  const animatedStyle = useAnimatedStyle(() => {
14
23
  return Object.assign({
15
24
  transform: [{ translateY: -offset.value }]
16
25
  }, isIOS ? {} : { flexBasis: basic.value });
17
26
  });
18
27
  const resetKeyboard = () => {
19
- keyboardAvoid?.current && extendObject(keyboardAvoid.current, {
20
- cursorSpacing: 0,
21
- ref: null
22
- });
28
+ if (keyboardAvoid?.current) {
29
+ keyboardAvoid.current = null;
30
+ }
23
31
  offset.value = withTiming(0, { duration, easing });
24
32
  basic.value = 'auto';
25
33
  };
@@ -34,7 +42,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
34
42
  const { ref, cursorSpacing = 0 } = keyboardAvoid.current;
35
43
  setTimeout(() => {
36
44
  ref?.current?.measure((x, y, width, height, pageX, pageY) => {
37
- const aboveOffset = offset.value + pageY + height - endCoordinates.screenY;
45
+ const aboveOffset = pageY + height - endCoordinates.screenY;
38
46
  const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
39
47
  const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing);
40
48
  const value = aboveOffset > 0 ? belowValue : aboveValue;
@@ -77,13 +85,16 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
77
85
  subscriptions.forEach(subscription => subscription.remove());
78
86
  };
79
87
  }, [keyboardAvoid]);
80
- return (<View style={style}>
81
- <Animated.View style={[
88
+ return (<GestureDetector gesture={gesture}>
89
+ <View style={style}>
90
+ <Animated.View style={[
82
91
  contentContainerStyle,
83
92
  animatedStyle
84
93
  ]}>
85
- {children}
86
- </Animated.View>
87
- </View>);
94
+ {children}
95
+ </Animated.View>
96
+ </View>
97
+ </GestureDetector>);
88
98
  };
99
+ KeyboardAvoidingView.displayName = 'MpxKeyboardAvoidingView';
89
100
  export default KeyboardAvoidingView;
@@ -1,6 +1,5 @@
1
1
  import { useState, useCallback, forwardRef, useImperativeHandle } from 'react';
2
2
  import { View, StyleSheet } from 'react-native';
3
- import { extendObject } from '../utils';
4
3
  const _PortalManager = forwardRef((props, ref) => {
5
4
  const [state, setState] = useState({
6
5
  portals: []
@@ -14,7 +13,7 @@ const _PortalManager = forwardRef((props, ref) => {
14
13
  setState((prevState) => ({
15
14
  portals: prevState.portals.map((item) => {
16
15
  if (item.key === key) {
17
- return extendObject({}, item, { children });
16
+ return Object.assign({}, item, { children });
18
17
  }
19
18
  return item;
20
19
  })
@@ -41,7 +41,7 @@ import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, ext
41
41
  import { IntersectionObserverContext, ScrollViewContext } from './context';
42
42
  const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
43
43
  const { textProps, innerProps: props = {} } = splitProps(scrollViewProps);
44
- const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, __selectRef } = props;
44
+ const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, 'scroll-event-throttle': scrollEventThrottle = 0, __selectRef } = props;
45
45
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
46
46
  const waitForHandlers = flatGesture(waitFor);
47
47
  const [refreshing, setRefreshing] = useState(true);
@@ -54,7 +54,6 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
54
54
  scrollTop: 0,
55
55
  visibleLength: 0
56
56
  });
57
- const scrollEventThrottle = 50;
58
57
  const hasCallScrollToUpper = useRef(true);
59
58
  const hasCallScrollToLower = useRef(false);
60
59
  const initialTimeout = useRef(null);
@@ -73,7 +72,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
73
72
  pagingEnabled,
74
73
  fastDeceleration: false,
75
74
  decelerationDisabled: false,
76
- scrollTo: scrollToOffset
75
+ scrollTo
77
76
  },
78
77
  gestureRef: scrollViewRef
79
78
  });
@@ -83,6 +82,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
83
82
  };
84
83
  }, []);
85
84
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
85
+ const lastOffset = useRef(0);
86
86
  if (scrollX && scrollY) {
87
87
  warn('scroll-x and scroll-y cannot be set to true at the same time, Mpx will use the value of scroll-y as the criterion');
88
88
  }
@@ -112,6 +112,9 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
112
112
  }
113
113
  firstScrollIntoViewChange.current = true;
114
114
  }, [scrollIntoView]);
115
+ function scrollTo({ top = 0, left = 0, animated = false }) {
116
+ scrollToOffset(left, top, animated);
117
+ }
115
118
  function handleScrollIntoView() {
116
119
  const refs = __selectRef(`#${scrollIntoView}`, 'node');
117
120
  if (!refs)
@@ -130,7 +133,8 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
130
133
  function onStartReached(e) {
131
134
  const { bindscrolltoupper } = props;
132
135
  const { offset } = scrollOptions.current;
133
- if (bindscrolltoupper && (offset <= upperThreshold)) {
136
+ const isScrollingBackward = offset < lastOffset.current;
137
+ if (bindscrolltoupper && (offset <= upperThreshold) && isScrollingBackward) {
134
138
  if (!hasCallScrollToUpper.current) {
135
139
  bindscrolltoupper(getCustomEvent('scrolltoupper', e, {
136
140
  detail: {
@@ -149,12 +153,13 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
149
153
  const { bindscrolltolower } = props;
150
154
  const { contentLength, visibleLength, offset } = scrollOptions.current;
151
155
  const distanceFromEnd = contentLength - visibleLength - offset;
152
- if (bindscrolltolower && (distanceFromEnd < lowerThreshold)) {
156
+ const isScrollingForward = offset > lastOffset.current;
157
+ if (bindscrolltolower && (distanceFromEnd < lowerThreshold) && isScrollingForward) {
153
158
  if (!hasCallScrollToLower.current) {
154
159
  hasCallScrollToLower.current = true;
155
160
  bindscrolltolower(getCustomEvent('scrolltolower', e, {
156
161
  detail: {
157
- direction: scrollX ? 'right' : 'botttom'
162
+ direction: scrollX ? 'right' : 'bottom'
158
163
  },
159
164
  layoutRef
160
165
  }, props));
@@ -203,6 +208,8 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
203
208
  onStartReached(e);
204
209
  onEndReached(e);
205
210
  updateIntersection();
211
+ // 在 onStartReached、onEndReached 执行完后更新 lastOffset
212
+ lastOffset.current = scrollOptions.current.offset;
206
213
  }
207
214
  function onScrollEnd(e) {
208
215
  const { bindscrollend } = props;
@@ -222,6 +229,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
222
229
  onStartReached(e);
223
230
  onEndReached(e);
224
231
  updateIntersection();
232
+ lastOffset.current = scrollOptions.current.offset;
225
233
  }
226
234
  function updateIntersection() {
227
235
  if (enableTriggerIntersectionObserver && intersectionObservers) {
@@ -230,9 +238,9 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
230
238
  }
231
239
  }
232
240
  }
233
- function scrollToOffset(x = 0, y = 0) {
241
+ function scrollToOffset(x = 0, y = 0, animated = scrollWithAnimation) {
234
242
  if (scrollViewRef.current) {
235
- scrollViewRef.current.scrollTo({ x, y, animated: !!scrollWithAnimation });
243
+ scrollViewRef.current.scrollTo({ x, y, animated });
236
244
  scrollOptions.current.scrollLeft = x;
237
245
  scrollOptions.current.scrollTop = y;
238
246
  snapScrollLeft.current = x;
@@ -0,0 +1,22 @@
1
+ import { View } from 'react-native';
2
+ import { createElement, forwardRef, useRef } from 'react';
3
+ import useNodesRef from './useNodesRef';
4
+ import { extendObject, splitProps, splitStyle, wrapChildren } from './utils';
5
+ const _View2 = forwardRef((simpleViewProps, ref) => {
6
+ const nodeRef = useRef(null);
7
+ const { textProps, innerProps: props = {} } = splitProps(simpleViewProps);
8
+ const { textStyle, innerStyle = {} } = splitStyle(props.style || {});
9
+ useNodesRef(props, ref, nodeRef, {
10
+ style: innerStyle || {}
11
+ });
12
+ return createElement(View, extendObject({}, props, {
13
+ style: innerStyle,
14
+ ref: nodeRef
15
+ }), wrapChildren(props, {
16
+ hasVarDec: false,
17
+ textStyle: textStyle,
18
+ textProps
19
+ }));
20
+ });
21
+ _View2.displayName = 'MpxSimpleView';
22
+ export default _View2;
@@ -3,33 +3,33 @@
3
3
  * Subtraction:
4
4
  * type, password, confirm-hold
5
5
  * Addition:
6
- * - confirm-type: Not support `return`
6
+ * confirm-type
7
7
  * ✔ auto-height
8
8
  * ✘ fixed
9
9
  * ✘ show-confirm-bar
10
10
  * ✔ bindlinechange: No `heightRpx` info.
11
11
  */
12
12
  import { forwardRef, createElement } from 'react';
13
- import { Keyboard } from 'react-native';
14
13
  import Input from './mpx-input';
15
14
  import { omit, extendObject } from './utils';
16
15
  const DEFAULT_TEXTAREA_WIDTH = 300;
17
16
  const DEFAULT_TEXTAREA_HEIGHT = 150;
18
17
  const Textarea = forwardRef((props, ref) => {
19
- const { style = {} } = props;
18
+ const { style = {}, 'confirm-type': confirmType = 'return' } = props;
20
19
  const restProps = omit(props, [
21
20
  'ref',
22
21
  'type',
23
22
  'style',
24
23
  'password',
25
24
  'multiline',
25
+ 'confirm-type',
26
26
  'confirm-hold'
27
27
  ]);
28
28
  return createElement(Input, extendObject(restProps, {
29
- ref: ref,
29
+ ref,
30
+ confirmType,
30
31
  multiline: true,
31
- confirmType: 'next',
32
- bindblur: () => Keyboard.dismiss(),
32
+ 'confirm-type': confirmType,
33
33
  style: extendObject({
34
34
  width: DEFAULT_TEXTAREA_WIDTH,
35
35
  height: DEFAULT_TEXTAREA_HEIGHT
@@ -14,6 +14,7 @@ import { parseUrl, PERCENT_REGEX, splitStyle, splitProps, useTransformStyle, wra
14
14
  import { error } from '@mpxjs/utils';
15
15
  import LinearGradient from 'react-native-linear-gradient';
16
16
  import { GestureDetector } from 'react-native-gesture-handler';
17
+ import Portal from './mpx-portal';
17
18
  const linearMap = new Map([
18
19
  ['top', 0],
19
20
  ['bottom', 180],
@@ -555,7 +556,7 @@ const _View = forwardRef((viewProps, ref) => {
555
556
  const enableHover = !!hoverStyle;
556
557
  const { isHover, gesture } = useHover({ enableHover, hoverStartTime, hoverStayTime });
557
558
  const styleObj = extendObject({}, defaultStyle, style, isHover ? hoverStyle : {});
558
- const { normalStyle, hasSelfPercent, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, {
559
+ const { normalStyle, hasSelfPercent, hasPositionFixed, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, {
559
560
  enableVar,
560
561
  externalVarContext,
561
562
  parentFontSize,
@@ -600,12 +601,16 @@ const _View = forwardRef((viewProps, ref) => {
600
601
  innerStyle,
601
602
  enableFastImage
602
603
  });
603
- const BaseComponent = enableStyleAnimation
604
+ let finalComponent = enableStyleAnimation
604
605
  ? createElement(Animated.View, innerProps, childNode)
605
606
  : createElement(View, innerProps, childNode);
606
- return enableHover
607
- ? createElement(GestureDetector, { gesture: gesture }, BaseComponent)
608
- : BaseComponent;
607
+ if (enableHover) {
608
+ finalComponent = createElement(GestureDetector, { gesture: gesture }, finalComponent);
609
+ }
610
+ if (hasPositionFixed) {
611
+ finalComponent = createElement(Portal, null, finalComponent);
612
+ }
613
+ return finalComponent;
609
614
  });
610
615
  _View.displayName = 'MpxView';
611
616
  export default _View;
@@ -45,7 +45,7 @@ const _WebView = forwardRef((props, ref) => {
45
45
  button: 'Reload'
46
46
  }
47
47
  };
48
- const currentErrorText = errorText[mpx.i18n.locale || 'zh-CN'];
48
+ const currentErrorText = errorText[mpx.i18n?.locale || 'zh-CN'];
49
49
  if (props.style) {
50
50
  warn('The web-view component does not support the style prop.');
51
51
  }
@@ -155,8 +155,8 @@ const _WebView = forwardRef((props, ref) => {
155
155
  switch (type) {
156
156
  case 'setTitle':
157
157
  { // case下不允许直接声明,包个块解决该问题
158
- const title = postData._documentTitle;
159
- if (title) {
158
+ const title = postData._documentTitle?.trim();
159
+ if (title !== undefined) {
160
160
  navigation && navigation.setOptions({ title });
161
161
  }
162
162
  }
@@ -252,6 +252,7 @@ const _WebView = forwardRef((props, ref) => {
252
252
  };
253
253
  const onLoadEnd = function (res) {
254
254
  if (__mpx_mode__ === 'android') {
255
+ res.persist();
255
256
  setTimeout(() => {
256
257
  onLoadEndHandle(res);
257
258
  }, 0);
@@ -276,13 +277,13 @@ const _WebView = forwardRef((props, ref) => {
276
277
  setIsLoaded(false);
277
278
  }
278
279
  };
279
- return (<Portal key={pageLoadErr ? 'error' : 'webview'}>
280
+ return (<Portal>
280
281
  {pageLoadErr
281
282
  ? (<View style={[styles.loadErrorContext, defaultWebViewStyle]}>
282
283
  <View style={styles.loadErrorText}><Text style={{ fontSize: 14, color: '#999999' }}>{currentErrorText.text}</Text></View>
283
284
  <View style={styles.loadErrorButton} onTouchEnd={_reload}><Text style={{ fontSize: 12, color: '#666666' }}>{currentErrorText.button}</Text></View>
284
285
  </View>)
285
- : (<WebView style={defaultWebViewStyle} source={{ uri: src }} ref={webViewRef} javaScriptEnabled={true} onNavigationStateChange={_changeUrl} onMessage={_message} injectedJavaScript={injectedJavaScript} onLoadProgress={_onLoadProgress} onLoadEnd={onLoadEnd} onHttpError={onHttpError} onError={onError} onLoadStart={onLoadStart} allowsBackForwardNavigationGestures={isLoaded}></WebView>)}
286
+ : (<WebView style={defaultWebViewStyle} pointerEvents={isLoaded ? 'auto' : 'none'} source={{ uri: src }} ref={webViewRef} javaScriptEnabled={true} onNavigationStateChange={_changeUrl} onMessage={_message} injectedJavaScript={injectedJavaScript} onLoadProgress={_onLoadProgress} onLoadEnd={onLoadEnd} onHttpError={onHttpError} onError={onError} onLoadStart={onLoadStart} allowsBackForwardNavigationGestures={true}></WebView>)}
286
287
  </Portal>);
287
288
  });
288
289
  _WebView.displayName = 'MpxWebview';
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useMemo, useRef } from 'react';
2
2
  import { Easing, useSharedValue, withTiming, useAnimatedStyle, withSequence, withDelay, makeMutable, cancelAnimation } from 'react-native-reanimated';
3
- import { error } from '@mpxjs/utils';
3
+ import { error, hasOwn } from '@mpxjs/utils';
4
4
  // 微信 timingFunction 和 RN Easing 对应关系
5
5
  const EasingKey = {
6
6
  linear: Easing.linear,
@@ -14,20 +14,20 @@ const EasingKey = {
14
14
  const TransformInitial = {
15
15
  // matrix: 0,
16
16
  // matrix3d: 0,
17
- rotate: '0deg',
17
+ // rotate: '0deg',
18
18
  rotateX: '0deg',
19
19
  rotateY: '0deg',
20
20
  rotateZ: '0deg',
21
21
  // rotate3d:[0,0,0]
22
- scale: 1,
22
+ // scale: 1,
23
23
  // scale3d: [1, 1, 1],
24
24
  scaleX: 1,
25
25
  scaleY: 1,
26
26
  // scaleZ: 1,
27
- skew: 0,
27
+ // skew: 0,
28
28
  skewX: '0deg',
29
29
  skewY: '0deg',
30
- translate: 0,
30
+ // translate: 0,
31
31
  // translate3d: 0,
32
32
  translateX: 0,
33
33
  translateY: 0
@@ -92,23 +92,23 @@ const parseTransform = (transformStr) => {
92
92
  case 'skewX':
93
93
  case 'skewY':
94
94
  case 'perspective':
95
+ // rotate 处理成 rotateZ
96
+ key = key === 'rotate' ? 'rotateZ' : key;
95
97
  // 单个值处理
96
98
  transform.push({ [key]: global.__formatValue(val) });
97
99
  break;
98
100
  case 'matrix':
99
- case 'matrix3d':
100
101
  transform.push({ [key]: parseValues(val, ',').map(val => +val) });
101
102
  break;
102
103
  case 'translate':
103
104
  case 'scale':
104
105
  case 'skew':
105
- case 'rotate3d': // x y z angle
106
106
  case 'translate3d': // x y 支持 z不支持
107
107
  case 'scale3d': // x y 支持 z不支持
108
108
  {
109
109
  // 2 个以上的值处理
110
110
  key = key.replace('3d', '');
111
- const vals = parseValues(val, ',').splice(0, key === 'rotate' ? 4 : 3);
111
+ const vals = parseValues(val, ',').splice(0, 3);
112
112
  // scale(.5) === scaleX(.5) scaleY(.5)
113
113
  if (vals.length === 1 && key === 'scale') {
114
114
  vals.push(vals[0]);
@@ -132,6 +132,13 @@ const formatStyle = (style) => {
132
132
  transform: parseTransform(style.transform)
133
133
  });
134
134
  };
135
+ // transform 数组转对象
136
+ function getTransformObj(transforms) {
137
+ 'worklet';
138
+ return transforms.reduce((transformObj, item) => {
139
+ return Object.assign(transformObj, item);
140
+ }, {});
141
+ }
135
142
  export default function useAnimationHooks(props) {
136
143
  const { style = {}, animation, enableAnimation } = props;
137
144
  const enableStyleAnimation = enableAnimation || !!animation;
@@ -150,7 +157,9 @@ export default function useAnimationHooks(props) {
150
157
  // 记录动画key的style样式值 没有的话设置为false
151
158
  // eslint-disable-next-line react-hooks/rules-of-hooks
152
159
  const animatedKeys = useRef({});
153
- // const animatedKeys = useRef({} as {[propName: keyof ExtendedViewStyle]: boolean|number|string})
160
+ // 记录上次style map
161
+ // eslint-disable-next-line react-hooks/rules-of-hooks
162
+ const lastStyleRef = useRef({});
154
163
  // ** 全量 style prop sharedValue
155
164
  // 不能做增量的原因:
156
165
  // 1 尝试用 useRef,但 useAnimatedStyle 访问后的 ref 不能在增加新的值,被冻结
@@ -163,6 +172,12 @@ export default function useAnimationHooks(props) {
163
172
  return valMap;
164
173
  }, {});
165
174
  }, []);
175
+ // ** style更新同步
176
+ // eslint-disable-next-line react-hooks/rules-of-hooks
177
+ useEffect(() => {
178
+ // style 更新后同步更新 lastStyleRef & shareValMap
179
+ updateStyleVal();
180
+ }, [style]);
166
181
  // ** 获取动画样式prop & 驱动动画
167
182
  // eslint-disable-next-line react-hooks/rules-of-hooks
168
183
  useEffect(() => {
@@ -175,16 +190,6 @@ export default function useAnimationHooks(props) {
175
190
  // 驱动动画
176
191
  createAnimation(keys);
177
192
  }, [id]);
178
- // 同步style更新
179
- // useEffect(() => {
180
- // Object.keys(animatedKeys.current).forEach(key => {
181
- // const originVal = getOriginalStyleVal(key, isTransform(key))
182
- // if (originVal && animatedKeys.current[key] !== originVal) {
183
- // animatedKeys.current[key] = originVal
184
- // shareValMap[key].value = originVal
185
- // }
186
- // })
187
- // }, [style])
188
193
  // ** 清空动画
189
194
  // eslint-disable-next-line react-hooks/rules-of-hooks
190
195
  useEffect(() => {
@@ -194,7 +199,7 @@ export default function useAnimationHooks(props) {
194
199
  });
195
200
  };
196
201
  }, []);
197
- // 根据 animation action 创建&驱动动画 key => wi
202
+ // 根据 animation action 创建&驱动动画
198
203
  function createAnimation(animatedKeys = []) {
199
204
  const actions = animation?.actions || [];
200
205
  const sequence = {};
@@ -251,6 +256,7 @@ export default function useAnimationHooks(props) {
251
256
  : withTiming(value, { duration, easing });
252
257
  return delay ? withDelay(delay, animation) : animation;
253
258
  }
259
+ // 获取样式初始值(prop style or 默认值)
254
260
  function getInitialVal(key, isTransform = false) {
255
261
  if (isTransform && Array.isArray(originalStyle.transform)) {
256
262
  let initialVal = InitialValue[key];
@@ -263,31 +269,12 @@ export default function useAnimationHooks(props) {
263
269
  }
264
270
  return originalStyle[key] === undefined ? InitialValue[key] : originalStyle[key];
265
271
  }
266
- // 从 prop style 中获取样式初始值 没有为undefined
267
- // function getOriginalStyleVal (key: keyof ExtendedViewStyle, isTransform = false) {
268
- // if (isTransform && Array.isArray(originalStyle.transform)) {
269
- // let initialVal = undefined // InitialValue[key]
270
- // // 仅支持 { transform: [{rotateX: '45deg'}, {rotateZ: '0.785398rad'}] } 格式的初始样式
271
- // originalStyle.transform.forEach(item => {
272
- // if (item[key] !== undefined) initialVal = item[key]
273
- // })
274
- // return initialVal
275
- // }
276
- // return originalStyle[key] // === undefined ? InitialValue[key] : originalStyle[key]
277
- // }
278
- // 获取动画shareVal初始值(prop style or 默认值)
279
- // function getInitialVal (key: keyof ExtendedViewStyle, isTransform = false) {
280
- // const originalVal = getOriginalStyleVal(key, isTransform)
281
- // return originalVal === undefined ? InitialValue[key] : originalStyle[key]
282
- // }
283
272
  // 循环 animation actions 获取所有有动画的 style prop name
284
273
  function getAnimatedStyleKeys() {
285
274
  return (animation?.actions || []).reduce((keyMap, action) => {
286
275
  const { rules, transform } = action;
287
276
  const ruleArr = [...rules.keys(), ...transform.keys()];
288
277
  ruleArr.forEach(key => {
289
- // const originalVal = getOriginalStyleVal(key, isTransform(key))
290
- // if (!keyMap[key]) keyMap[key] = originalVal === undefined ? false : originalVal
291
278
  if (!keyMap[key])
292
279
  keyMap[key] = true;
293
280
  });
@@ -295,7 +282,7 @@ export default function useAnimationHooks(props) {
295
282
  }, animatedKeys.current);
296
283
  }
297
284
  // animated key transform 格式化
298
- function formatAnimatedKeys(keys = []) {
285
+ function formatAnimatedKeys(keys) {
299
286
  const animatedKeys = [];
300
287
  const transforms = [];
301
288
  keys.forEach(key => {
@@ -310,13 +297,24 @@ export default function useAnimationHooks(props) {
310
297
  animatedKeys.push(transforms);
311
298
  return animatedKeys;
312
299
  }
313
- // transform 数组转对象
314
- function getTransformObj() {
315
- 'worklet';
316
- const transforms = originalStyle.transform || [];
317
- return transforms.reduce((transformObj, item) => {
318
- return Object.assign(transformObj, item);
319
- }, {});
300
+ // 设置 lastShareValRef & shareValMap
301
+ function updateStyleVal() {
302
+ Object.entries(originalStyle).forEach(([key, value]) => {
303
+ if (key === 'transform') {
304
+ Object.entries(getTransformObj(value)).forEach(([key, value]) => {
305
+ if (value !== lastStyleRef.current[key]) {
306
+ lastStyleRef.current[key] = value;
307
+ shareValMap[key].value = value;
308
+ }
309
+ });
310
+ }
311
+ else if (hasOwn(shareValMap, key)) {
312
+ if (value !== lastStyleRef.current[key]) {
313
+ lastStyleRef.current[key] = value;
314
+ shareValMap[key].value = value;
315
+ }
316
+ }
317
+ });
320
318
  }
321
319
  // ** 生成动画样式
322
320
  // eslint-disable-next-line react-hooks/rules-of-hooks
@@ -325,7 +323,7 @@ export default function useAnimationHooks(props) {
325
323
  return animatedStyleKeys.value.reduce((styles, key) => {
326
324
  // console.info('getAnimationStyles', key, shareValMap[key].value)
327
325
  if (Array.isArray(key)) {
328
- const transformStyle = getTransformObj();
326
+ const transformStyle = getTransformObj(originalStyle.transform || []);
329
327
  key.forEach((transformKey) => {
330
328
  transformStyle[transformKey] = shareValMap[transformKey].value;
331
329
  });
@@ -67,20 +67,6 @@ export const useUpdateEffect = (effect, deps) => {
67
67
  }
68
68
  }, deps);
69
69
  };
70
- /**
71
- * 解析行内样式
72
- * @param inlineStyle
73
- * @returns
74
- */
75
- export const parseInlineStyle = (inlineStyle = '') => {
76
- return inlineStyle.split(';').reduce((styleObj, style) => {
77
- const [k, v, ...rest] = style.split(':');
78
- if (rest.length || !v || !k)
79
- return styleObj;
80
- const key = k.trim().replace(/-./g, c => c.substring(1).toUpperCase());
81
- return extendObject(styleObj, { [key]: global.__formatValue(v.trim()) });
82
- }, {});
83
- };
84
70
  export const parseUrl = (cssUrl = '') => {
85
71
  if (!cssUrl)
86
72
  return;
@@ -253,12 +239,8 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
253
239
  const normalStyleChangedRef = useRef(false);
254
240
  let hasVarDec = false;
255
241
  let hasVarUse = false;
256
- let hasSelfPercent = false;
257
242
  const varKeyPaths = [];
258
243
  const unoVarKeyPaths = [];
259
- const percentKeyPaths = [];
260
- const calcKeyPaths = [];
261
- const envKeyPaths = [];
262
244
  const [width, setWidth] = useState(0);
263
245
  const [height, setHeight] = useState(0);
264
246
  const navigation = useNavigation();
@@ -318,6 +300,11 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
318
300
  normalStyleChangedRef.current = !normalStyleChangedRef.current;
319
301
  }
320
302
  const memoResult = useMemo(() => {
303
+ let hasSelfPercent = false;
304
+ let hasPositionFixed = false;
305
+ const percentKeyPaths = [];
306
+ const calcKeyPaths = [];
307
+ const envKeyPaths = [];
321
308
  // transform can be memoized
322
309
  function envVisitor({ value, keyPath }) {
323
310
  if (envUseRegExp.test(value)) {
@@ -338,6 +325,12 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
338
325
  percentKeyPaths.push(keyPath.slice());
339
326
  }
340
327
  }
328
+ function transformPosition(styleObj) {
329
+ if (styleObj.position === 'fixed') {
330
+ hasPositionFixed = true;
331
+ styleObj.position = 'absolute';
332
+ }
333
+ }
341
334
  // traverse env & calc & percent
342
335
  traverseStyle(normalStyle, [envVisitor, percentVisitor, calcVisitor]);
343
336
  const percentConfig = {
@@ -369,11 +362,14 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
369
362
  }
370
363
  }
371
364
  });
365
+ // apply position
366
+ transformPosition(normalStyle);
372
367
  // transform number enum stringify
373
368
  transformStringify(normalStyle);
374
369
  return {
375
370
  normalStyle,
376
- hasSelfPercent
371
+ hasSelfPercent,
372
+ hasPositionFixed
377
373
  };
378
374
  }, [normalStyleChangedRef.current, width, height, parentWidth, parentHeight, parentFontSize]);
379
375
  return extendObject({
@@ -448,8 +444,8 @@ export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout
448
444
  hasLayoutRef.current = true;
449
445
  if (hasSelfPercent) {
450
446
  const { width, height } = e?.nativeEvent?.layout || {};
451
- setWidth(width || 0);
452
- setHeight(height || 0);
447
+ setWidth && setWidth(width || 0);
448
+ setHeight && setHeight(height || 0);
453
449
  }
454
450
  if (enableOffset) {
455
451
  nodeRef.current?.measure((x, y, width, height, offsetLeft, offsetTop) => {
@@ -411,7 +411,7 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
411
411
  onLayout: onSvgLoad,
412
412
  onError: binderror && onSvgError,
413
413
  style: extendObject(
414
- { transformOrigin: 'top left' },
414
+ { transformOrigin: 'left top' },
415
415
  modeStyle
416
416
  )
417
417
  })
@@ -426,7 +426,7 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
426
426
  onError: binderror && onImageError,
427
427
  style: extendObject(
428
428
  {
429
- transformOrigin: 'top left',
429
+ transformOrigin: 'left top',
430
430
  width: isCropMode ? imageWidth : '100%',
431
431
  height: isCropMode ? imageHeight : '100%'
432
432
  },