@mpxjs/webpack-plugin 2.10.15 → 2.10.16-beta.2

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 (89) hide show
  1. package/lib/dependencies/AppEntryDependency.js +2 -2
  2. package/lib/dependencies/DynamicEntryDependency.js +1 -1
  3. package/lib/dependencies/ImportDependency.js +102 -0
  4. package/lib/dependencies/RecordModuleIdMapDependency.js +49 -0
  5. package/lib/dependencies/ResolveDependency.js +1 -1
  6. package/lib/{retry-runtime-module.js → dependencies/RetryRuntimeModule.js} +1 -1
  7. package/lib/helpers.js +2 -0
  8. package/lib/index.js +51 -25
  9. package/lib/json-compiler/helper.js +72 -2
  10. package/lib/json-compiler/index.js +14 -54
  11. package/lib/json-compiler/plugin.js +2 -2
  12. package/lib/loader.js +6 -2
  13. package/lib/native-loader.js +6 -3
  14. package/lib/platform/json/wx/index.js +24 -29
  15. package/lib/platform/style/wx/index.js +8 -1
  16. package/lib/platform/template/wx/component-config/button.js +12 -3
  17. package/lib/platform/template/wx/component-config/camera.js +12 -0
  18. package/lib/platform/template/wx/component-config/component.js +31 -33
  19. package/lib/platform/template/wx/component-config/slider.js +12 -0
  20. package/lib/platform/template/wx/component-config/unsupported.js +1 -1
  21. package/lib/react/processJSON.js +39 -71
  22. package/lib/react/processStyles.js +3 -2
  23. package/lib/react/processTemplate.js +6 -6
  24. package/lib/react/script-helper.js +6 -16
  25. package/lib/react/style-helper.js +10 -2
  26. package/lib/resolver/AddEnvPlugin.js +13 -0
  27. package/lib/resolver/AddModePlugin.js +18 -0
  28. package/lib/runtime/components/react/context.ts +2 -0
  29. package/lib/runtime/components/react/dist/context.js +1 -0
  30. package/lib/runtime/components/react/dist/mpx-camera.jsx +102 -0
  31. package/lib/runtime/components/react/dist/mpx-image.jsx +81 -37
  32. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +19 -4
  33. package/lib/runtime/components/react/dist/mpx-picker-view/index.jsx +3 -2
  34. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +9 -6
  35. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItem.jsx +8 -11
  36. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.jsx +20 -0
  37. package/lib/runtime/components/react/dist/mpx-portal/index.jsx +5 -1
  38. package/lib/runtime/components/react/dist/mpx-progress.jsx +26 -22
  39. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +6 -14
  40. package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
  41. package/lib/runtime/components/react/dist/mpx-text.jsx +33 -5
  42. package/lib/runtime/components/react/dist/mpx-view.jsx +8 -11
  43. package/lib/runtime/components/react/dist/mpx-web-view.jsx +1 -1
  44. package/lib/runtime/components/react/dist/utils.jsx +16 -6
  45. package/lib/runtime/components/react/mpx-camera.tsx +167 -0
  46. package/lib/runtime/components/react/mpx-image.tsx +89 -42
  47. package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +31 -4
  48. package/lib/runtime/components/react/mpx-picker-view/index.tsx +4 -1
  49. package/lib/runtime/components/react/mpx-picker-view-column/index.tsx +19 -8
  50. package/lib/runtime/components/react/mpx-picker-view-column/pickerViewColumnItem.tsx +8 -12
  51. package/lib/runtime/components/react/mpx-picker-view-column/pickerViewColumnItemLite.tsx +55 -0
  52. package/lib/runtime/components/react/mpx-portal/index.tsx +8 -2
  53. package/lib/runtime/components/react/mpx-progress.tsx +26 -24
  54. package/lib/runtime/components/react/mpx-scroll-view.tsx +6 -17
  55. package/lib/runtime/components/react/mpx-slider.tsx +444 -0
  56. package/lib/runtime/components/react/mpx-text.tsx +38 -5
  57. package/lib/runtime/components/react/mpx-view.tsx +8 -11
  58. package/lib/runtime/components/react/mpx-web-view.tsx +1 -1
  59. package/lib/runtime/components/react/utils.tsx +15 -6
  60. package/lib/runtime/components/web/mpx-input.vue +1 -1
  61. package/lib/runtime/components/web/mpx-scroll-view.vue +7 -1
  62. package/lib/runtime/components/web/mpx-video.vue +12 -1
  63. package/lib/runtime/optionProcessor.js +3 -1
  64. package/lib/runtime/optionProcessorReact.js +4 -2
  65. package/lib/script-setup-compiler/index.js +2 -2
  66. package/lib/style-compiler/index.js +3 -2
  67. package/lib/style-compiler/load-postcss-config.js +1 -1
  68. package/lib/style-compiler/plugins/trans-special.js +10 -2
  69. package/lib/style-compiler/strip-conditional-loader.js +155 -15
  70. package/lib/template-compiler/compiler.js +262 -61
  71. package/lib/template-compiler/gen-node-react.js +18 -6
  72. package/lib/template-compiler/index.js +6 -4
  73. package/lib/template-compiler/parse-exps.js +1 -1
  74. package/lib/utils/chain-assign.js +47 -0
  75. package/lib/utils/check-core-version-match.js +75 -15
  76. package/lib/utils/const.js +2 -1
  77. package/lib/utils/dom-tag-config.js +1 -1
  78. package/lib/utils/env.js +6 -1
  79. package/lib/utils/get-build-tag-component.js +35 -0
  80. package/lib/utils/pre-process-json.js +5 -0
  81. package/lib/web/processJSON.js +44 -16
  82. package/lib/web/processScript.js +1 -1
  83. package/lib/web/processTemplate.js +4 -4
  84. package/lib/web/script-helper.js +19 -9
  85. package/lib/wxs/pre-loader.js +5 -5
  86. package/lib/wxss/loader.js +1 -9
  87. package/package.json +14 -5
  88. package/LICENSE +0 -433
  89. package/lib/dependencies/ImportDependencyTemplate.js +0 -50
