@mpxjs/webpack-plugin 2.9.69-beta.3 → 2.9.69-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +17 -1
- package/lib/platform/style/wx/index.js +18 -18
- package/lib/platform/template/wx/component-config/movable-view.js +8 -1
- package/lib/platform/template/wx/component-config/scroll-view.js +1 -1
- package/lib/react/processScript.js +1 -1
- package/lib/resolver/AddEnvPlugin.js +1 -0
- package/lib/resolver/AddModePlugin.js +1 -0
- package/lib/runtime/components/react/context.ts +25 -0
- package/lib/runtime/components/react/dist/context.js +2 -0
- package/lib/runtime/components/react/dist/getInnerListeners.js +2 -2
- package/lib/runtime/components/react/dist/locale-provider.jsx +15 -0
- package/lib/runtime/components/react/dist/mpx-button.jsx +9 -37
- package/lib/runtime/components/react/dist/mpx-image.jsx +13 -9
- package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
- package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +12 -13
- package/lib/runtime/components/react/dist/mpx-portal/portal-consumer.jsx +23 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +124 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +40 -0
- package/lib/runtime/components/react/dist/mpx-portal.jsx +12 -0
- package/lib/runtime/components/react/dist/mpx-provider.jsx +31 -0
- package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +10 -16
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +10 -5
- package/lib/runtime/components/react/dist/mpx-view.jsx +15 -21
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +78 -20
- package/lib/runtime/components/react/dist/pickerFaces.js +1 -1
- package/lib/runtime/components/react/dist/useAnimationHooks.js +14 -2
- package/lib/runtime/components/react/getInnerListeners.ts +5 -7
- package/lib/runtime/components/react/locale-provider.tsx +83 -0
- package/lib/runtime/components/react/mpx-button.tsx +13 -49
- package/lib/runtime/components/react/mpx-image.tsx +41 -25
- package/lib/runtime/components/react/mpx-input.tsx +1 -1
- package/lib/runtime/components/react/mpx-movable-view.tsx +1 -1
- package/lib/runtime/components/react/mpx-picker/time.tsx +2 -1
- package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +88 -0
- package/lib/runtime/components/react/mpx-picker-view-column.tsx +180 -163
- package/lib/runtime/components/react/mpx-picker-view.tsx +35 -37
- package/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +32 -0
- package/lib/runtime/components/react/mpx-portal/portal-host.tsx +158 -0
- package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +64 -0
- package/lib/runtime/components/react/mpx-portal.tsx +29 -0
- package/lib/runtime/components/react/mpx-provider.tsx +51 -0
- package/lib/runtime/components/react/mpx-rich-text/index.tsx +12 -18
- package/lib/runtime/components/react/mpx-root-portal.tsx +1 -1
- package/lib/runtime/components/react/mpx-scroll-view.tsx +29 -18
- package/lib/runtime/components/react/mpx-swiper-item.tsx +38 -10
- package/lib/runtime/components/react/mpx-swiper.tsx +696 -0
- package/lib/runtime/components/react/mpx-view.tsx +25 -71
- package/lib/runtime/components/react/mpx-web-view.tsx +156 -23
- package/lib/runtime/components/react/pickerFaces.ts +15 -7
- package/lib/runtime/components/react/pickerVIewContext.ts +18 -0
- package/lib/runtime/components/react/pickerViewMask.tsx +30 -0
- package/lib/runtime/components/react/{pickerOverlay.tsx → pickerViewOverlay.tsx} +5 -3
- package/lib/runtime/components/react/types/global.d.ts +6 -1
- package/lib/runtime/components/react/useAnimationHooks.ts +20 -4
- package/lib/runtime/components/react/utils.tsx +75 -5
- package/lib/style-compiler/index.js +3 -4
- package/lib/style-compiler/strip-conditional-loader.js +118 -0
- package/lib/template-compiler/compiler.js +9 -14
- package/lib/utils/hump-dash.js +1 -1
- package/lib/utils/pre-process-json.js +5 -9
- package/package.json +1 -1
- package/lib/runtime/components/react/mpx-swiper/carouse.tsx +0 -527
- package/lib/runtime/components/react/mpx-swiper/index.tsx +0 -80
- package/lib/runtime/components/react/mpx-swiper/type.ts +0 -87
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
import { View, NativeSyntheticEvent, LayoutChangeEvent } from 'react-native'
|
|
2
|
+
import { GestureDetector, Gesture } from 'react-native-gesture-handler'
|
|
3
|
+
import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, runOnUI, useAnimatedReaction, cancelAnimation } from 'react-native-reanimated'
|
|
4
|
+
|
|
5
|
+
import React, { JSX, forwardRef, useRef, useEffect, ReactNode, ReactElement } from 'react'
|
|
6
|
+
import useInnerProps, { getCustomEvent } from './getInnerListeners'
|
|
7
|
+
import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数
|
|
8
|
+
import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from './utils'
|
|
9
|
+
import { SwiperContext } from './context'
|
|
10
|
+
/**
|
|
11
|
+
* ✔ indicator-dots
|
|
12
|
+
* ✔ indicator-color
|
|
13
|
+
* ✔ indicator-active-color
|
|
14
|
+
* ✔ autoplay
|
|
15
|
+
* ✔ current
|
|
16
|
+
* ✔ interval
|
|
17
|
+
* ✔ duration
|
|
18
|
+
* ✔ circular
|
|
19
|
+
* ✔ vertical
|
|
20
|
+
* ✔ previous-margin
|
|
21
|
+
* ✔ next-margin
|
|
22
|
+
* ✔ easing-function ="easeOutCubic"
|
|
23
|
+
* ✘ display-multiple-items
|
|
24
|
+
* ✘ snap-to-edge
|
|
25
|
+
*/
|
|
26
|
+
type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'
|
|
27
|
+
type StrTransType = 'translationX' | 'translationY'
|
|
28
|
+
type StrAbsoType = 'absoluteX' | 'absoluteY'
|
|
29
|
+
type StrVelocity = 'velocityX' | 'velocityY'
|
|
30
|
+
type EventDataType = {
|
|
31
|
+
translation: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface SwiperProps {
|
|
35
|
+
children?: ReactNode;
|
|
36
|
+
circular?: boolean;
|
|
37
|
+
current?: number;
|
|
38
|
+
interval?: number;
|
|
39
|
+
autoplay?: boolean;
|
|
40
|
+
// scrollView 只有安卓可以设
|
|
41
|
+
duration?: number;
|
|
42
|
+
// 滑动过程中元素是否scale变化
|
|
43
|
+
scale?: boolean;
|
|
44
|
+
'indicator-dots'?: boolean;
|
|
45
|
+
'indicator-color'?: string;
|
|
46
|
+
'indicator-active-color'?: string;
|
|
47
|
+
vertical?: boolean;
|
|
48
|
+
style: {
|
|
49
|
+
[key: string]: any
|
|
50
|
+
};
|
|
51
|
+
'easing-function'?: EaseType;
|
|
52
|
+
'previous-margin'?: string;
|
|
53
|
+
'next-margin'?: string;
|
|
54
|
+
'enable-offset'?: boolean;
|
|
55
|
+
'enable-var': boolean;
|
|
56
|
+
'parent-font-size'?: number;
|
|
57
|
+
'parent-width'?: number;
|
|
58
|
+
'parent-height'?: number;
|
|
59
|
+
'external-var-context'?: Record<string, any>;
|
|
60
|
+
bindchange?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 默认的Style类型
|
|
65
|
+
*/
|
|
66
|
+
const styles: { [key: string]: Object } = {
|
|
67
|
+
pagination_x: {
|
|
68
|
+
position: 'absolute',
|
|
69
|
+
bottom: 25,
|
|
70
|
+
left: 0,
|
|
71
|
+
right: 0,
|
|
72
|
+
flexDirection: 'row',
|
|
73
|
+
flex: 1,
|
|
74
|
+
justifyContent: 'center',
|
|
75
|
+
alignItems: 'center'
|
|
76
|
+
},
|
|
77
|
+
pagination_y: {
|
|
78
|
+
position: 'absolute',
|
|
79
|
+
right: 15,
|
|
80
|
+
top: 0,
|
|
81
|
+
bottom: 0,
|
|
82
|
+
flexDirection: 'column',
|
|
83
|
+
flex: 1,
|
|
84
|
+
justifyContent: 'center',
|
|
85
|
+
alignItems: 'center'
|
|
86
|
+
},
|
|
87
|
+
pagerWrapperx: {
|
|
88
|
+
position: 'absolute',
|
|
89
|
+
flexDirection: 'row',
|
|
90
|
+
justifyContent: 'center',
|
|
91
|
+
alignItems: 'center'
|
|
92
|
+
},
|
|
93
|
+
pagerWrappery: {
|
|
94
|
+
position: 'absolute',
|
|
95
|
+
flexDirection: 'column',
|
|
96
|
+
justifyContent: 'center',
|
|
97
|
+
alignItems: 'center'
|
|
98
|
+
},
|
|
99
|
+
swiper: {
|
|
100
|
+
overflow: 'scroll',
|
|
101
|
+
display: 'flex',
|
|
102
|
+
justifyContent: 'flex-start'
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const dotCommonStyle = {
|
|
107
|
+
width: 8,
|
|
108
|
+
height: 8,
|
|
109
|
+
borderRadius: 4,
|
|
110
|
+
marginLeft: 3,
|
|
111
|
+
marginRight: 3,
|
|
112
|
+
marginTop: 3,
|
|
113
|
+
marginBottom: 3,
|
|
114
|
+
zIndex: 98
|
|
115
|
+
}
|
|
116
|
+
const activeDotStyle = {
|
|
117
|
+
zIndex: 99
|
|
118
|
+
}
|
|
119
|
+
const longPressRatio = 100
|
|
120
|
+
|
|
121
|
+
const easeMap = {
|
|
122
|
+
default: Easing.linear,
|
|
123
|
+
linear: Easing.linear,
|
|
124
|
+
easeInCubic: Easing.in(Easing.cubic),
|
|
125
|
+
easeOutCubic: Easing.out(Easing.cubic),
|
|
126
|
+
easeInOutCubic: Easing.inOut(Easing.cubic)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((props: SwiperProps, ref): JSX.Element => {
|
|
130
|
+
const {
|
|
131
|
+
'indicator-dots': showsPagination,
|
|
132
|
+
'indicator-color': dotColor = 'rgba(0, 0, 0, .3)',
|
|
133
|
+
'indicator-active-color': activeDotColor = '#000000',
|
|
134
|
+
'enable-var': enableVar = false,
|
|
135
|
+
'parent-font-size': parentFontSize,
|
|
136
|
+
'parent-width': parentWidth,
|
|
137
|
+
'parent-height': parentHeight,
|
|
138
|
+
'external-var-context': externalVarContext,
|
|
139
|
+
style = {}
|
|
140
|
+
} = props
|
|
141
|
+
const previousMargin = props['previous-margin'] ? parseInt(props['previous-margin']) : 0
|
|
142
|
+
const nextMargin = props['next-margin'] ? parseInt(props['next-margin']) : 0
|
|
143
|
+
const easeingFunc = props['easing-function'] || 'default'
|
|
144
|
+
const easeDuration = props.duration || 500
|
|
145
|
+
const horizontal = props.vertical !== undefined ? !props.vertical : true
|
|
146
|
+
// 默认前后补位的元素个数
|
|
147
|
+
const patchElementNum = props.circular ? (previousMargin ? 2 : 1) : 0
|
|
148
|
+
const nodeRef = useRef<View>(null)
|
|
149
|
+
useNodesRef<View, SwiperProps>(props, ref, nodeRef, {})
|
|
150
|
+
|
|
151
|
+
// 计算transfrom之类的
|
|
152
|
+
const {
|
|
153
|
+
normalStyle,
|
|
154
|
+
hasVarDec,
|
|
155
|
+
varContextRef,
|
|
156
|
+
hasSelfPercent,
|
|
157
|
+
setWidth,
|
|
158
|
+
setHeight
|
|
159
|
+
} = useTransformStyle(style, {
|
|
160
|
+
enableVar,
|
|
161
|
+
externalVarContext,
|
|
162
|
+
parentFontSize,
|
|
163
|
+
parentWidth,
|
|
164
|
+
parentHeight
|
|
165
|
+
})
|
|
166
|
+
const { textStyle } = splitStyle(normalStyle)
|
|
167
|
+
const { textProps } = splitProps(props)
|
|
168
|
+
const children = Array.isArray(props.children) ? props.children.filter(child => child) : (props.children ? [props.children] : [])
|
|
169
|
+
const initWidth = typeof normalStyle?.width === 'number' ? normalStyle.width - previousMargin - nextMargin : normalStyle.width
|
|
170
|
+
const initHeight = typeof normalStyle?.height === 'number' ? normalStyle.height - previousMargin - nextMargin : normalStyle.height
|
|
171
|
+
const dir = useSharedValue(horizontal === false ? 'y' : 'x')
|
|
172
|
+
const pstep = dir.value === 'x' ? initWidth : initHeight
|
|
173
|
+
const initStep: number = isNaN(pstep) ? 0 : pstep
|
|
174
|
+
// 每个元素的宽度 or 高度
|
|
175
|
+
const step = useSharedValue(initStep)
|
|
176
|
+
const totalElements = useSharedValue(children.length)
|
|
177
|
+
// 记录选中元素的索引值
|
|
178
|
+
const currentIndex = useSharedValue(0)
|
|
179
|
+
// 记录元素的偏移量
|
|
180
|
+
const offset = useSharedValue(0)
|
|
181
|
+
const strAbso = 'absolute' + dir.value.toUpperCase() as StrAbsoType
|
|
182
|
+
const arrPages: Array<ReactNode> | ReactNode = renderItems()
|
|
183
|
+
// 标识手指触摸和抬起, 起点在onBegin
|
|
184
|
+
const touchfinish = useSharedValue(true)
|
|
185
|
+
// 记录上一帧的绝对定位坐标
|
|
186
|
+
const preAbsolutePos = useSharedValue(0)
|
|
187
|
+
// 记录从onBegin 到 onTouchesUp 时移动的距离
|
|
188
|
+
const moveTranstion = useSharedValue(0)
|
|
189
|
+
// 记录从onBegin 到 onTouchesUp 的时间
|
|
190
|
+
const moveTime = useSharedValue(0)
|
|
191
|
+
const timerId = useRef(0 as number | ReturnType<typeof setTimeout>)
|
|
192
|
+
// 用户点击未移动状态下,记录用户上一次操作的transtion 的 direction
|
|
193
|
+
const customTrans = useSharedValue(0)
|
|
194
|
+
const intervalTimer = props.interval || 500
|
|
195
|
+
totalElements.value = children.length
|
|
196
|
+
const {
|
|
197
|
+
// 存储layout布局信息
|
|
198
|
+
layoutRef,
|
|
199
|
+
layoutProps,
|
|
200
|
+
layoutStyle
|
|
201
|
+
} = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout })
|
|
202
|
+
|
|
203
|
+
const innerProps = useInnerProps(props, {
|
|
204
|
+
ref: nodeRef
|
|
205
|
+
}, [
|
|
206
|
+
'style',
|
|
207
|
+
'indicator-dots',
|
|
208
|
+
'indicator-color',
|
|
209
|
+
'indicator-active-color',
|
|
210
|
+
'previous-margin',
|
|
211
|
+
'vertical',
|
|
212
|
+
'previous-margin',
|
|
213
|
+
'next-margin',
|
|
214
|
+
'easing-function',
|
|
215
|
+
'autoplay',
|
|
216
|
+
'circular',
|
|
217
|
+
'interval',
|
|
218
|
+
'easing-function'
|
|
219
|
+
], { layoutRef: layoutRef })
|
|
220
|
+
|
|
221
|
+
function onWrapperLayout (e: LayoutChangeEvent) {
|
|
222
|
+
const { width, height } = e.nativeEvent.layout
|
|
223
|
+
const realWidth = dir.value === 'x' ? width - previousMargin - nextMargin : width
|
|
224
|
+
const realHeight = dir.value === 'y' ? height - previousMargin - nextMargin : height
|
|
225
|
+
step.value = dir.value === 'x' ? realWidth : realHeight
|
|
226
|
+
if (touchfinish.value) {
|
|
227
|
+
runOnUI(() => {
|
|
228
|
+
offset.value = getOffset(currentIndex.value)
|
|
229
|
+
})()
|
|
230
|
+
pauseLoop()
|
|
231
|
+
resumeLoop()
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const dotAnimatedStyle = useAnimatedStyle(() => {
|
|
236
|
+
if (!step.value) return {}
|
|
237
|
+
const dotStep = dotCommonStyle.width + dotCommonStyle.marginRight + dotCommonStyle.marginLeft
|
|
238
|
+
if (dir.value === 'x') {
|
|
239
|
+
return { transform: [{ translateX: currentIndex.value * dotStep }] }
|
|
240
|
+
} else {
|
|
241
|
+
return { transform: [{ translateY: currentIndex.value * dotStep }] }
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
function renderPagination () {
|
|
246
|
+
if (totalElements.value <= 1) return null
|
|
247
|
+
const activeColor = activeDotColor || '#007aff'
|
|
248
|
+
const unActionColor = dotColor || 'rgba(0,0,0,.2)'
|
|
249
|
+
// 正常渲染所有dots
|
|
250
|
+
const dots: Array<ReactNode> = []
|
|
251
|
+
for (let i = 0; i < totalElements.value; i++) {
|
|
252
|
+
dots.push(<View style={[dotCommonStyle, { backgroundColor: unActionColor }]} key={i}></View>)
|
|
253
|
+
}
|
|
254
|
+
return (
|
|
255
|
+
<View pointerEvents="none" style = {styles['pagination_' + dir.value]}>
|
|
256
|
+
<View style = {[styles['pagerWrapper' + dir.value]]}>
|
|
257
|
+
<Animated.View style={[
|
|
258
|
+
dotCommonStyle,
|
|
259
|
+
activeDotStyle,
|
|
260
|
+
{
|
|
261
|
+
backgroundColor: activeColor,
|
|
262
|
+
position: 'absolute',
|
|
263
|
+
left: 0,
|
|
264
|
+
top: 0
|
|
265
|
+
},
|
|
266
|
+
dotAnimatedStyle
|
|
267
|
+
]}
|
|
268
|
+
/>
|
|
269
|
+
{dots}
|
|
270
|
+
</View>
|
|
271
|
+
</View>)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function renderItems () {
|
|
275
|
+
const itemAnimatedStyles = useAnimatedStyle(() => {
|
|
276
|
+
return dir.value === 'x' ? { width: step.value, height: '100%' } : { width: '100%', height: step.value }
|
|
277
|
+
})
|
|
278
|
+
let renderChild = children.slice()
|
|
279
|
+
if (props.circular && totalElements.value > 1) {
|
|
280
|
+
// 最前面加最后一个元素
|
|
281
|
+
const lastChild = React.cloneElement(children[totalElements.value - 1] as ReactElement)
|
|
282
|
+
// 最后面加第一个元素
|
|
283
|
+
const firstChild = React.cloneElement(children[0] as ReactElement)
|
|
284
|
+
if (previousMargin) {
|
|
285
|
+
const lastChild1 = React.cloneElement(children[totalElements.value - 2] as ReactElement)
|
|
286
|
+
const firstChild1 = React.cloneElement(children[1] as ReactElement)
|
|
287
|
+
renderChild = [lastChild1, lastChild].concat(renderChild).concat([firstChild, firstChild1])
|
|
288
|
+
} else {
|
|
289
|
+
renderChild = [lastChild].concat(renderChild).concat([firstChild])
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const arrChilds = renderChild.map((child, index) => {
|
|
293
|
+
const extraStyle = {} as { [key: string]: any }
|
|
294
|
+
if (index === 0 && !props.circular) {
|
|
295
|
+
previousMargin && dir.value === 'x' && (extraStyle.marginLeft = previousMargin)
|
|
296
|
+
previousMargin && dir.value === 'y' && (extraStyle.marginTop = previousMargin)
|
|
297
|
+
}
|
|
298
|
+
if (index === totalElements.value - 1 && !props.circular) {
|
|
299
|
+
nextMargin && dir.value === 'x' && (extraStyle.marginRight = nextMargin)
|
|
300
|
+
nextMargin && dir.value === 'y' && (extraStyle.marginBottom = nextMargin)
|
|
301
|
+
}
|
|
302
|
+
const newChild = React.cloneElement(child, {
|
|
303
|
+
itemIndex: index,
|
|
304
|
+
customStyle: [itemAnimatedStyles, extraStyle],
|
|
305
|
+
key: 'page' + index
|
|
306
|
+
})
|
|
307
|
+
return newChild
|
|
308
|
+
})
|
|
309
|
+
const contextValue = {
|
|
310
|
+
offset,
|
|
311
|
+
step,
|
|
312
|
+
scale: props.scale
|
|
313
|
+
}
|
|
314
|
+
return (<SwiperContext.Provider value={contextValue}>{arrChilds}</SwiperContext.Provider>)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function createAutoPlay () {
|
|
318
|
+
let targetOffset = 0
|
|
319
|
+
let nextIndex = currentIndex.value
|
|
320
|
+
if (!props.circular) {
|
|
321
|
+
// 获取下一个位置的坐标, 循环到最后一个元素,直接停止, 取消定时器
|
|
322
|
+
if (currentIndex.value === totalElements.value - 1) {
|
|
323
|
+
pauseLoop()
|
|
324
|
+
return
|
|
325
|
+
}
|
|
326
|
+
nextIndex += 1
|
|
327
|
+
targetOffset = -nextIndex * step.value - previousMargin
|
|
328
|
+
offset.value = withTiming(targetOffset, {
|
|
329
|
+
duration: easeDuration,
|
|
330
|
+
easing: easeMap[easeingFunc]
|
|
331
|
+
}, () => {
|
|
332
|
+
currentIndex.value = nextIndex
|
|
333
|
+
runOnJS(loop)()
|
|
334
|
+
})
|
|
335
|
+
} else {
|
|
336
|
+
// 默认向右, 向下
|
|
337
|
+
if (nextIndex === totalElements.value - 1) {
|
|
338
|
+
nextIndex = 0
|
|
339
|
+
targetOffset = -(totalElements.value + patchElementNum) * step.value + previousMargin
|
|
340
|
+
// 执行动画到下一帧
|
|
341
|
+
offset.value = withTiming(targetOffset, {
|
|
342
|
+
duration: easeDuration
|
|
343
|
+
}, () => {
|
|
344
|
+
const initOffset = -step.value * patchElementNum + previousMargin
|
|
345
|
+
// 将开始位置设置为真正的位置
|
|
346
|
+
offset.value = initOffset
|
|
347
|
+
currentIndex.value = nextIndex
|
|
348
|
+
runOnJS(loop)()
|
|
349
|
+
})
|
|
350
|
+
} else {
|
|
351
|
+
nextIndex = currentIndex.value + 1
|
|
352
|
+
targetOffset = -(nextIndex + patchElementNum) * step.value + previousMargin
|
|
353
|
+
// 执行动画到下一帧
|
|
354
|
+
offset.value = withTiming(targetOffset, {
|
|
355
|
+
duration: easeDuration,
|
|
356
|
+
easing: easeMap[easeingFunc]
|
|
357
|
+
}, () => {
|
|
358
|
+
currentIndex.value = nextIndex
|
|
359
|
+
runOnJS(loop)()
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function handleSwiperChange (current: number) {
|
|
366
|
+
if (props.current !== currentIndex.value) {
|
|
367
|
+
const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef })
|
|
368
|
+
props.bindchange && props.bindchange(eventData)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function getOffset (index?: number) {
|
|
373
|
+
'worklet'
|
|
374
|
+
if (!step.value) return 0
|
|
375
|
+
let targetOffset = 0
|
|
376
|
+
if (props.circular && totalElements.value > 1) {
|
|
377
|
+
const targetIndex = (index || props.current || 0) + patchElementNum
|
|
378
|
+
targetOffset = -(step.value * targetIndex - previousMargin)
|
|
379
|
+
} else {
|
|
380
|
+
targetOffset = -(index || props?.current || 0) * step.value
|
|
381
|
+
}
|
|
382
|
+
return targetOffset
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function loop () {
|
|
386
|
+
timerId.current = setTimeout(() => {
|
|
387
|
+
createAutoPlay()
|
|
388
|
+
}, intervalTimer)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function pauseLoop () {
|
|
392
|
+
timerId.current && clearTimeout(timerId.current)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function resumeLoop () {
|
|
396
|
+
if (props.autoplay) {
|
|
397
|
+
loop()
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
useAnimatedReaction(() => currentIndex.value, (newIndex, preIndex) => {
|
|
402
|
+
// 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
|
|
403
|
+
const isInit = !preIndex && newIndex === 0
|
|
404
|
+
if (!isInit && newIndex !== preIndex && props.bindchange) {
|
|
405
|
+
runOnJS(handleSwiperChange)(newIndex)
|
|
406
|
+
}
|
|
407
|
+
})
|
|
408
|
+
useEffect(() => {
|
|
409
|
+
if (!step.value) {
|
|
410
|
+
return
|
|
411
|
+
}
|
|
412
|
+
const targetOffset = getOffset()
|
|
413
|
+
if (props.current !== undefined && props.current !== currentIndex.value) {
|
|
414
|
+
offset.value = withTiming(targetOffset, {
|
|
415
|
+
duration: easeDuration,
|
|
416
|
+
easing: easeMap[easeingFunc]
|
|
417
|
+
}, () => {
|
|
418
|
+
currentIndex.value = props.current || 0
|
|
419
|
+
})
|
|
420
|
+
}
|
|
421
|
+
}, [props.current])
|
|
422
|
+
|
|
423
|
+
useEffect(() => {
|
|
424
|
+
if (!step.value) {
|
|
425
|
+
return
|
|
426
|
+
}
|
|
427
|
+
if (props.autoplay) {
|
|
428
|
+
resumeLoop()
|
|
429
|
+
} else {
|
|
430
|
+
pauseLoop()
|
|
431
|
+
}
|
|
432
|
+
return () => {
|
|
433
|
+
if (props.autoplay) {
|
|
434
|
+
pauseLoop()
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}, [props.autoplay])
|
|
438
|
+
|
|
439
|
+
function getTargetPosition (eventData: EventDataType) {
|
|
440
|
+
'worklet'
|
|
441
|
+
// 移动的距离
|
|
442
|
+
const { translation } = eventData
|
|
443
|
+
let resetOffsetPos = 0
|
|
444
|
+
let selectedIndex = currentIndex.value
|
|
445
|
+
// 是否临界点
|
|
446
|
+
let isCriticalItem = false
|
|
447
|
+
// 真实滚动到的偏移量坐标
|
|
448
|
+
let moveToTargetPos = 0
|
|
449
|
+
const currentOffset = translation < 0 ? offset.value - previousMargin : offset.value + previousMargin
|
|
450
|
+
const computedIndex = Math.abs(currentOffset) / step.value
|
|
451
|
+
const moveToIndex = translation < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex)
|
|
452
|
+
// 实际应该定位的索引值
|
|
453
|
+
if (!props.circular) {
|
|
454
|
+
selectedIndex = moveToIndex
|
|
455
|
+
moveToTargetPos = selectedIndex * step.value
|
|
456
|
+
} else {
|
|
457
|
+
if (moveToIndex >= totalElements.value + patchElementNum) {
|
|
458
|
+
selectedIndex = moveToIndex - (totalElements.value + patchElementNum)
|
|
459
|
+
resetOffsetPos = (selectedIndex + patchElementNum) * step.value - previousMargin
|
|
460
|
+
moveToTargetPos = moveToIndex * step.value - previousMargin
|
|
461
|
+
isCriticalItem = true
|
|
462
|
+
} else if (moveToIndex <= patchElementNum - 1) {
|
|
463
|
+
selectedIndex = moveToIndex === 0 ? totalElements.value - patchElementNum : totalElements.value - 1
|
|
464
|
+
resetOffsetPos = (selectedIndex + patchElementNum) * step.value - previousMargin
|
|
465
|
+
moveToTargetPos = moveToIndex * step.value - previousMargin
|
|
466
|
+
isCriticalItem = true
|
|
467
|
+
} else {
|
|
468
|
+
selectedIndex = moveToIndex - patchElementNum
|
|
469
|
+
moveToTargetPos = moveToIndex * step.value - previousMargin
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
selectedIndex,
|
|
474
|
+
isCriticalItem,
|
|
475
|
+
resetOffset: -resetOffsetPos,
|
|
476
|
+
targetOffset: -moveToTargetPos
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function canMove (eventData: EventDataType) {
|
|
481
|
+
'worklet'
|
|
482
|
+
const { translation } = eventData
|
|
483
|
+
const currentOffset = Math.abs(offset.value)
|
|
484
|
+
if (!props.circular) {
|
|
485
|
+
if (translation < 0) {
|
|
486
|
+
return currentOffset < step.value * (totalElements.value - 1)
|
|
487
|
+
} else {
|
|
488
|
+
return currentOffset > 0
|
|
489
|
+
}
|
|
490
|
+
} else {
|
|
491
|
+
return true
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function handleEnd (eventData: EventDataType) {
|
|
496
|
+
'worklet'
|
|
497
|
+
const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(eventData)
|
|
498
|
+
if (isCriticalItem) {
|
|
499
|
+
offset.value = withTiming(targetOffset, {
|
|
500
|
+
duration: easeDuration,
|
|
501
|
+
easing: easeMap[easeingFunc]
|
|
502
|
+
}, () => {
|
|
503
|
+
if (touchfinish.value !== false) {
|
|
504
|
+
currentIndex.value = selectedIndex
|
|
505
|
+
offset.value = resetOffset
|
|
506
|
+
runOnJS(resumeLoop)()
|
|
507
|
+
}
|
|
508
|
+
})
|
|
509
|
+
} else {
|
|
510
|
+
offset.value = withTiming(targetOffset, {
|
|
511
|
+
duration: easeDuration,
|
|
512
|
+
easing: easeMap[easeingFunc]
|
|
513
|
+
}, () => {
|
|
514
|
+
if (touchfinish.value !== false) {
|
|
515
|
+
currentIndex.value = selectedIndex
|
|
516
|
+
runOnJS(resumeLoop)()
|
|
517
|
+
}
|
|
518
|
+
})
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function handleBack (eventData: EventDataType) {
|
|
523
|
+
'worklet'
|
|
524
|
+
const { translation } = eventData
|
|
525
|
+
// 向右滑动的back:trans < 0, 向左滑动的back: trans < 0
|
|
526
|
+
let currentOffset = Math.abs(offset.value)
|
|
527
|
+
if (props.circular) {
|
|
528
|
+
currentOffset += translation < 0 ? previousMargin : -previousMargin
|
|
529
|
+
}
|
|
530
|
+
const curIndex = currentOffset / step.value
|
|
531
|
+
const moveToIndex = (translation < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElementNum
|
|
532
|
+
const targetOffset = -(moveToIndex + patchElementNum) * step.value + (props.circular ? -previousMargin : 0)
|
|
533
|
+
offset.value = withTiming(targetOffset, {
|
|
534
|
+
duration: easeDuration,
|
|
535
|
+
easing: easeMap[easeingFunc]
|
|
536
|
+
}, () => {
|
|
537
|
+
if (touchfinish.value !== false) {
|
|
538
|
+
currentIndex.value = moveToIndex
|
|
539
|
+
runOnJS(resumeLoop)()
|
|
540
|
+
}
|
|
541
|
+
})
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function handleLongPress (eventData: EventDataType) {
|
|
545
|
+
'worklet'
|
|
546
|
+
const { translation } = eventData
|
|
547
|
+
let currentOffset = Math.abs(offset.value)
|
|
548
|
+
if (props.circular) {
|
|
549
|
+
currentOffset += previousMargin
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const half = currentOffset % step.value > step.value / 2
|
|
553
|
+
// 向右trans < 0, 向左trans > 0
|
|
554
|
+
const isExceedHalf = translation < 0 ? half : !half
|
|
555
|
+
if (+translation === 0) {
|
|
556
|
+
runOnJS(resumeLoop)()
|
|
557
|
+
} else if (isExceedHalf) {
|
|
558
|
+
handleEnd(eventData)
|
|
559
|
+
} else {
|
|
560
|
+
handleBack(eventData)
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function reachBoundary (eventData: EventDataType) {
|
|
565
|
+
'worklet'
|
|
566
|
+
// 移动的距离
|
|
567
|
+
const { translation } = eventData
|
|
568
|
+
const elementsLength = step.value * totalElements.value
|
|
569
|
+
|
|
570
|
+
let isBoundary = false
|
|
571
|
+
let resetOffset = 0
|
|
572
|
+
// Y轴向下滚动, transDistance > 0, 向上滚动 < 0 X轴向左滚动, transDistance > 0
|
|
573
|
+
const currentOffset = offset.value
|
|
574
|
+
const moveStep = Math.ceil(translation / elementsLength)
|
|
575
|
+
if (translation < 0) {
|
|
576
|
+
const posEnd = (totalElements.value + patchElementNum + 1) * step.value
|
|
577
|
+
const posReverseEnd = (patchElementNum - 1) * step.value
|
|
578
|
+
if (currentOffset < -posEnd + step.value) {
|
|
579
|
+
isBoundary = true
|
|
580
|
+
resetOffset = Math.abs(moveStep) === 0 ? patchElementNum * step.value + translation : moveStep * elementsLength
|
|
581
|
+
}
|
|
582
|
+
if (currentOffset > -posReverseEnd) {
|
|
583
|
+
isBoundary = true
|
|
584
|
+
resetOffset = moveStep * elementsLength
|
|
585
|
+
}
|
|
586
|
+
} else if (translation > 0) {
|
|
587
|
+
const posEnd = (patchElementNum - 1) * step.value
|
|
588
|
+
const posReverseEnd = (patchElementNum + totalElements.value) * step.value
|
|
589
|
+
if (currentOffset > -posEnd) {
|
|
590
|
+
isBoundary = true
|
|
591
|
+
resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? translation : 0)
|
|
592
|
+
}
|
|
593
|
+
if (currentOffset < -posReverseEnd) {
|
|
594
|
+
isBoundary = true
|
|
595
|
+
resetOffset = moveStep * elementsLength + patchElementNum * step.value
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return {
|
|
599
|
+
isBoundary,
|
|
600
|
+
resetOffset: -resetOffset
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const gestureHandler = Gesture.Pan()
|
|
605
|
+
.onBegin((e) => {
|
|
606
|
+
'worklet'
|
|
607
|
+
touchfinish.value = false
|
|
608
|
+
cancelAnimation(offset)
|
|
609
|
+
runOnJS(pauseLoop)()
|
|
610
|
+
preAbsolutePos.value = e[strAbso]
|
|
611
|
+
moveTranstion.value = e[strAbso]
|
|
612
|
+
moveTime.value = new Date().getTime()
|
|
613
|
+
})
|
|
614
|
+
.onTouchesMove((e) => {
|
|
615
|
+
'worklet'
|
|
616
|
+
const touchEventData = e.changedTouches[0]
|
|
617
|
+
const moveDistance = touchEventData[strAbso] - preAbsolutePos.value
|
|
618
|
+
customTrans.value = moveDistance
|
|
619
|
+
const eventData = {
|
|
620
|
+
translation: moveDistance
|
|
621
|
+
}
|
|
622
|
+
// 处理用户一直拖拽到临界点的场景, 不会执行onEnd
|
|
623
|
+
if (!props.circular && !canMove(eventData)) {
|
|
624
|
+
return
|
|
625
|
+
}
|
|
626
|
+
const { isBoundary, resetOffset } = reachBoundary(eventData)
|
|
627
|
+
if (isBoundary && props.circular && !touchfinish.value) {
|
|
628
|
+
offset.value = resetOffset
|
|
629
|
+
} else {
|
|
630
|
+
offset.value = moveDistance + offset.value
|
|
631
|
+
}
|
|
632
|
+
preAbsolutePos.value = touchEventData[strAbso]
|
|
633
|
+
})
|
|
634
|
+
.onTouchesUp((e) => {
|
|
635
|
+
'worklet'
|
|
636
|
+
const touchEventData = e.changedTouches[0]
|
|
637
|
+
const moveDistance = touchEventData[strAbso] - moveTranstion.value
|
|
638
|
+
touchfinish.value = true
|
|
639
|
+
const eventData = {
|
|
640
|
+
translation: moveDistance
|
|
641
|
+
}
|
|
642
|
+
// 用户手指按下起来, 需要计算正确的位置, 比如在滑动过程中突然按下然后起来,需要计算到正确的位置
|
|
643
|
+
if (!props.circular && !canMove(eventData)) {
|
|
644
|
+
return
|
|
645
|
+
}
|
|
646
|
+
if (!moveDistance) {
|
|
647
|
+
handleLongPress({ translation: customTrans.value })
|
|
648
|
+
} else {
|
|
649
|
+
const strVelocity = moveDistance / (new Date().getTime() - moveTime.value) * 1000
|
|
650
|
+
if (Math.abs(strVelocity) < longPressRatio) {
|
|
651
|
+
handleLongPress(eventData)
|
|
652
|
+
} else {
|
|
653
|
+
handleEnd(eventData)
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
})
|
|
657
|
+
|
|
658
|
+
const animatedStyles = useAnimatedStyle(() => {
|
|
659
|
+
if (dir.value === 'x') {
|
|
660
|
+
return { transform: [{ translateX: offset.value }], opacity: step.value > 0 ? 1 : 0 }
|
|
661
|
+
} else {
|
|
662
|
+
return { transform: [{ translateY: offset.value }], opacity: step.value > 0 ? 1 : 0 }
|
|
663
|
+
}
|
|
664
|
+
})
|
|
665
|
+
|
|
666
|
+
function renderSwiper () {
|
|
667
|
+
return (<View style={[normalStyle, layoutStyle, styles.swiper]} {...layoutProps} {...innerProps}>
|
|
668
|
+
<Animated.View style={[{
|
|
669
|
+
flexDirection: dir.value === 'x' ? 'row' : 'column',
|
|
670
|
+
width: '100%',
|
|
671
|
+
height: '100%'
|
|
672
|
+
}, animatedStyles]}>
|
|
673
|
+
{wrapChildren({
|
|
674
|
+
children: arrPages
|
|
675
|
+
}, {
|
|
676
|
+
hasVarDec,
|
|
677
|
+
varContext: varContextRef.current,
|
|
678
|
+
textStyle,
|
|
679
|
+
textProps
|
|
680
|
+
})}
|
|
681
|
+
</Animated.View>
|
|
682
|
+
{showsPagination && renderPagination()}
|
|
683
|
+
</View>)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (totalElements.value === 1) {
|
|
687
|
+
return renderSwiper()
|
|
688
|
+
} else {
|
|
689
|
+
return (<GestureDetector gesture={gestureHandler}>
|
|
690
|
+
{renderSwiper()}
|
|
691
|
+
</GestureDetector>)
|
|
692
|
+
}
|
|
693
|
+
})
|
|
694
|
+
SwiperWrapper.displayName = 'MpxSwiperWrapper'
|
|
695
|
+
|
|
696
|
+
export default SwiperWrapper
|