@mpxjs/webpack-plugin 2.10.15-prelease.1 → 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.
- package/lib/dependencies/AppEntryDependency.js +2 -2
- package/lib/dependencies/RecordModuleIdMapDependency.js +49 -0
- package/lib/dependencies/ResolveDependency.js +1 -1
- package/lib/helpers.js +2 -0
- package/lib/index.js +40 -15
- package/lib/json-compiler/helper.js +72 -2
- package/lib/json-compiler/index.js +14 -54
- package/lib/json-compiler/plugin.js +2 -2
- package/lib/loader.js +6 -2
- package/lib/native-loader.js +6 -3
- package/lib/platform/json/wx/index.js +24 -29
- package/lib/platform/style/wx/index.js +8 -1
- package/lib/platform/template/wx/component-config/button.js +12 -3
- package/lib/platform/template/wx/component-config/camera.js +12 -0
- package/lib/platform/template/wx/component-config/component.js +31 -33
- package/lib/platform/template/wx/component-config/unsupported.js +1 -1
- package/lib/react/processJSON.js +39 -71
- package/lib/react/processStyles.js +3 -2
- package/lib/react/processTemplate.js +6 -6
- package/lib/react/script-helper.js +6 -16
- package/lib/react/style-helper.js +10 -2
- package/lib/runtime/components/react/context.ts +2 -0
- package/lib/runtime/components/react/dist/context.js +1 -0
- package/lib/runtime/components/react/dist/mpx-camera.jsx +102 -0
- package/lib/runtime/components/react/dist/mpx-image.jsx +81 -37
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +19 -4
- package/lib/runtime/components/react/dist/mpx-picker-view/index.jsx +3 -2
- package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +9 -6
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItem.jsx +8 -11
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.jsx +20 -0
- package/lib/runtime/components/react/dist/mpx-portal/index.jsx +5 -1
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +6 -14
- package/lib/runtime/components/react/dist/mpx-text.jsx +33 -5
- package/lib/runtime/components/react/dist/mpx-view.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +1 -1
- package/lib/runtime/components/react/dist/utils.jsx +16 -6
- package/lib/runtime/components/react/mpx-camera.tsx +167 -0
- package/lib/runtime/components/react/mpx-image.tsx +89 -42
- package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +31 -4
- package/lib/runtime/components/react/mpx-picker-view/index.tsx +4 -1
- package/lib/runtime/components/react/mpx-picker-view-column/index.tsx +19 -8
- package/lib/runtime/components/react/mpx-picker-view-column/pickerViewColumnItem.tsx +8 -12
- package/lib/runtime/components/react/mpx-picker-view-column/pickerViewColumnItemLite.tsx +55 -0
- package/lib/runtime/components/react/mpx-portal/index.tsx +8 -2
- package/lib/runtime/components/react/mpx-scroll-view.tsx +6 -17
- package/lib/runtime/components/react/mpx-text.tsx +38 -5
- package/lib/runtime/components/react/mpx-view.tsx +1 -1
- package/lib/runtime/components/react/mpx-web-view.tsx +1 -1
- package/lib/runtime/components/react/utils.tsx +15 -6
- package/lib/runtime/components/web/mpx-input.vue +0 -14
- package/lib/script-setup-compiler/index.js +2 -2
- package/lib/style-compiler/index.js +3 -2
- package/lib/style-compiler/load-postcss-config.js +1 -1
- package/lib/style-compiler/plugins/trans-special.js +10 -2
- package/lib/style-compiler/strip-conditional-loader.js +155 -15
- package/lib/template-compiler/compiler.js +252 -55
- package/lib/template-compiler/gen-node-react.js +18 -6
- package/lib/template-compiler/index.js +6 -4
- package/lib/template-compiler/parse-exps.js +1 -1
- package/lib/utils/const.js +2 -1
- package/lib/utils/dom-tag-config.js +1 -1
- package/lib/utils/env.js +6 -1
- package/lib/utils/get-build-tag-component.js +35 -0
- package/lib/utils/pre-process-json.js +5 -0
- package/lib/web/processJSON.js +44 -16
- package/lib/web/processScript.js +1 -1
- package/lib/web/processTemplate.js +4 -4
- package/lib/web/script-helper.js +19 -9
- package/lib/wxss/loader.js +1 -9
- package/package.json +12 -4
|
@@ -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
|
|
66
|
-
viewHeight
|
|
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) =>
|
|
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
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
213
|
+
const state = useRef<ImageState>({
|
|
214
|
+
viewWidth,
|
|
215
|
+
viewHeight
|
|
216
|
+
})
|
|
193
217
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
406
|
-
isWidthFixMode ? { height:
|
|
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)
|
|
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)
|
|
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
|
|
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
|
-
<
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
365
|
+
{renderScollView()}
|
|
366
|
+
{renderMask()}
|
|
367
|
+
{renderIndicator()}
|
|
357
368
|
</View>
|
|
358
369
|
)
|
|
359
370
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
2
|
import { LayoutChangeEvent } from 'react-native'
|
|
3
|
-
import Reanimated, { Extrapolation, interpolate, useAnimatedStyle
|
|
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 =
|
|
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.
|
|
34
|
+
const inputRange = facesShared.map((f) => itemHeight * (index + f.index))
|
|
39
35
|
return {
|
|
40
|
-
opacity: interpolate(offsetYShared.value, inputRange, facesShared.
|
|
36
|
+
opacity: interpolate(offsetYShared.value, inputRange, facesShared.map((x) => x.opacity), Extrapolation.CLAMP),
|
|
41
37
|
transform: [
|
|
42
|
-
{ translateY: interpolate(offsetYShared.value, inputRange, facesShared.
|
|
43
|
-
{ rotateX: interpolate(offsetYShared.value, inputRange, facesShared.
|
|
44
|
-
{ scale: interpolate(offsetYShared.value, inputRange, facesShared.
|
|
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
|