@@ -0,0 +1,167 @@
1
+ import React, { forwardRef, useRef, useCallback, useContext, useState, useEffect } from 'react'
2
+ import { Camera, useCameraDevice, useCodeScanner, useCameraFormat, useFrameProcessor } from 'react-native-vision-camera'
3
+ import { getCustomEvent } from './getInnerListeners'
4
+ import { RouteContext } from './context'
5
+
6
+ interface CameraProps {
7
+ mode?: 'normal' | 'scanCode'
8
+ resolution?: 'low' | 'medium' | 'high'
9
+ devicePosition?: 'front' | 'back'
10
+ flash?: 'auto' | 'on' | 'off'
11
+ frameSize?: 'small' | 'medium' | 'large'
12
+ style?: Record<string, any>
13
+ bindstop?: () => void
14
+ binderror?: (error: { message: string }) => void
15
+ bindinitdone?: (result: { type: string, data: string }) => void
16
+ bindscancode?: (result: { type: string, data: string }) => void
17
+ }
18
+
19
+ interface CameraRef {
20
+ setZoom: (zoom: number) => void
21
+ getTakePhoto: () => (() => Promise<any>) | undefined
22
+ getStartRecord: () => ((options: any) => void) | undefined
23
+ getStopRecord: () => (() => void) | undefined
24
+ }
25
+
26
+ type HandlerRef<T, P> = {
27
+ // 根据实际的 HandlerRef 类型定义调整
28
+ current: T | null
29
+ }
30
+
31
+ const _camera = forwardRef<HandlerRef<Camera, CameraProps>, CameraProps>((props: CameraProps, ref): JSX.Element | null => {
32
+ const cameraRef = useRef<Camera>(null)
33
+ const {
34
+ mode = 'normal',
35
+ resolution = 'medium',
36
+ devicePosition = 'back',
37
+ flash = 'auto',
38
+ frameSize = 'medium',
39
+ bindinitdone,
40
+ bindstop,
41
+ bindscancode
42
+ } = props
43
+
44
+ const isPhoto = mode === 'normal'
45
+ const device = useCameraDevice(devicePosition || 'back')
46
+ const { navigation } = useContext(RouteContext) || {}
47
+ const [zoomValue, setZoomValue] = useState<number>(1)
48
+ const [hasPermission, setHasPermission] = useState<boolean | null>(null)
49
+
50
+ // 先定义常量,避免在条件判断后使用
51
+ const maxZoom = device?.maxZoom || 1
52
+ const RESOLUTION_MAPPING: Record<string, { width: number, height: number }> = {
53
+ low: { width: 640, height: 480 },
54
+ medium: { width: 1280, height: 720 },
55
+ high: { width: 1920, height: 1080 }
56
+ }
57
+ const FRAME_SIZE_MAPPING: Record<string, { width: number, height: number }> = {
58
+ small: { width: 480, height: 360 },
59
+ medium: { width: 720, height: 540 },
60
+ large: { width: 1080, height: 810 }
61
+ }
62
+
63
+ // 所有 Hooks 必须在条件判断之前调用
64
+ const format = useCameraFormat(device, [
65
+ {
66
+ photoResolution: RESOLUTION_MAPPING[resolution],
67
+ videoResolution: FRAME_SIZE_MAPPING[frameSize] || RESOLUTION_MAPPING[resolution]
68
+ }
69
+ ])
70
+
71
+ const codeScanner = useCodeScanner({
72
+ codeTypes: ['qr', 'ean-13'],
73
+ onCodeScanned: (codes) => {
74
+ const result = codes.map(code => code.value).join(',')
75
+ bindscancode && bindscancode(getCustomEvent('scancode', {}, {
76
+ detail: {
77
+ result: codes.map(code => code.value).join(',')
78
+ }
79
+ }))
80
+ }
81
+ })
82
+
83
+ const onInitialized = useCallback(() => {
84
+ bindinitdone && bindinitdone(getCustomEvent('initdone', {}, {
85
+ detail: {
86
+ maxZoom
87
+ }
88
+ }))
89
+ }, [bindinitdone, maxZoom])
90
+
91
+ const onStopped = useCallback(() => {
92
+ bindstop && bindstop()
93
+ }, [bindstop])
94
+
95
+ // 检查相机权限
96
+ useEffect(() => {
97
+ const checkCameraPermission = async () => {
98
+ try {
99
+ const cameraPermission = global?.__mpx?.config?.rnConfig?.cameraPermission
100
+ if (typeof cameraPermission === 'function') {
101
+ const permissionResult = await cameraPermission()
102
+ setHasPermission(permissionResult === true)
103
+ } else {
104
+ setHasPermission(true)
105
+ }
106
+ } catch (error) {
107
+ setHasPermission(false)
108
+ }
109
+ }
110
+
111
+ checkCameraPermission()
112
+ }, [])
113
+
114
+ const camera: CameraRef = {
115
+ setZoom: (zoom: number) => {
116
+ setZoomValue(zoom)
117
+ },
118
+ getTakePhoto: () => {
119
+ return cameraRef.current?.takePhoto
120
+ },
121
+ getStartRecord: () => {
122
+ return cameraRef.current?.startRecording
123
+ },
124
+ getStopRecord: () => {
125
+ return cameraRef.current?.stopRecording
126
+ }
127
+ }
128
+
129
+ if (navigation) {
130
+ navigation.camera = camera
131
+ }
132
+
133
+ // 所有 Hooks 调用完成后再进行条件判断
134
+ if (hasPermission === null) {
135
+ return null
136
+ }
137
+
138
+ if (!hasPermission) {
139
+ return null
140
+ }
141
+
142
+ if (!device) {
143
+ return null
144
+ }
145
+
146
+ return (
147
+ <Camera
148
+ ref={cameraRef}
149
+ isActive={true}
150
+ photo={isPhoto}
151
+ video={true}
152
+ onInitialized={onInitialized}
153
+ onStopped={onStopped}
154
+ device={device}
155
+ flash={flash}
156
+ format={format}
157
+ codeScanner={!isPhoto ? codeScanner : undefined}
158
+ style={{ flex: 1 }}
159
+ zoom={zoomValue}
160
+ {...props}
161
+ />
162
+ )
163
+ })
164
+
165
+ _camera.displayName = 'MpxCamera'
166
+
167
+ export default _camera
@@ -62,8 +62,8 @@ export interface ImageProps {
62
62
  }
