@mpxjs/webpack-plugin 2.10.11 → 2.10.13
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/DynamicEntryDependency.js +9 -6
- package/lib/dependencies/ImportDependencyTemplate.js +12 -0
- package/lib/index.js +21 -2
- package/lib/react/LoadAsyncChunkModule.js +1 -1
- package/lib/react/processScript.js +5 -2
- package/lib/retry-runtime-module.js +56 -0
- package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +13 -4
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +154 -74
- package/lib/runtime/components/react/dist/useAnimationHooks.js +8 -1
- package/lib/runtime/components/react/dist/utils.jsx +17 -6
- package/lib/runtime/components/react/mpx-async-suspense.tsx +14 -4
- package/lib/runtime/components/react/mpx-swiper.tsx +159 -78
- package/lib/runtime/components/react/types/global.d.ts +2 -0
- package/lib/runtime/components/react/useAnimationHooks.ts +8 -1
- package/lib/runtime/components/react/utils.tsx +20 -6
- package/lib/web/processMainScript.js +1 -1
- package/lib/web/processScript.js +6 -2
- package/lib/web/script-helper.js +15 -10
- package/package.json +4 -4
|
@@ -5,7 +5,6 @@ import { VarContext, ScrollViewContext, RouteContext } from './context';
|
|
|
5
5
|
import { ExpressionParser, parseFunc, ReplaceSource } from './parser';
|
|
6
6
|
import { initialWindowMetrics } from 'react-native-safe-area-context';
|
|
7
7
|
import FastImage from '@d11/react-native-fast-image';
|
|
8
|
-
import { runOnJS } from 'react-native-reanimated';
|
|
9
8
|
import { Gesture } from 'react-native-gesture-handler';
|
|
10
9
|
export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/;
|
|
11
10
|
export const PERCENT_REGEX = /^\s*-?\d+(\.\d+)?%\s*$/;
|
|
@@ -663,13 +662,11 @@ export function useHover({ enableHover, hoverStartTime, hoverStayTime, disabled
|
|
|
663
662
|
const gesture = useMemo(() => {
|
|
664
663
|
return Gesture.Pan()
|
|
665
664
|
.onTouchesDown(() => {
|
|
666
|
-
|
|
667
|
-
runOnJS(setStartTimer)();
|
|
665
|
+
setStartTimer();
|
|
668
666
|
})
|
|
669
667
|
.onTouchesUp(() => {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
});
|
|
668
|
+
setStayTimer();
|
|
669
|
+
}).runOnJS(true);
|
|
673
670
|
}, []);
|
|
674
671
|
if (gestureRef) {
|
|
675
672
|
gesture.simultaneousWithExternalGesture(gestureRef);
|
|
@@ -679,3 +676,17 @@ export function useHover({ enableHover, hoverStartTime, hoverStayTime, disabled
|
|
|
679
676
|
gesture
|
|
680
677
|
};
|
|
681
678
|
}
|
|
679
|
+
export function useRunOnJSCallback(callbackMapRef) {
|
|
680
|
+
const invokeCallback = useCallback((key, ...args) => {
|
|
681
|
+
const callback = callbackMapRef.current[key];
|
|
682
|
+
// eslint-disable-next-line node/no-callback-literal
|
|
683
|
+
if (isFunction(callback))
|
|
684
|
+
return callback(...args);
|
|
685
|
+
}, []);
|
|
686
|
+
useEffect(() => {
|
|
687
|
+
return () => {
|
|
688
|
+
callbackMapRef.current = {};
|
|
689
|
+
};
|
|
690
|
+
}, []);
|
|
691
|
+
return invokeCallback;
|
|
692
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState, ComponentType, useEffect, useCallback, useRef, ReactNode, createElement } from 'react'
|
|
2
2
|
import { View, Image, StyleSheet, Text, TouchableOpacity } from 'react-native'
|
|
3
3
|
import FastImage from '@d11/react-native-fast-image'
|
|
4
|
+
import { AnyFunc } from './types/common'
|
|
4
5
|
|
|
5
6
|
const asyncChunkMap = new Map()
|
|
6
7
|
|
|
@@ -136,10 +137,19 @@ const AsyncSuspense: React.FC<AsyncSuspenseProps> = ({
|
|
|
136
137
|
.catch((e) => {
|
|
137
138
|
if (cancelled) return
|
|
138
139
|
if (type === 'component') {
|
|
139
|
-
global.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
global.__mpxAppCbs.lazyLoad.forEach((cb: AnyFunc) => {
|
|
141
|
+
// eslint-disable-next-line node/no-callback-literal
|
|
142
|
+
cb({
|
|
143
|
+
type: 'subpackage',
|
|
144
|
+
subpackage: [chunkName],
|
|
145
|
+
errMsg: `loadSubpackage: ${e.type}`
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
if (type === 'page' && typeof mpxGlobal.__mpx.config?.rnConfig?.lazyLoadPageErrorHandler === 'function') {
|
|
150
|
+
mpxGlobal.__mpx.config.rnConfig.lazyLoadPageErrorHandler({
|
|
151
|
+
subpackage: chunkName,
|
|
152
|
+
errType: e.type
|
|
143
153
|
})
|
|
144
154
|
}
|
|
145
155
|
loadChunkPromise.current = null
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { View, NativeSyntheticEvent, LayoutChangeEvent } from 'react-native'
|
|
2
|
-
import { GestureDetector, Gesture, PanGesture } from 'react-native-gesture-handler'
|
|
2
|
+
import { GestureDetector, Gesture, PanGesture, GestureStateChangeEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler'
|
|
3
3
|
import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation } from 'react-native-reanimated'
|
|
4
4
|
|
|
5
5
|
import React, { JSX, forwardRef, useRef, useEffect, ReactNode, ReactElement, useMemo, createElement } from 'react'
|
|
@@ -26,8 +26,12 @@ import Portal from './mpx-portal'
|
|
|
26
26
|
*/
|
|
27
27
|
type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'
|
|
28
28
|
type StrAbsoType = 'absoluteX' | 'absoluteY'
|
|
29
|
+
type StrVelocityType = 'velocityX' | 'velocityY'
|
|
29
30
|
type EventDataType = {
|
|
31
|
+
// 和上一帧offset值的对比
|
|
30
32
|
translation: number
|
|
33
|
+
// onUpdate时根据上一个判断方向,onFinalize根据transformStart判断
|
|
34
|
+
transdir: number
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
interface SwiperProps {
|
|
@@ -46,7 +50,7 @@ interface SwiperProps {
|
|
|
46
50
|
vertical?: boolean
|
|
47
51
|
style: {
|
|
48
52
|
[key: string]: any
|
|
49
|
-
}
|
|
53
|
+
}
|
|
50
54
|
'easing-function'?: EaseType
|
|
51
55
|
'previous-margin'?: string
|
|
52
56
|
'next-margin'?: string
|
|
@@ -54,7 +58,7 @@ interface SwiperProps {
|
|
|
54
58
|
'enable-var': boolean
|
|
55
59
|
'parent-font-size'?: number
|
|
56
60
|
'parent-width'?: number
|
|
57
|
-
'parent-height'?: number
|
|
61
|
+
'parent-height'?: number
|
|
58
62
|
'external-var-context'?: Record<string, any>
|
|
59
63
|
'wait-for'?: Array<GestureHandler>
|
|
60
64
|
'simultaneous-handlers'?: Array<GestureHandler>
|
|
@@ -143,7 +147,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
143
147
|
style = {},
|
|
144
148
|
autoplay = false,
|
|
145
149
|
circular = false,
|
|
146
|
-
disableGesture = false
|
|
150
|
+
disableGesture = false,
|
|
151
|
+
bindchange
|
|
147
152
|
} = props
|
|
148
153
|
const easeingFunc = props['easing-function'] || 'default'
|
|
149
154
|
const easeDuration = props.duration || 500
|
|
@@ -198,14 +203,13 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
198
203
|
// 记录元素的偏移量
|
|
199
204
|
const offset = useSharedValue(getOffset(props.current || 0, initStep))
|
|
200
205
|
const strAbso = 'absolute' + dir.toUpperCase() as StrAbsoType
|
|
206
|
+
const strVelocity = 'velocity' + dir.toUpperCase() as StrVelocityType
|
|
201
207
|
// 标识手指触摸和抬起, 起点在onBegin
|
|
202
208
|
const touchfinish = useSharedValue(true)
|
|
203
209
|
// 记录上一帧的绝对定位坐标
|
|
204
210
|
const preAbsolutePos = useSharedValue(0)
|
|
205
211
|
// 记录从onBegin 到 onTouchesUp 时移动的距离
|
|
206
212
|
const moveTranstion = useSharedValue(0)
|
|
207
|
-
// 记录从onBegin 到 onTouchesUp 的时间
|
|
208
|
-
const moveTime = useSharedValue(0)
|
|
209
213
|
const timerId = useRef(0 as number | ReturnType<typeof setTimeout>)
|
|
210
214
|
const intervalTimer = props.interval || 500
|
|
211
215
|
|
|
@@ -427,7 +431,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
427
431
|
function handleSwiperChange (current: number) {
|
|
428
432
|
if (props.current !== currentIndex.value) {
|
|
429
433
|
const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef })
|
|
430
|
-
|
|
434
|
+
bindchange && bindchange(eventData)
|
|
431
435
|
}
|
|
432
436
|
}
|
|
433
437
|
|
|
@@ -469,7 +473,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
469
473
|
// 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
|
|
470
474
|
useAnimatedReaction(() => currentIndex.value, (newIndex: number, preIndex: number) => {
|
|
471
475
|
// 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
|
|
472
|
-
if (newIndex !== preIndex &&
|
|
476
|
+
if (newIndex !== preIndex && bindchange) {
|
|
473
477
|
runOnJS(handleSwiperChange)(newIndex)
|
|
474
478
|
}
|
|
475
479
|
})
|
|
@@ -504,7 +508,11 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
504
508
|
}, [children.length])
|
|
505
509
|
|
|
506
510
|
useEffect(() => {
|
|
507
|
-
|
|
511
|
+
// 1. 如果用户在touch的过程中, 外部更新了current以外部为准(小程序表现)
|
|
512
|
+
// 2. 手指滑动过程中更新索引,外部会把current再传入进来,导致offset直接更新,增加判断不同才更新
|
|
513
|
+
if (props.current !== currentIndex.value) {
|
|
514
|
+
updateCurrent(props.current || 0, step.value)
|
|
515
|
+
}
|
|
508
516
|
}, [props.current])
|
|
509
517
|
|
|
510
518
|
useEffect(() => {
|
|
@@ -528,7 +536,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
528
536
|
function getTargetPosition (eventData: EventDataType) {
|
|
529
537
|
'worklet'
|
|
530
538
|
// 移动的距离
|
|
531
|
-
const {
|
|
539
|
+
const { transdir } = eventData
|
|
532
540
|
let resetOffsetPos = 0
|
|
533
541
|
let selectedIndex = currentIndex.value
|
|
534
542
|
// 是否临界点
|
|
@@ -536,9 +544,9 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
536
544
|
// 真实滚动到的偏移量坐标
|
|
537
545
|
let moveToTargetPos = 0
|
|
538
546
|
const tmp = !circularShared.value ? 0 : preMarginShared.value
|
|
539
|
-
const currentOffset =
|
|
547
|
+
const currentOffset = transdir < 0 ? offset.value - tmp : offset.value + tmp
|
|
540
548
|
const computedIndex = Math.abs(currentOffset) / step.value
|
|
541
|
-
const moveToIndex =
|
|
549
|
+
const moveToIndex = transdir < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex)
|
|
542
550
|
// 实际应该定位的索引值
|
|
543
551
|
if (!circularShared.value) {
|
|
544
552
|
selectedIndex = moveToIndex
|
|
@@ -568,13 +576,17 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
568
576
|
}
|
|
569
577
|
function canMove (eventData: EventDataType) {
|
|
570
578
|
'worklet'
|
|
571
|
-
|
|
572
|
-
|
|
579
|
+
// 旧版:如果在快速多次滑动时,只根据当前的offset判断,会出现offset没超出,加上translation后越界的场景(如在倒数第二个元素快速滑动)
|
|
580
|
+
// 新版:会加上translation
|
|
581
|
+
const { translation, transdir } = eventData
|
|
582
|
+
const gestureMovePos = offset.value + translation
|
|
573
583
|
if (!circularShared.value) {
|
|
574
|
-
|
|
575
|
-
|
|
584
|
+
// 如果只判断区间,中间非滑动状态(handleResistanceMove)向左滑动,突然改为向右滑动,但是还在非滑动态,本应该可滑动判断为了不可滑动
|
|
585
|
+
const posEnd = -step.value * (childrenLength.value - 1)
|
|
586
|
+
if (transdir < 0) {
|
|
587
|
+
return gestureMovePos > posEnd
|
|
576
588
|
} else {
|
|
577
|
-
return
|
|
589
|
+
return gestureMovePos < 0
|
|
578
590
|
}
|
|
579
591
|
} else {
|
|
580
592
|
return true
|
|
@@ -606,25 +618,16 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
606
618
|
})
|
|
607
619
|
}
|
|
608
620
|
}
|
|
609
|
-
function handleBackInit () {
|
|
610
|
-
'worklet'
|
|
611
|
-
// 微信的效果
|
|
612
|
-
// 1. 只有一个元素,即使设置了circular,也不会产生循环的效果,2. 可以响应手势,但是会有回弹的效果
|
|
613
|
-
offset.value = withTiming(0, {
|
|
614
|
-
duration: easeDuration,
|
|
615
|
-
easing: easeMap[easeingFunc]
|
|
616
|
-
})
|
|
617
|
-
}
|
|
618
621
|
function handleBack (eventData: EventDataType) {
|
|
619
622
|
'worklet'
|
|
620
|
-
const {
|
|
623
|
+
const { transdir } = eventData
|
|
621
624
|
// 向右滑动的back:trans < 0, 向左滑动的back: trans < 0
|
|
622
625
|
let currentOffset = Math.abs(offset.value)
|
|
623
626
|
if (circularShared.value) {
|
|
624
|
-
currentOffset +=
|
|
627
|
+
currentOffset += transdir < 0 ? preMarginShared.value : -preMarginShared.value
|
|
625
628
|
}
|
|
626
629
|
const curIndex = currentOffset / step.value
|
|
627
|
-
const moveToIndex = (
|
|
630
|
+
const moveToIndex = (transdir < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElmNumShared.value
|
|
628
631
|
const targetOffset = -(moveToIndex + patchElmNumShared.value) * step.value + (circularShared.value ? preMarginShared.value : 0)
|
|
629
632
|
offset.value = withTiming(targetOffset, {
|
|
630
633
|
duration: easeDuration,
|
|
@@ -636,64 +639,108 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
636
639
|
}
|
|
637
640
|
})
|
|
638
641
|
}
|
|
639
|
-
|
|
642
|
+
// 当前的offset和index多对应的offset进行对比,判断是否超过一半
|
|
643
|
+
function computeHalf (eventData: EventDataType) {
|
|
640
644
|
'worklet'
|
|
645
|
+
const { transdir } = eventData
|
|
641
646
|
const currentOffset = Math.abs(offset.value)
|
|
642
647
|
let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value
|
|
643
648
|
if (circularShared.value) {
|
|
644
649
|
preOffset -= preMarginShared.value
|
|
645
650
|
}
|
|
646
|
-
// 正常事件中拿到的
|
|
651
|
+
// 正常事件中拿到的translation值(正向滑动<0,倒着滑>0)
|
|
647
652
|
const diffOffset = preOffset - currentOffset
|
|
648
653
|
const half = Math.abs(diffOffset) > step.value / 2
|
|
654
|
+
const isTriggerUpdateHalf = (transdir < 0 && currentOffset < preOffset) || (transdir > 0 && currentOffset > preOffset)
|
|
655
|
+
return {
|
|
656
|
+
diffOffset,
|
|
657
|
+
half,
|
|
658
|
+
isTriggerUpdateHalf
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
function handleLongPress (eventData: EventDataType) {
|
|
662
|
+
'worklet'
|
|
663
|
+
const { diffOffset, half, isTriggerUpdateHalf } = computeHalf(eventData)
|
|
649
664
|
if (+diffOffset === 0) {
|
|
650
665
|
runOnJS(resumeLoop)()
|
|
666
|
+
} else if (isTriggerUpdateHalf) {
|
|
667
|
+
// 如果触发了onUpdate时的索引变更,则直接以update时的index为准
|
|
668
|
+
const targetIndex = !circularShared.value ? currentIndex.value : currentIndex.value + patchElmNumShared.value - 1
|
|
669
|
+
offset.value = withTiming(-targetIndex * step.value, {
|
|
670
|
+
duration: easeDuration,
|
|
671
|
+
easing: easeMap[easeingFunc]
|
|
672
|
+
}, () => {
|
|
673
|
+
if (touchfinish.value !== false) {
|
|
674
|
+
currentIndex.value = targetIndex
|
|
675
|
+
runOnJS(resumeLoop)()
|
|
676
|
+
}
|
|
677
|
+
})
|
|
651
678
|
} else if (half) {
|
|
652
|
-
handleEnd(
|
|
679
|
+
handleEnd(eventData)
|
|
653
680
|
} else {
|
|
654
|
-
handleBack(
|
|
681
|
+
handleBack(eventData)
|
|
655
682
|
}
|
|
656
683
|
}
|
|
657
684
|
function reachBoundary (eventData: EventDataType) {
|
|
658
685
|
'worklet'
|
|
659
|
-
//
|
|
686
|
+
// 1. 基于当前的offset和translation判断是否超过当前边界值
|
|
660
687
|
const { translation } = eventData
|
|
661
|
-
const
|
|
688
|
+
const boundaryStart = -patchElmNumShared.value * step.value
|
|
689
|
+
const boundaryEnd = -(childrenLength.value + patchElmNumShared.value) * step.value
|
|
690
|
+
const moveToOffset = offset.value + translation
|
|
662
691
|
let isBoundary = false
|
|
663
692
|
let resetOffset = 0
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
} else if (translation > 0) {
|
|
679
|
-
const posEnd = (patchElmNumShared.value - 1) * step.value
|
|
680
|
-
const posReverseEnd = (patchElmNumShared.value + childrenLength.value) * step.value
|
|
681
|
-
if (currentOffset > -posEnd) {
|
|
682
|
-
isBoundary = true
|
|
683
|
-
resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? translation : 0)
|
|
684
|
-
}
|
|
685
|
-
if (currentOffset < -posReverseEnd) {
|
|
686
|
-
isBoundary = true
|
|
687
|
-
resetOffset = moveStep * elementsLength + patchElmNumShared.value * step.value
|
|
688
|
-
}
|
|
693
|
+
if (moveToOffset < boundaryEnd) {
|
|
694
|
+
isBoundary = true
|
|
695
|
+
// 超过边界的距离
|
|
696
|
+
const exceedLength = Math.abs(moveToOffset) - Math.abs(boundaryEnd)
|
|
697
|
+
// 计算对标正常元素所在的offset
|
|
698
|
+
resetOffset = patchElmNumShared.value * step.value + exceedLength
|
|
699
|
+
}
|
|
700
|
+
if (moveToOffset > boundaryStart) {
|
|
701
|
+
isBoundary = true
|
|
702
|
+
// 超过边界的距离
|
|
703
|
+
const exceedLength = Math.abs(boundaryStart) - Math.abs(moveToOffset)
|
|
704
|
+
// 计算对标正常元素所在的offset
|
|
705
|
+
resetOffset = (patchElmNumShared.value + childrenLength.value - 1) * step.value + (step.value - exceedLength)
|
|
689
706
|
}
|
|
690
707
|
return {
|
|
691
708
|
isBoundary,
|
|
692
709
|
resetOffset: -resetOffset
|
|
693
710
|
}
|
|
694
711
|
}
|
|
712
|
+
// 非循环超出边界,应用阻力; 开始滑动少阻力小,滑动越长阻力越大
|
|
713
|
+
function handleResistanceMove (eventData: EventDataType) {
|
|
714
|
+
'worklet'
|
|
715
|
+
const { translation, transdir } = eventData
|
|
716
|
+
const moveToOffset = offset.value + translation
|
|
717
|
+
const maxOverDrag = Math.floor(step.value / 2)
|
|
718
|
+
const maxOffset = translation < 0 ? -(childrenLength.value - 1) * step.value : 0
|
|
719
|
+
let resistance = 0.1
|
|
720
|
+
let overDrag = 0
|
|
721
|
+
let finalOffset = 0
|
|
722
|
+
// 向右向下小于0, 向左向上大于0;
|
|
723
|
+
if (transdir < 0) {
|
|
724
|
+
overDrag = Math.abs(moveToOffset - maxOffset)
|
|
725
|
+
} else {
|
|
726
|
+
overDrag = Math.abs(moveToOffset)
|
|
727
|
+
}
|
|
728
|
+
// 滑动越多resistance越小
|
|
729
|
+
resistance = 1 - overDrag / maxOverDrag
|
|
730
|
+
// 确保阻力在合理范围内
|
|
731
|
+
resistance = Math.min(0.5, resistance)
|
|
732
|
+
// 限制在最大拖拽范围内
|
|
733
|
+
if (transdir < 0) {
|
|
734
|
+
const adjustOffset = offset.value + translation * resistance
|
|
735
|
+
finalOffset = Math.max(adjustOffset, maxOffset - maxOverDrag)
|
|
736
|
+
} else {
|
|
737
|
+
const adjustOffset = offset.value + translation * resistance
|
|
738
|
+
finalOffset = Math.min(adjustOffset, maxOverDrag)
|
|
739
|
+
}
|
|
740
|
+
return finalOffset
|
|
741
|
+
}
|
|
695
742
|
const gesturePan = Gesture.Pan()
|
|
696
|
-
.onBegin((e) => {
|
|
743
|
+
.onBegin((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
|
|
697
744
|
'worklet'
|
|
698
745
|
if (!step.value) return
|
|
699
746
|
touchfinish.value = false
|
|
@@ -701,47 +748,81 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
701
748
|
runOnJS(pauseLoop)()
|
|
702
749
|
preAbsolutePos.value = e[strAbso]
|
|
703
750
|
moveTranstion.value = e[strAbso]
|
|
704
|
-
moveTime.value = new Date().getTime()
|
|
705
751
|
})
|
|
706
|
-
.
|
|
752
|
+
.onUpdate((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
|
|
707
753
|
'worklet'
|
|
708
754
|
if (touchfinish.value) return
|
|
709
|
-
const
|
|
710
|
-
const moveDistance = touchEventData[strAbso] - preAbsolutePos.value
|
|
755
|
+
const moveDistance = e[strAbso] - preAbsolutePos.value
|
|
711
756
|
const eventData = {
|
|
712
|
-
translation: moveDistance
|
|
757
|
+
translation: moveDistance,
|
|
758
|
+
transdir: moveDistance !== 0 ? moveDistance : e[strAbso] - moveTranstion.value
|
|
713
759
|
}
|
|
714
|
-
//
|
|
715
|
-
|
|
760
|
+
// 1. 支持滑动中超出一半更新索引的能力:只更新索引并不会影响onFinalize依据当前offset计算的索引
|
|
761
|
+
const { half } = computeHalf(eventData)
|
|
762
|
+
if (childrenLength.value > 1 && half) {
|
|
763
|
+
const { selectedIndex } = getTargetPosition(eventData)
|
|
764
|
+
currentIndex.value = selectedIndex
|
|
765
|
+
}
|
|
766
|
+
// 2. 非循环: 处理用户一直拖拽到临界点的场景,如果放到onFinalize无法阻止offset.value更新为越界的值
|
|
767
|
+
if (!circularShared.value) {
|
|
768
|
+
if (canMove(eventData)) {
|
|
769
|
+
offset.value = moveDistance + offset.value
|
|
770
|
+
} else {
|
|
771
|
+
const finalOffset = handleResistanceMove(eventData)
|
|
772
|
+
offset.value = finalOffset
|
|
773
|
+
}
|
|
774
|
+
preAbsolutePos.value = e[strAbso]
|
|
716
775
|
return
|
|
717
776
|
}
|
|
777
|
+
// 3. 循环更新: 只有一个元素时可滑动,加入阻力
|
|
778
|
+
if (circularShared.value && childrenLength.value === 1) {
|
|
779
|
+
const finalOffset = handleResistanceMove(eventData)
|
|
780
|
+
offset.value = finalOffset
|
|
781
|
+
preAbsolutePos.value = e[strAbso]
|
|
782
|
+
return
|
|
783
|
+
}
|
|
784
|
+
// 4. 循环更新:正常
|
|
718
785
|
const { isBoundary, resetOffset } = reachBoundary(eventData)
|
|
719
786
|
if (childrenLength.value > 1 && isBoundary && circularShared.value) {
|
|
720
787
|
offset.value = resetOffset
|
|
721
788
|
} else {
|
|
722
789
|
offset.value = moveDistance + offset.value
|
|
723
790
|
}
|
|
724
|
-
preAbsolutePos.value =
|
|
791
|
+
preAbsolutePos.value = e[strAbso]
|
|
725
792
|
})
|
|
726
|
-
.
|
|
793
|
+
.onFinalize((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
|
|
727
794
|
'worklet'
|
|
728
795
|
if (touchfinish.value) return
|
|
729
|
-
const touchEventData = e.changedTouches[0]
|
|
730
|
-
const moveDistance = touchEventData[strAbso] - moveTranstion.value
|
|
731
796
|
touchfinish.value = true
|
|
797
|
+
// 触发过onUpdate正常情况下e[strAbso] - preAbsolutePos.value=0; 未触发过onUpdate的情况下e[strAbso] - preAbsolutePos.value 不为0
|
|
798
|
+
const moveDistance = e[strAbso] - preAbsolutePos.value
|
|
732
799
|
const eventData = {
|
|
733
|
-
translation: moveDistance
|
|
800
|
+
translation: moveDistance,
|
|
801
|
+
transdir: moveDistance !== 0 ? moveDistance : e[strAbso] - moveTranstion.value
|
|
734
802
|
}
|
|
803
|
+
// 1. 只有一个元素:循环 和 非循环状态,都走回弹效果
|
|
735
804
|
if (childrenLength.value === 1) {
|
|
736
|
-
|
|
805
|
+
offset.value = withTiming(0, {
|
|
806
|
+
duration: easeDuration,
|
|
807
|
+
easing: easeMap[easeingFunc]
|
|
808
|
+
})
|
|
809
|
+
return
|
|
737
810
|
}
|
|
738
|
-
//
|
|
811
|
+
// 2.非循环状态不可移动态:最后一个元素 和 第一个元素
|
|
812
|
+
// 非循环支持最后元素可滑动能力后,向左快速移动未超过最大可移动范围一半,因为offset为正值,向左滑动handleBack,默认向上取整
|
|
813
|
+
// 但是在offset大于0时,取0。[-100, 0](back取0), [0, 100](back取1), 所以handleLongPress里的处理逻辑需要兼容支持,因此这里直接单独处理,不耦合下方公共的判断逻辑。
|
|
739
814
|
if (!circularShared.value && !canMove(eventData)) {
|
|
815
|
+
if (eventData.transdir < 0) {
|
|
816
|
+
handleBack(eventData)
|
|
817
|
+
} else {
|
|
818
|
+
handleEnd(eventData)
|
|
819
|
+
}
|
|
740
820
|
return
|
|
741
821
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
822
|
+
// 3. 非循环状态可移动态、循环状态, 正常逻辑处理
|
|
823
|
+
const velocity = e[strVelocity]
|
|
824
|
+
if (Math.abs(velocity) < longPressRatio) {
|
|
825
|
+
handleLongPress(eventData)
|
|
745
826
|
} else {
|
|
746
827
|
handleEnd(eventData)
|
|
747
828
|
}
|
|
@@ -40,6 +40,8 @@ declare let global: {
|
|
|
40
40
|
__formatValue (value: string): string | number
|
|
41
41
|
} & Record<string, any>
|
|
42
42
|
|
|
43
|
+
declare let mpxGlobal: Record<string, any>
|
|
44
|
+
|
|
43
45
|
declare module '@react-navigation/native' {
|
|
44
46
|
export function useNavigation (): Record<string, any>
|
|
45
47
|
export function usePreventRemove(
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from 'react-native-reanimated'
|
|
15
15
|
import type { AnimationCallback, WithTimingConfig, SharedValue, AnimatableValue } from 'react-native-reanimated'
|
|
16
16
|
import { error, hasOwn, collectDataset } from '@mpxjs/utils'
|
|
17
|
+
import { useRunOnJSCallback } from './utils'
|
|
17
18
|
import { ExtendedViewStyle } from './types/common'
|
|
18
19
|
import type { _ViewProps } from './mpx-view'
|
|
19
20
|
|
|
@@ -218,13 +219,19 @@ export default function useAnimationHooks<T, P> (props: _ViewProps & { enableAni
|
|
|
218
219
|
timeStamp: Date.now()
|
|
219
220
|
})
|
|
220
221
|
}
|
|
222
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
223
|
+
const runOnJSCallbackRef = useRef({
|
|
224
|
+
withTimingCallback
|
|
225
|
+
})
|
|
226
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
227
|
+
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
|
|
221
228
|
// 创建单个animation
|
|
222
229
|
function getAnimation ({ key, value }: { key: string, value: string|number }, { delay, duration, easing }: ExtendWithTimingConfig, callback?: AnimationCallback) {
|
|
223
230
|
const animation = typeof callback === 'function'
|
|
224
231
|
? withTiming(value, { duration, easing }, (finished, current) => {
|
|
225
232
|
callback(finished, current)
|
|
226
233
|
if (transitionend && finished) {
|
|
227
|
-
runOnJS(
|
|
234
|
+
runOnJS(runOnJSCallback)('withTimingCallback', finished, current, duration)
|
|
228
235
|
}
|
|
229
236
|
})
|
|
230
237
|
: withTiming(value, { duration, easing })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useCallback, useMemo, useRef, ReactNode, ReactElement, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement, createElement } from 'react'
|
|
1
|
+
import { useEffect, useCallback, useMemo, useRef, ReactNode, ReactElement, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement, createElement, MutableRefObject } from 'react'
|
|
2
2
|
import { LayoutChangeEvent, TextStyle, ImageProps, Image } from 'react-native'
|
|
3
3
|
import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn } from '@mpxjs/utils'
|
|
4
4
|
import { VarContext, ScrollViewContext, RouteContext } from './context'
|
|
@@ -787,13 +787,11 @@ export function useHover ({ enableHover, hoverStartTime, hoverStayTime, disabled
|
|
|
787
787
|
const gesture = useMemo(() => {
|
|
788
788
|
return Gesture.Pan()
|
|
789
789
|
.onTouchesDown(() => {
|
|
790
|
-
|
|
791
|
-
runOnJS(setStartTimer)()
|
|
790
|
+
setStartTimer()
|
|
792
791
|
})
|
|
793
792
|
.onTouchesUp(() => {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
})
|
|
793
|
+
setStayTimer()
|
|
794
|
+
}).runOnJS(true)
|
|
797
795
|
}, [])
|
|
798
796
|
|
|
799
797
|
if (gestureRef) {
|
|
@@ -805,3 +803,19 @@ export function useHover ({ enableHover, hoverStartTime, hoverStayTime, disabled
|
|
|
805
803
|
gesture
|
|
806
804
|
}
|
|
807
805
|
}
|
|
806
|
+
|
|
807
|
+
export function useRunOnJSCallback (callbackMapRef: MutableRefObject<Record<string, AnyFunc>>) {
|
|
808
|
+
const invokeCallback = useCallback((key: string, ...args: any) => {
|
|
809
|
+
const callback = callbackMapRef.current[key]
|
|
810
|
+
// eslint-disable-next-line node/no-callback-literal
|
|
811
|
+
if (isFunction(callback)) return callback(...args)
|
|
812
|
+
}, [])
|
|
813
|
+
|
|
814
|
+
useEffect(() => {
|
|
815
|
+
return () => {
|
|
816
|
+
callbackMapRef.current = {}
|
|
817
|
+
}
|
|
818
|
+
}, [])
|
|
819
|
+
|
|
820
|
+
return invokeCallback
|
|
821
|
+
}
|
|
@@ -49,7 +49,7 @@ import { processAppOption, getComponent } from ${stringifyRequest(loaderContext,
|
|
|
49
49
|
Vue.use(VueRouter)\n`
|
|
50
50
|
|
|
51
51
|
if (i18n) {
|
|
52
|
-
output += buildI18n({ i18n, loaderContext })
|
|
52
|
+
output += buildI18n({ i18n, isMain: true, loaderContext })
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
output += buildGlobalParams({
|
package/lib/web/processScript.js
CHANGED
|
@@ -7,7 +7,8 @@ const {
|
|
|
7
7
|
buildComponentsMap,
|
|
8
8
|
getRequireScript,
|
|
9
9
|
buildGlobalParams,
|
|
10
|
-
stringifyRequest
|
|
10
|
+
stringifyRequest,
|
|
11
|
+
buildI18n
|
|
11
12
|
} = require('./script-helper')
|
|
12
13
|
|
|
13
14
|
module.exports = function (script, {
|
|
@@ -24,7 +25,7 @@ module.exports = function (script, {
|
|
|
24
25
|
wxsModuleMap,
|
|
25
26
|
localComponentsMap
|
|
26
27
|
}, callback) {
|
|
27
|
-
const { projectRoot, appInfo, webConfig } = loaderContext.getMpx()
|
|
28
|
+
const { projectRoot, appInfo, webConfig, i18n } = loaderContext.getMpx()
|
|
28
29
|
|
|
29
30
|
let output = '/* script */\n'
|
|
30
31
|
|
|
@@ -70,6 +71,9 @@ module.exports = function (script, {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
content += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, webConfig, hasApp })
|
|
74
|
+
if (!hasApp && i18n) {
|
|
75
|
+
content += buildI18n({ i18n, loaderContext })
|
|
76
|
+
}
|
|
73
77
|
content += getRequireScript({ ctorType, script, loaderContext })
|
|
74
78
|
content += `
|
|
75
79
|
export default processComponentOption({
|
package/lib/web/script-helper.js
CHANGED
|
@@ -186,13 +186,17 @@ function buildGlobalParams ({
|
|
|
186
186
|
return content
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
function buildI18n ({ i18n, loaderContext }) {
|
|
189
|
+
function buildI18n ({ i18n, isMain, loaderContext }) {
|
|
190
190
|
let i18nContent = ''
|
|
191
191
|
const i18nObj = Object.assign({}, i18n)
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
if (!isMain) {
|
|
193
|
+
i18nContent += `import Vue from 'vue'
|
|
194
|
+
import Mpx from '@mpxjs/core'\n`
|
|
195
|
+
}
|
|
196
|
+
i18nContent += `import VueI18n from 'vue-i18n'
|
|
194
197
|
import { createI18n } from 'vue-i18n-bridge'
|
|
195
|
-
|
|
198
|
+
if (!Mpx.i18n) {
|
|
199
|
+
Vue.use(VueI18n , { bridge: true })\n`
|
|
196
200
|
const requestObj = {}
|
|
197
201
|
const i18nKeys = ['messages', 'dateTimeFormats', 'numberFormats']
|
|
198
202
|
i18nKeys.forEach((key) => {
|
|
@@ -201,15 +205,16 @@ function buildI18n ({ i18n, loaderContext }) {
|
|
|
201
205
|
delete i18nObj[`${key}Path`]
|
|
202
206
|
}
|
|
203
207
|
})
|
|
204
|
-
i18nContent += `
|
|
208
|
+
i18nContent += ` var i18nCfg = ${JSON.stringify(i18nObj)}\n`
|
|
205
209
|
Object.keys(requestObj).forEach((key) => {
|
|
206
|
-
i18nContent += `
|
|
210
|
+
i18nContent += ` i18nCfg.${key} = require(${requestObj[key]})\n`
|
|
207
211
|
})
|
|
208
212
|
i18nContent += `
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
i18nCfg.legacy = false
|
|
214
|
+
var i18n = createI18n(i18nCfg, VueI18n)
|
|
215
|
+
Vue.use(i18n)
|
|
216
|
+
Mpx.i18n = i18n
|
|
217
|
+
}\n`
|
|
213
218
|
return i18nContent
|
|
214
219
|
}
|
|
215
220
|
|