63
63
 
64
64
  interface ImageState {
65
- viewWidth?: number
66
- viewHeight?: number
65
+ viewWidth: number
66
+ viewHeight: number
67
67
  imageWidth?: number
68
68
  imageHeight?: number
69
69
  ratio?: number
@@ -93,9 +93,20 @@ const ModeMap = new Map<Mode, ImageResizeMode | undefined>([
93
93
  ...cropMode.map<[Mode, ImageResizeMode]>(mode => [mode, 'stretch'])
94
94
  ])
95
95
 
96
- const isNumber = (value: DimensionValue) => typeof value === 'number'
96
+ const isNumber = (value: DimensionValue): value is number => typeof value === 'number'
97
97
 
98
- const relativeCenteredSize = (viewSize: number, imageSize: number) => (viewSize - imageSize) / 2
98
+ const relativeCenteredSize = (viewSize: number, imageSize: number) => {
99
+ return (viewSize - imageSize) / 2
100
+ }
101
+
102
+ // 获取能完全显示图片的缩放比例:长宽方向的缩放比例最小值即为能完全展示的比例
103
+ function getFitScale (width1: number, height1: number, width2: number, height2: number) {
104
+ return Math.min(width2 / width1, height2 / height1)
105
+ }
106
+
107
+ function getFillScale (width1: number, height1: number, width2: number, height2: number) {
108
+ return Math.max(width2 / width1, height2 / height1)
109
+ }
99
110
 
100
111
  function noMeetCalcRule (isSvg: boolean, mode: Mode, viewWidth: number, viewHeight: number, ratio: number) {
101
112
  const isMeetSize = viewWidth && viewHeight && ratio
@@ -104,6 +115,17 @@ function noMeetCalcRule (isSvg: boolean, mode: Mode, viewWidth: number, viewHeig
104
115
  return false
105
116
  }
106
117
 
118
+ const getFixedWidth = (viewWidth: number, viewHeight: number, ratio: number) => {
119
+ if (!ratio) return viewWidth
120
+ const fixed = viewHeight / ratio
121
+ return !fixed ? viewWidth : fixed
122
+ }
123
+
124
+ const getFixedHeight = (viewWidth: number, viewHeight: number, ratio: number) => {
125
+ const fixed = viewWidth * ratio
126
+ return !fixed ? viewHeight : fixed
127
+ }
128
+
107
129
  const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, ref): JSX.Element => {
108
130
  const {
109
131
  src = '',
@@ -131,8 +153,6 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
131
153
  { overflow: 'hidden' }
132
154
  )
133
155
 
134
- const state = useRef<ImageState>({})
135
-
136
156
  const nodeRef = useRef(null)
137
157
  useNodesRef(props, ref, nodeRef, {
138
158
  defaultStyle
@@ -148,14 +168,18 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
148
168
  const onLayout = ({ nativeEvent: { layout: { width, height } } }: LayoutChangeEvent) => {
149
169
  state.current.viewWidth = width
150
170
  state.current.viewHeight = height
151
-
171
+ // 实际渲染尺寸可能会指定的值不一致,误差低于 0.5 则认为没有变化
172
+ if (Math.abs(viewHeight - height) < 0.5 && Math.abs(viewWidth - width) < 0.5) {
173
+ if (state.current.imageWidth && state.current.imageHeight && state.current.ratio) {
174
+ if (!loaded) setLoaded(true)
175
+ }
176
+ return
177
+ }
152
178
  if (state.current.imageWidth && state.current.imageHeight && state.current.ratio) {
153
- setViewWidth(width)
154
- setViewHeight(height)
155
179
  setRatio(state.current.ratio)
156
180
  setImageWidth(state.current.imageWidth)
157
181
  setImageHeight(state.current.imageHeight)
158
- state.current = {}
182
+ setViewSize(state.current.viewWidth!, state.current.viewHeight!, state.current.ratio!)
159
183
  setLoaded(true)
160
184
  }
161
185
  }
@@ -186,43 +210,57 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
186
210
  const [ratio, setRatio] = useState(0)
187
211
  const [loaded, setLoaded] = useState(!isLayoutMode)
188
212
 
189
- const fixedHeight = useMemo(() => {
190
- const fixed = viewWidth * ratio
191
- return !fixed ? viewHeight : fixed
192
- }, [ratio, viewWidth, viewHeight])
213
+ const state = useRef<ImageState>({
214
+ viewWidth,
215
+ viewHeight
216
+ })
193
217
 
194
- const fixedWidth = useMemo(() => {
195
- if (!ratio) return viewWidth
196
- const fixed = viewHeight / ratio
197
- return !fixed ? viewWidth : fixed
198
- }, [ratio, viewWidth, viewHeight])
218
+ function setViewSize (viewWidth: number, viewHeight: number, ratio: number) {
219
+ // 在特定模式下可预测 view 的变化,在onLayout触发时能以此避免重复render
220
+ switch (mode) {
221
+ case 'widthFix': {
222
+ setViewWidth(viewWidth)
223
+ const fixedHeight = getFixedHeight(viewWidth, viewHeight, ratio)
224
+ setViewHeight(fixedHeight)
225
+ break
226
+ }
227
+ case 'heightFix': {
228
+ setViewHeight(viewHeight)
229
+ const fixedWidth = getFixedWidth(viewWidth, viewHeight, ratio)
230
+ setViewWidth(fixedWidth)
231
+ break
232
+ }
233
+ default:
234
+ setViewHeight(viewHeight)
235
+ setViewWidth(viewWidth)
236
+ break
237
+ }
238
+ }
199
239
 
200
240
  const modeStyle: ImageStyle = useMemo(() => {
201
241
  if (noMeetCalcRule(isSvg, mode, viewWidth, viewHeight, ratio)) return {}
202
242
  switch (mode) {
203
- case 'scaleToFill':
243
+ case 'scaleToFill': // wx 中 svg 图片的 scaleToFill 模式效果与 aspectFit 一致,不会就行图片缩放,此处保持一致
204
244
  case 'aspectFit':
205
245
  if (isSvg) {
206
- const scale = ratio <= 1
207
- ? imageWidth >= viewWidth ? viewWidth / imageWidth : imageWidth / viewWidth
208
- : imageHeight >= viewHeight ? viewHeight / imageHeight : imageHeight / viewHeight
246
+ const scale = getFitScale(imageWidth, imageHeight, viewWidth, viewHeight)
209
247
  return {
210
248
  transform: [
211
- { scale },
212
- ratio <= 1 ? { translateY: -(imageHeight * scale - viewHeight) / 2 / scale } : { translateX: -(imageWidth * scale - viewWidth) / 2 / scale }
249
+ { translateY: relativeCenteredSize(viewHeight, imageHeight * scale) },
250
+ { translateX: relativeCenteredSize(viewWidth, imageWidth * scale) },
251
+ { scale }
213
252
  ]
214
253
  }
215
254
  }
216
255
  return {}
217
256
  case 'aspectFill':
218
257
  if (isSvg) {
219
- const scale = ratio >= 1
220
- ? imageWidth >= viewWidth ? viewWidth / imageWidth : imageWidth / viewWidth
221
- : imageHeight >= viewHeight ? viewHeight / imageHeight : imageHeight / viewHeight
258
+ const scale = getFillScale(imageWidth, imageHeight, viewWidth, viewHeight)
222
259
  return {
223
260
  transform: [
224
- { scale },
225
- ratio >= 1 ? { translateY: -(imageHeight * scale - viewHeight) / 2 / scale } : { translateX: -(imageWidth * scale - viewWidth) / 2 / scale }
261
+ { translateY: relativeCenteredSize(viewHeight, imageHeight * scale) },
262
+ { translateX: relativeCenteredSize(viewWidth, imageWidth * scale) },
263
+ { scale }
226
264
  ]
227
265
  }
228
266
  }
@@ -230,9 +268,7 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
230
268
  case 'widthFix':
231
269
  case 'heightFix':
232
270
  if (isSvg) {
233
- const scale = ratio >= 1
234
- ? imageWidth >= fixedWidth ? fixedWidth / imageWidth : imageWidth / fixedWidth
235
- : imageHeight >= fixedHeight ? fixedHeight / imageHeight : imageHeight / fixedHeight
271
+ const scale = getFitScale(imageWidth, imageHeight, viewWidth, viewHeight)
236
272
  return {
237
273
  transform: [{ scale }]
238
274
  }
@@ -295,13 +331,25 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
295
331
  default:
296
332
  return {}
297
333
  }
298
- }, [isSvg, mode, viewWidth, viewHeight, imageWidth, imageHeight, ratio, fixedWidth, fixedHeight])
334
+ }, [isSvg, mode, viewWidth, viewHeight, imageWidth, imageHeight, ratio])
299
335
 
300
336
  const onSvgLoad = (evt: LayoutChangeEvent) => {
301
337
  const { width, height } = evt.nativeEvent.layout
302
- setRatio(!width ? 0 : height / width)
303
- setImageWidth(width)
338
+ state.current.imageHeight = height
304
339
  setImageHeight(height)
340
+ state.current.ratio = !width ? 0 : height / width
341
+
342
+ if (isWidthFixMode
343
+ ? state.current.viewWidth
344
+ : isHeightFixMode
345
+ ? state.current.viewHeight
346
+ : state.current.viewWidth && state.current.viewHeight) {
347
+ setRatio(state.current.ratio)
348
+ setImageWidth(width)
349
+ setImageHeight(height)
350
+ setViewSize(state.current.viewWidth!, state.current.viewHeight!, state.current.ratio)
351
+ setLoaded(true)
352
+ }
305
353
 
306
354
  bindload && bindload(
307
355
  getCustomEvent(
@@ -375,12 +423,11 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
375
423
  : isHeightFixMode
376
424
  ? state.current.viewHeight
377
425
  : state.current.viewWidth && state.current.viewHeight) {
378
- state.current.viewWidth && setViewWidth(state.current.viewWidth)
379
- state.current.viewHeight && setViewHeight(state.current.viewHeight)
380
- setRatio(!width ? 0 : height / width)
426
+ setRatio(state.current.ratio)
381
427
  setImageWidth(width)
382
428
  setImageHeight(height)
383
- state.current = {}
429
+ setViewSize(state.current.viewWidth!, state.current.viewHeight!, state.current.ratio!)
430
+
384
431
  setLoaded(true)
385
432
  }
386
433
  },
@@ -402,8 +449,8 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
402
449
  {},
403
450
  normalStyle,
404
451
  layoutStyle,
405
- isHeightFixMode ? { width: fixedWidth } : {},
406
- isWidthFixMode ? { height: fixedHeight } : {}
452
+ isHeightFixMode ? { width: viewWidth } : {},
453
+ isWidthFixMode ? { height: viewHeight } : {}
407
454
  )
408
455
  }
409
456
  ),
@@ -1,6 +1,6 @@
1
- import React, { ReactNode, useContext, useEffect } from 'react'
1
+ import React, { ReactNode, useContext, useEffect, useRef } from 'react'
2
2
  import { DimensionValue, EmitterSubscription, Keyboard, View, ViewStyle, NativeSyntheticEvent, NativeTouchEvent } from 'react-native'
3
- import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated'
3
+ import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing, cancelAnimation } from 'react-native-reanimated'
4
4
  import { KeyboardAvoidContext } from './context'
5
5
  import { isIOS } from './utils'
6
6
 
@@ -18,15 +18,28 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
18
18
  const basic = useSharedValue('auto')
19
19
  const keyboardAvoid = useContext(KeyboardAvoidContext)
20
20
 
21
+ // fix: 某些特殊机型下隐藏键盘可能会先触发一次 keyboardWillShow,
22
+ // 比如机型 iPhone 11 Pro,可能会导致显隐动画冲突
23
+ // 因此增加状态标记 + cancelAnimation 来优化
24
+ const isShow = useRef<boolean>(false)
25
+
21
26
  const animatedStyle = useAnimatedStyle(() => ({
22
27
  transform: [{ translateY: -offset.value }],
23
28
  flexBasis: basic.value as DimensionValue
24
29
  }))
25
30
 
26
31
  const resetKeyboard = () => {
32
+ if (!isShow.current) {
33
+ return
34
+ }
35
+
36
+ isShow.current = false
37
+
27
38
  if (keyboardAvoid?.current) {
28
39
  keyboardAvoid.current = null
29
40
  }
41
+
42
+ cancelAnimation(offset)
30
43
  offset.value = withTiming(0, { duration, easing })
31
44
  basic.value = 'auto'
32
45
  }
@@ -43,15 +56,22 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
43
56
  if (isIOS) {
44
57
  subscriptions = [
45
58
  Keyboard.addListener('keyboardWillShow', (evt: any) => {
46
- if (!keyboardAvoid?.current) return
59
+ if (!keyboardAvoid?.current || isShow.current) {
60
+ return
61
+ }
62
+
63
+ isShow.current = true
64
+
47
65
  const { endCoordinates } = evt
48
66
  const { ref, cursorSpacing = 0 } = keyboardAvoid.current
67
+
49
68
  setTimeout(() => {
50
69
  ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
51
70
  const aboveOffset = offset.value + pageY + height - endCoordinates.screenY
52
71
  const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
53
72
  const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing)
54
73
  const value = aboveOffset > 0 ? belowValue : aboveValue
74
+ cancelAnimation(offset)
55
75
  offset.value = withTiming(value, { duration, easing }, (finished) => {
56
76
  if (finished) {
57
77
  // Set flexBasic after animation to trigger re-layout and reset layout information
@@ -66,15 +86,22 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
66
86
  } else {
67
87
  subscriptions = [
68
88
  Keyboard.addListener('keyboardDidShow', (evt: any) => {
69
- if (!keyboardAvoid?.current) return
89
+ if (!keyboardAvoid?.current || isShow.current) {
90
+ return
91
+ }
92
+
93
+ isShow.current = true
94
+
70
95
  const { endCoordinates } = evt
71
96
  const { ref, cursorSpacing = 0 } = keyboardAvoid.current
97
+
72
98
  ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
73
99
  const aboveOffset = pageY + height - endCoordinates.screenY
74
100
  const belowOffset = endCoordinates.height - aboveOffset
75
101
  const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
76
102
  const belowValue = Math.min(belowOffset, cursorSpacing)
77
103
  const value = aboveOffset > 0 ? belowValue : aboveValue
104
+ cancelAnimation(offset)
78
105
  offset.value = withTiming(value, { duration, easing }, (finished) => {
79
106
  if (finished) {
80
107
  // Set flexBasic after animation to trigger re-layout and reset layout information
@@ -33,6 +33,7 @@ interface PickerViewProps {
33
33
  style?: {
34
34
  [key: string]: any
35
35
  }
36
+ 'enable-wheel-animation'?: boolean
36
37
  'indicator-style'?: Record<string, any>,
37
38
  'mask-style'?: Record<string, any>,
38
39
  'enable-var'?: boolean
@@ -69,6 +70,7 @@ const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProp
69
70
  value = [],
70
71
  bindchange,
71
72
  style,
73
+ 'enable-wheel-animation': enableWheelAnimation = true,
72
74
  'indicator-style': indicatorStyle = {},
73
75
  'mask-style': pickerMaskStyle = {},
74
76
  'enable-var': enableVar,
@@ -176,7 +178,8 @@ const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProp
176
178
  onSelectChange: onSelectChange.bind(null, index),
177
179
  initialIndex,
178
180
  pickerIndicatorStyle,
179
- pickerMaskStyle
181
+ pickerMaskStyle,
182
+ enableWheelAnimation
180
183
  }
181
184
  )
182
185
  const realElement = React.cloneElement(child, wrappedProps)
@@ -5,7 +5,8 @@ import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAn
5
5
  import useNodesRef, { HandlerRef } from '../useNodesRef'
6
6
  import PickerIndicator from './pickerViewIndicator'
7
7
  import PickerMask from './pickerViewMask'
8
- import MpxPickerVIewColumnItem from './pickerViewColumnItem'
8
+ import MpxPickerViewColumnItem from './pickerViewColumnItem'
9
+ import MpxPickerViewColumnItemLite from './pickerViewColumnItemLite'
9
10
  import { PickerViewColumnAnimationContext } from '../mpx-picker-view/pickerVIewContext'
10
11
  import { calcHeightOffsets } from './pickerViewFaces'
11
12
 
@@ -25,6 +26,7 @@ interface ColumnProps {
25
26
  }
26
27
  pickerMaskStyle: Record<string, any>
27
28
  pickerIndicatorStyle: Record<string, any>
29
+ enableWheelAnimation?: boolean
28
30
  }
29
31
 
30
32
  const visibleCount = 5
@@ -39,6 +41,7 @@ const _PickerViewColumn = forwardRef<HandlerRef<ScrollView & View, ColumnProps>,
39
41
  wrapperStyle,
40
42
  pickerMaskStyle,
41
43
  pickerIndicatorStyle,
44
+ enableWheelAnimation = true,
42
45
  'enable-var': enableVar,
43
46
  'external-var-context': externalVarContext
44
47
  } = props
@@ -287,8 +290,8 @@ const _PickerViewColumn = forwardRef<HandlerRef<ScrollView & View, ColumnProps>,
287
290
 
288
291
  const renderInnerchild = () =>
289
292
  columnData.map((item: React.ReactElement, index: number) => {
290
- return (
291
- <MpxPickerVIewColumnItem
293
+ return enableWheelAnimation
294
+ ? (<MpxPickerViewColumnItem
292
295
  key={index}
293
296
  item={item}
294
297
  index={index}
@@ -297,8 +300,16 @@ const _PickerViewColumn = forwardRef<HandlerRef<ScrollView & View, ColumnProps>,
297
300
  textProps={textProps}
298
301
  visibleCount={visibleCount}
299
302
  onItemLayout={onItemLayout}
300
- />
301
- )
303
+ />)
304
+ : (<MpxPickerViewColumnItemLite
305
+ key={index}
306
+ item={item}
307
+ index={index}
308
+ itemHeight={itemHeight}
309
+ textStyle={textStyle}
310
+ textProps={textProps}
311
+ onItemLayout={onItemLayout}
312
+ />)
302
313
  })
303
314
 
304
315
  const renderScollView = () => {
@@ -351,9 +362,9 @@ const _PickerViewColumn = forwardRef<HandlerRef<ScrollView & View, ColumnProps>,
351
362
 
352
363
  return (
353
364
  <View style={[styles.wrapper, normalStyle]}>
354
- {renderScollView()}
355
- {renderMask()}
356
- {renderIndicator()}
365
+ {renderScollView()}
366
+ {renderMask()}
367
+ {renderIndicator()}
357
368
  </View>
358
369
  )
359
370
  })
@@ -1,6 +1,6 @@
1
- import React, { useEffect } from 'react'
1
+ import React, { useMemo } from 'react'
2
2
  import { LayoutChangeEvent } from 'react-native'
3
- import Reanimated, { Extrapolation, interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated'
3
+ import Reanimated, { Extrapolation, interpolate, useAnimatedStyle } from 'react-native-reanimated'
4
4
  import { extendObject } from '../utils'
5
5
  import { createFaces } from './pickerViewFaces'
6
6
  import { usePickerViewColumnAnimationContext, usePickerViewStyleContext } from '../mpx-picker-view/pickerVIewContext'
@@ -28,20 +28,16 @@ const PickerViewColumnItem: React.FC<PickerColumnItemProps> = ({
28
28
  }) => {
29
29
  const textStyleFromAncestor = usePickerViewStyleContext()
30
30
  const offsetYShared = usePickerViewColumnAnimationContext()
31
- const facesShared = useSharedValue(createFaces(itemHeight, visibleCount))
32
-
33
- useEffect(() => {
34
- facesShared.value = createFaces(itemHeight, visibleCount)
35
- }, [itemHeight])
31
+ const facesShared = useMemo(() => createFaces(itemHeight, visibleCount), [itemHeight, visibleCount])
36
32
 
37
33
  const animatedStyles = useAnimatedStyle(() => {
38
- const inputRange = facesShared.value.map((f) => itemHeight * (index + f.index))
34
+ const inputRange = facesShared.map((f) => itemHeight * (index + f.index))
39
35
  return {
40
- opacity: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.opacity), Extrapolation.CLAMP),
36
+ opacity: interpolate(offsetYShared.value, inputRange, facesShared.map((x) => x.opacity), Extrapolation.CLAMP),
41
37
  transform: [
42
- { translateY: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.offsetY), Extrapolation.EXTEND) },
43
- { rotateX: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.deg), Extrapolation.CLAMP) + 'deg' },
44
- { scale: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.scale), Extrapolation.EXTEND) }
38
+ { translateY: interpolate(offsetYShared.value, inputRange, facesShared.map((x) => x.offsetY), Extrapolation.EXTEND) },
39
+ { rotateX: interpolate(offsetYShared.value, inputRange, facesShared.map((x) => x.deg), Extrapolation.CLAMP) + 'deg' },
40
+ { scale: interpolate(offsetYShared.value, inputRange, facesShared.map((x) => x.scale), Extrapolation.EXTEND) }
45
41
  ]
46
42
  }
47
43
  })
@@ -0,0 +1,55 @@
1
+ import React from 'react'
2
+ import { LayoutChangeEvent, View } from 'react-native'
3
+ import { extendObject } from '../utils'
4
+ import { usePickerViewStyleContext } from '../mpx-picker-view/pickerVIewContext'
5
+
6
+ interface PickerColumnItemProps {
7
+ item: React.ReactElement
8
+ index: number
9
+ itemHeight: number
10
+ itemWidth?: number | '100%'
11
+ textStyle: Record<string, any>
12
+ textProps?: any
13
+ onItemLayout?: (e: LayoutChangeEvent) => void
14
+ }
15
+
16
+ const PickerViewColumnItem: React.FC<PickerColumnItemProps> = ({
17
+ item,
18
+ index,
19
+ itemHeight,
20
+ itemWidth = '100%',
21
+ textStyle,
22
+ textProps,
23
+ onItemLayout
24
+ }) => {
25
+ const textStyleFromAncestor = usePickerViewStyleContext()
26
+ const strKey = `picker-column-item-${index}`
27
+ const restProps = index === 0 ? { onLayout: onItemLayout } : {}
28
+ const itemProps = extendObject(
29
+ {
30
+ style: extendObject(
31
+ { height: itemHeight, width: '100%' },
32
+ textStyleFromAncestor,
33
+ textStyle,
34
+ item.props.style
35
+ )
36
+ },
37
+ textProps,
38
+ restProps
39
+ )
40
+ const realItem = React.cloneElement(item, itemProps)
41
+
42
+ return (
43
+ <View
44
+ key={strKey}
45
+ style={[
46
+ { height: itemHeight, width: itemWidth, pointerEvents: 'none' }
47
+ ]}
48
+ >
49
+ {realItem}
50
+ </View>
51
+ )
52
+ }
53
+
54
+ PickerViewColumnItem.displayName = 'MpxPickerViewColumnItem'
55
+ export default PickerViewColumnItem