@mpxjs/webpack-plugin 2.10.18-beta.1 → 2.10.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/LICENSE +433 -0
  2. package/lib/index.js +0 -12
  3. package/lib/runtime/components/react/dist/mpx-camera.d.ts +4 -4
  4. package/lib/runtime/components/react/dist/mpx-camera.jsx +75 -41
  5. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +92 -15
  6. package/lib/runtime/components/react/dist/mpx-swiper.d.ts +0 -1
  7. package/lib/runtime/components/react/dist/mpx-swiper.jsx +24 -43
  8. package/lib/runtime/components/react/mpx-camera.tsx +78 -47
  9. package/lib/runtime/components/react/mpx-scroll-view.tsx +106 -16
  10. package/lib/runtime/components/react/mpx-swiper.tsx +23 -43
  11. package/lib/runtime/components/web/mpx-scroll-view.vue +2 -5
  12. package/lib/template-compiler/compiler.js +3 -7
  13. package/lib/utils/const.js +0 -29
  14. package/package.json +4 -3
  15. package/lib/resolver/ExtendComponentsPlugin.js +0 -60
  16. package/lib/runtime/components/ali/mpx-section-list.mpx +0 -566
  17. package/lib/runtime/components/ali/mpx-sticky-header.mpx +0 -212
  18. package/lib/runtime/components/ali/mpx-sticky-section.mpx +0 -17
  19. package/lib/runtime/components/react/dist/mpx-section-list.d.ts +0 -48
  20. package/lib/runtime/components/react/dist/mpx-section-list.jsx +0 -292
  21. package/lib/runtime/components/react/mpx-section-list.tsx +0 -439
  22. package/lib/runtime/components/web/mpx-section-list.vue +0 -551
  23. package/lib/runtime/components/wx/mpx-section-list-default/list-footer.mpx +0 -26
  24. package/lib/runtime/components/wx/mpx-section-list-default/list-header.mpx +0 -26
  25. package/lib/runtime/components/wx/mpx-section-list-default/list-item.mpx +0 -26
  26. package/lib/runtime/components/wx/mpx-section-list-default/section-header.mpx +0 -26
  27. package/lib/runtime/components/wx/mpx-section-list.mpx +0 -209
  28. package/lib/runtime/components/wx/mpx-sticky-header.mpx +0 -40
  29. package/lib/runtime/components/wx/mpx-sticky-section.mpx +0 -31
@@ -1,8 +1,10 @@
1
- import { createElement, forwardRef, useRef, useCallback, useContext, useState, useEffect } from 'react';
2
- import { useTransformStyle, useLayout, extendObject } from './utils';
1
+ import { createElement, forwardRef, useRef, useCallback, useContext, useState, useEffect, useMemo } from 'react';
2
+ import { getCurrentPage, useTransformStyle, useLayout, extendObject } from './utils';
3
3
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
4
- import { noop } from '@mpxjs/utils';
4
+ import { Camera, useCameraDevice, useCodeScanner, useCameraFormat } from 'react-native-vision-camera';
5
+ import { noop, warn, hasOwn } from '@mpxjs/utils';
5
6
  import { RouteContext } from './context';
7
+ import { watch } from '@mpxjs/core';
6
8
  const qualityValue = {
7
9
  high: 90,
8
10
  normal: 75,
@@ -12,9 +14,8 @@ const qualityValue = {
12
14
  let RecordRes = null;
13
15
  const _camera = forwardRef((props, ref) => {
14
16
  // eslint-disable-next-line @typescript-eslint/no-var-requires
15
- const { Camera, useCameraDevice, useCodeScanner, useCameraFormat } = require('react-native-vision-camera');
16
17
  const cameraRef = useRef(null);
17
- const { mode = 'normal', resolution = 'medium', devicePosition = 'back', flash = 'auto', frameSize = 'medium', bindinitdone, bindstop, bindscancode, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'enable-var': enableVar, 'external-var-context': externalVarContext, style = {} } = props;
18
+ const { mode = 'normal', resolution = 'medium', 'device-position': devicePosition = 'back', flash = 'auto', 'frame-size': frameSize = 'medium', bindinitdone, bindstop, bindscancode, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'enable-var': enableVar, 'external-var-context': externalVarContext, style = {} } = props;
18
19
  const styleObj = extendObject({}, style);
19
20
  const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(styleObj, {
20
21
  enableVar,
@@ -24,23 +25,25 @@ const _camera = forwardRef((props, ref) => {
24
25
  parentHeight
25
26
  });
26
27
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: cameraRef });
27
- const isPhoto = mode === 'normal';
28
+ const isPhoto = useRef(false);
29
+ isPhoto.current = mode === 'normal';
28
30
  const device = useCameraDevice(devicePosition || 'back');
29
- const { navigation } = useContext(RouteContext) || {};
31
+ const { navigation, pageId } = useContext(RouteContext) || {};
30
32
  const [zoomValue, setZoomValue] = useState(1);
33
+ const [isActive, setIsActive] = useState(true);
31
34
  const [hasPermission, setHasPermission] = useState(null);
32
- const hasCamera = useRef(false);
35
+ const page = getCurrentPage(pageId);
33
36
  // 先定义常量,避免在条件判断后使用
34
37
  const maxZoom = device?.maxZoom || 1;
35
38
  const RESOLUTION_MAPPING = {
36
- low: { width: 640, height: 480 },
37
- medium: { width: 1280, height: 720 },
38
- high: { width: 1920, height: 1080 }
39
+ low: { width: 1280, height: 720 },
40
+ medium: { width: 1920, height: 1080 },
41
+ high: 'max'
39
42
  };
40
43
  const FRAME_SIZE_MAPPING = {
41
- small: { width: 480, height: 360 },
42
- medium: { width: 720, height: 540 },
43
- large: { width: 1080, height: 810 }
44
+ small: { width: 1280, height: 720 },
45
+ medium: { width: 1920, height: 1080 },
46
+ large: 'max'
44
47
  };
45
48
  const format = useCameraFormat(device, [
46
49
  {
@@ -48,15 +51,31 @@ const _camera = forwardRef((props, ref) => {
48
51
  videoResolution: FRAME_SIZE_MAPPING[frameSize] || RESOLUTION_MAPPING[resolution]
49
52
  }
50
53
  ]);
54
+ const isScancode = useCallback((fail, complete) => {
55
+ if (!isPhoto.current) {
56
+ const result = {
57
+ errMsg: 'Not allow to invoke takePhoto in \'scanCode\' mode.'
58
+ };
59
+ fail(result);
60
+ complete(result);
61
+ return true;
62
+ }
63
+ return false;
64
+ }, []);
51
65
  const codeScanner = useCodeScanner({
52
- codeTypes: ['qr', 'ean-13'],
66
+ codeTypes: ['qr'],
53
67
  onCodeScanned: (codes) => {
54
- const result = codes.map(code => code.value).join(',');
55
- bindscancode && bindscancode(getCustomEvent('scancode', {}, {
56
- detail: {
57
- result: codes.map(code => code.value).join(',')
58
- }
59
- }));
68
+ codes.forEach(code => {
69
+ const type = code.type === 'qr' ? 'QR_CODE' : code.type?.toUpperCase();
70
+ const frame = code.frame || {};
71
+ bindscancode && bindscancode(getCustomEvent('scancode', {}, {
72
+ detail: {
73
+ result: code.value,
74
+ type,
75
+ scanArea: [parseInt(frame.x) || 0, parseInt(frame.y) || 0, parseInt(frame.width) || 0, parseInt(frame.height) || 0]
76
+ }
77
+ }));
78
+ });
60
79
  }
61
80
  });
62
81
  const onInitialized = useCallback(() => {
@@ -69,14 +88,15 @@ const _camera = forwardRef((props, ref) => {
69
88
  const onStopped = useCallback(() => {
70
89
  bindstop && bindstop();
71
90
  }, [bindstop]);
72
- const camera = {
91
+ const camera = useMemo(() => ({
73
92
  setZoom: (zoom) => {
74
93
  setZoomValue(zoom);
75
94
  },
76
95
  takePhoto: (options = {}) => {
77
96
  const { success = noop, fail = noop, complete = noop } = options;
97
+ if (isScancode(fail, complete))
98
+ return;
78
99
  cameraRef.current?.takePhoto?.({
79
- flash,
80
100
  quality: qualityValue[options.quality || 'normal']
81
101
  }).then((res) => {
82
102
  const result = {
@@ -97,7 +117,8 @@ const _camera = forwardRef((props, ref) => {
97
117
  let { timeout = 30, success = noop, fail = noop, complete = noop, timeoutCallback = noop } = options;
98
118
  timeout = timeout > 300 ? 300 : timeout;
99
119
  let recordTimer = null;
100
- let isTimeout = false;
120
+ if (isScancode(fail, complete))
121
+ return;
101
122
  try {
102
123
  const result = {
103
124
  errMsg: 'startRecord:ok'
@@ -105,7 +126,6 @@ const _camera = forwardRef((props, ref) => {
105
126
  success(result);
106
127
  complete(result);
107
128
  cameraRef.current?.startRecording?.({
108
- flash,
109
129
  onRecordingError: (error) => {
110
130
  if (recordTimer)
111
131
  clearTimeout(recordTimer);
@@ -122,7 +142,6 @@ const _camera = forwardRef((props, ref) => {
122
142
  }
123
143
  });
124
144
  recordTimer = setTimeout(() => {
125
- isTimeout = true;
126
145
  cameraRef.current?.stopRecording().catch(() => {
127
146
  // 忽略停止录制时的错误
128
147
  });
@@ -140,6 +159,8 @@ const _camera = forwardRef((props, ref) => {
140
159
  },
141
160
  stopRecord: (options = {}) => {
142
161
  const { success = noop, fail = noop, complete = noop } = options;
162
+ if (isScancode(fail, complete))
163
+ return;
143
164
  try {
144
165
  cameraRef.current?.stopRecording().then(() => {
145
166
  setTimeout(() => {
@@ -170,16 +191,20 @@ const _camera = forwardRef((props, ref) => {
170
191
  complete(result);
171
192
  }
172
193
  }
173
- };
194
+ }), []);
174
195
  useEffect(() => {
175
- if (navigation) {
176
- if (navigation && !navigation.camera) {
177
- navigation.camera = camera;
178
- }
179
- else {
180
- hasCamera.current = true;
181
- navigation.camera.multi = true;
182
- }
196
+ let unWatch;
197
+ if (pageId && hasOwn(global.__mpxPageStatusMap, String(pageId))) {
198
+ unWatch = watch(() => global.__mpxPageStatusMap[pageId], (newVal) => {
199
+ if (newVal === 'show') {
200
+ if (page.id === pageId) {
201
+ setIsActive(true);
202
+ }
203
+ }
204
+ if (newVal === 'hide') {
205
+ setIsActive(false);
206
+ }
207
+ }, { sync: true });
183
208
  }
184
209
  const checkCameraPermission = async () => {
185
210
  try {
@@ -198,23 +223,25 @@ const _camera = forwardRef((props, ref) => {
198
223
  };
199
224
  checkCameraPermission();
200
225
  return () => {
201
- if (navigation && navigation.camera) {
202
- navigation.camera = null;
226
+ if (navigation?.camera === camera) {
227
+ delete navigation.camera;
203
228
  }
229
+ unWatch && unWatch();
204
230
  };
205
231
  }, []);
206
232
  const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
207
233
  ref: cameraRef,
208
234
  style: extendObject({}, normalStyle, layoutStyle),
209
- isActive: true,
235
+ isActive,
210
236
  photo: true,
211
237
  video: true,
212
238
  onInitialized,
213
239
  onStopped,
214
240
  device,
215
241
  format,
216
- codeScanner: !isPhoto ? codeScanner : undefined,
217
- zoom: zoomValue
242
+ codeScanner: !isPhoto.current ? codeScanner : undefined,
243
+ zoom: zoomValue,
244
+ torch: flash
218
245
  }), [
219
246
  'mode',
220
247
  'resolution',
@@ -227,7 +254,14 @@ const _camera = forwardRef((props, ref) => {
227
254
  ], {
228
255
  layoutRef
229
256
  });
230
- if (!hasPermission || hasCamera.current || !device) {
257
+ if (navigation && navigation.camera && navigation.camera !== camera) {
258
+ warn('<camera>: 一个页面只能插入一个');
259
+ return null;
260
+ }
261
+ else if (navigation) {
262
+ navigation.camera = camera;
263
+ }
264
+ if (!hasPermission || !device) {
231
265
  return null;
232
266
  }
233
267
  return createElement(Camera, innerProps);
@@ -160,19 +160,96 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
160
160
  }
161
161
  }
162
162
  }, [refresherTriggered]);
163
- function scrollTo({ top = 0, left = 0, animated = false }) {
164
- scrollToOffset(left, top, animated);
163
+ function scrollTo({ top = 0, left = 0, animated = false, duration }) {
164
+ // 如果指定了 duration 且需要动画,使用自定义动画
165
+ if (animated && duration && duration > 0) {
166
+ // 获取当前滚动位置
167
+ const currentY = scrollOptions.current.scrollTop || 0;
168
+ const currentX = scrollOptions.current.scrollLeft || 0;
169
+ const startTime = Date.now();
170
+ const deltaY = top - currentY;
171
+ const deltaX = left - currentX;
172
+ // 缓动函数:easeInOutCubic
173
+ const easing = (t) => {
174
+ return t < 0.5
175
+ ? 4 * t * t * t
176
+ : 1 - Math.pow(-2 * t + 2, 3) / 2;
177
+ };
178
+ // 使用 requestAnimationFrame 实现平滑动画
179
+ const animate = () => {
180
+ const elapsed = Date.now() - startTime;
181
+ const progress = Math.min(elapsed / duration, 1); // 0 到 1
182
+ const easeProgress = easing(progress);
183
+ const nextY = currentY + deltaY * easeProgress;
184
+ const nextX = currentX + deltaX * easeProgress;
185
+ if (scrollViewRef.current) {
186
+ scrollViewRef.current.scrollTo({ y: nextY, x: nextX, animated: false });
187
+ }
188
+ if (progress < 1) {
189
+ requestAnimationFrame(animate);
190
+ }
191
+ else {
192
+ // 确保最终位置准确
193
+ if (scrollViewRef.current) {
194
+ scrollViewRef.current.scrollTo({ y: top, x: left, animated: false });
195
+ }
196
+ }
197
+ };
198
+ requestAnimationFrame(animate);
199
+ }
200
+ else {
201
+ // 使用原生的 scrollTo
202
+ scrollToOffset(left, top, animated);
203
+ }
165
204
  }
166
- function handleScrollIntoView(selector = '', { offset = 0, animated = true } = {}) {
167
- const refs = __selectRef(`#${selector}`, 'node');
168
- if (!refs)
169
- return;
170
- const { nodeRef } = refs.getNodeInstance();
171
- nodeRef.current?.measureLayout(scrollViewRef.current, (left, top) => {
172
- const adjustedLeft = scrollX ? left + offset : left;
173
- const adjustedTop = scrollY ? top + offset : top;
174
- scrollToOffset(adjustedLeft, adjustedTop, animated);
175
- });
205
+ function handleScrollIntoView(selector = '', { offset = 0, animated = true, duration = undefined } = {}) {
206
+ try {
207
+ const currentSelectRef = propsRef.current.__selectRef;
208
+ if (!currentSelectRef) {
209
+ const errMsg = '__selectRef is not available. Please ensure the scroll-view component is properly initialized.';
210
+ warn(errMsg);
211
+ return;
212
+ }
213
+ const targetScrollView = scrollViewRef.current;
214
+ if (!targetScrollView) {
215
+ const errMsg = 'scrollViewRef is not ready';
216
+ warn(errMsg);
217
+ return;
218
+ }
219
+ // scroll-into-view prop 按微信规范直传裸 id(如 "section-1"),而 __refs 注册时 key 带 # 或 . 前缀,需补齐才能命中;
220
+ // pageScrollTo 调用方已自带前缀(如 "#section-1")
221
+ const normalizedSelector = selector.startsWith('#') || selector.startsWith('.') ? selector : `#${selector}`;
222
+ // 调用 __selectRef 查找元素
223
+ const refs = currentSelectRef(normalizedSelector, 'node');
224
+ if (!refs) {
225
+ const errMsg = `Element not found for selector: ${normalizedSelector}`;
226
+ warn(errMsg);
227
+ return;
228
+ }
229
+ const { nodeRef } = refs.getNodeInstance();
230
+ if (!nodeRef?.current) {
231
+ const errMsg = `Node ref not available for selector: ${normalizedSelector}`;
232
+ warn(errMsg);
233
+ return;
234
+ }
235
+ nodeRef.current.measureLayout(targetScrollView, (left, top) => {
236
+ const adjustedLeft = scrollX ? left + offset : left;
237
+ const adjustedTop = scrollY ? top + offset : top;
238
+ // 使用 scrollTo 方法,支持 duration 参数
239
+ if (duration !== undefined) {
240
+ scrollTo({ left: adjustedLeft, top: adjustedTop, animated, duration });
241
+ }
242
+ else {
243
+ scrollToOffset(adjustedLeft, adjustedTop, animated);
244
+ }
245
+ }, (error) => {
246
+ warn(`Failed to measure layout for selector ${normalizedSelector}: ${error}`);
247
+ });
248
+ }
249
+ catch (error) {
250
+ const errMsg = `handleScrollIntoView error for selector ${selector}: ${error?.message || error}`;
251
+ warn(errMsg);
252
+ }
176
253
  }
177
254
  function selectLength(size) {
178
255
  return !scrollX ? size.height : size.width;
@@ -457,9 +534,9 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
457
534
  }
458
535
  // 处理下拉刷新的手势 - 使用 useMemo 避免每次渲染都创建
459
536
  const panGesture = useMemo(() => {
460
- if (!hasRefresher)
461
- return Gesture.Pan(); // 返回空手势
462
537
  return Gesture.Pan()
538
+ .activeOffsetY([-5, 5])
539
+ .failOffsetX([-5, 5])
463
540
  .onUpdate((event) => {
464
541
  'worklet';
465
542
  if (enhanced && !!bounces) {
@@ -519,7 +596,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
519
596
  }
520
597
  })
521
598
  .simultaneousWithExternalGesture(scrollViewRef);
522
- }, [hasRefresher, enhanced, bounces, refreshing, refresherThreshold]);
599
+ }, [enhanced, bounces, refreshing, refresherThreshold]);
523
600
  const scrollAdditionalProps = extendObject({
524
601
  style: extendObject(hasOwn(innerStyle, 'flex') || hasOwn(innerStyle, 'flexGrow')
525
602
  ? {}
@@ -56,7 +56,6 @@ interface SwiperProps {
56
56
  'wait-for'?: Array<GestureHandler>;
57
57
  'simultaneous-handlers'?: Array<GestureHandler>;
58
58
  disableGesture?: boolean;
59
- 'display-multiple-items'?: number;
60
59
  bindchange?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void;
61
60
  }
62
61
  declare const SwiperWrapper: React.ForwardRefExoticComponent<SwiperProps & React.RefAttributes<HandlerRef<View, SwiperProps>>>;
@@ -72,7 +72,6 @@ const SwiperWrapper = forwardRef((props, ref) => {
72
72
  marginBottom: dotSpacing,
73
73
  zIndex: 98
74
74
  };
75
- const displayMultipleItems = props['display-multiple-items'] || 1;
76
75
  const easeingFunc = props['easing-function'] || 'default';
77
76
  const easeDuration = props.duration || 500;
78
77
  const horizontal = props.vertical !== undefined ? !props.vertical : true;
@@ -98,19 +97,19 @@ const SwiperWrapper = forwardRef((props, ref) => {
98
97
  const preMarginShared = useSharedValue(preMargin);
99
98
  const nextMarginShared = useSharedValue(nextMargin);
100
99
  const autoplayShared = useSharedValue(autoplay);
101
- const children = Array.isArray(props.children) ? props.children.filter(child => child) : (props.children ? [props.children] : []);
102
100
  // 默认前后补位的元素个数
103
- const patchElmNum = (circular && children.length > 1) ? displayMultipleItems + 1 : 0;
101
+ const patchElmNum = circular ? (preMargin ? 2 : 1) : 0;
104
102
  const patchElmNumShared = useSharedValue(patchElmNum);
105
- const displayMultipleItemsShared = useSharedValue(displayMultipleItems);
106
103
  const circularShared = useSharedValue(circular);
104
+ // 支持swiper-item 同时存在<swiper-item wx:for/>和<swiper-item>并列的情况
105
+ const children = React.Children.toArray(props.children);
107
106
  // 对有变化的变量,在worklet中只能使用sharedValue变量,useRef不能更新
108
107
  const childrenLength = useSharedValue(children.length);
109
108
  const initWidth = typeof normalStyle?.width === 'number' ? normalStyle.width - preMargin - nextMargin : normalStyle.width;
110
109
  const initHeight = typeof normalStyle?.height === 'number' ? normalStyle.height - preMargin - nextMargin : normalStyle.height;
111
110
  const dir = horizontal === false ? 'y' : 'x';
112
111
  const pstep = dir === 'x' ? initWidth : initHeight;
113
- const initStep = isNaN(pstep) ? 0 : pstep / displayMultipleItems;
112
+ const initStep = isNaN(pstep) ? 0 : pstep;
114
113
  // 每个元素的宽度 or 高度,有固定值直接初始化无则0
115
114
  const step = useSharedValue(initStep);
116
115
  // 记录选中元素的索引值
@@ -174,7 +173,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
174
173
  const { width, height } = e.nativeEvent.layout;
175
174
  const realWidth = dir === 'x' ? width - preMargin - nextMargin : width;
176
175
  const realHeight = dir === 'y' ? height - preMargin - nextMargin : height;
177
- const iStep = (dir === 'x' ? realWidth : realHeight) / displayMultipleItems;
176
+ const iStep = dir === 'x' ? realWidth : realHeight;
178
177
  if (iStep !== step.value) {
179
178
  step.value = iStep;
180
179
  updateCurrent(propCurrent, iStep);
@@ -230,35 +229,19 @@ const SwiperWrapper = forwardRef((props, ref) => {
230
229
  function renderItems() {
231
230
  const intLen = children.length;
232
231
  let renderChild = children.slice();
233
- // if (circular && intLen > 1) {
234
- // // 最前面加最后一个元素
235
- // const lastChild = React.cloneElement(children[intLen - 1] as ReactElement, { key: 'clone0' })
236
- // // 最后面加第一个元素
237
- // const firstChild = React.cloneElement(children[0] as ReactElement, { key: 'clone1' })
238
- // if (preMargin) {
239
- // const lastChild1 = React.cloneElement(children[intLen - 2] as ReactElement, { key: 'clone2' })
240
- // const firstChild1 = React.cloneElement(children[1] as ReactElement, { key: 'clone3' })
241
- // renderChild = [lastChild1, lastChild].concat(renderChild).concat([firstChild, firstChild1])
242
- // } else {
243
- // renderChild = [lastChild].concat(renderChild).concat([firstChild])
244
- // }
245
- // }
246
232
  if (circular && intLen > 1) {
247
- // 动态生成前置补位元素
248
- const frontClones = [];
249
- // 计算补位序列的起始索引。例如 len=3, patch=2 -> start=1 (即从B开始)
250
- const startIndex = intLen - (patchElmNum % intLen);
251
- for (let i = 0; i < patchElmNum; i++) {
252
- const sourceIndex = (startIndex + i) % intLen;
253
- frontClones.push(React.cloneElement(children[sourceIndex], { key: `clone_front_${i}` }));
254
- }
255
- // 动态生成后置补位元素
256
- const backClones = [];
257
- for (let i = 0; i < patchElmNum; i++) {
258
- const sourceIndex = i % intLen;
259
- backClones.push(React.cloneElement(children[sourceIndex], { key: `clone_back_${i}` }));
260
- }
261
- renderChild = [...frontClones, ...renderChild, ...backClones];
233
+ // 最前面加最后一个元素
234
+ const lastChild = React.cloneElement(children[intLen - 1], { key: 'clone0' });
235
+ // 最后面加第一个元素
236
+ const firstChild = React.cloneElement(children[0], { key: 'clone1' });
237
+ if (preMargin) {
238
+ const lastChild1 = React.cloneElement(children[intLen - 2], { key: 'clone2' });
239
+ const firstChild1 = React.cloneElement(children[1], { key: 'clone3' });
240
+ renderChild = [lastChild1, lastChild].concat(renderChild).concat([firstChild, firstChild1]);
241
+ }
242
+ else {
243
+ renderChild = [lastChild].concat(renderChild).concat([firstChild]);
244
+ }
262
245
  }
263
246
  const arrChildren = renderChild.map((child, index) => {
264
247
  const extraStyle = {};
@@ -293,7 +276,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
293
276
  let nextIndex = currentIndex.value;
294
277
  if (!circularShared.value) {
295
278
  // 获取下一个位置的坐标, 循环到最后一个元素,直接停止, 取消定时器
296
- if (currentIndex.value === childrenLength.value - displayMultipleItemsShared.value) {
279
+ if (currentIndex.value === childrenLength.value - 1) {
297
280
  pauseLoop();
298
281
  return;
299
282
  }
@@ -459,13 +442,12 @@ const SwiperWrapper = forwardRef((props, ref) => {
459
442
  };
460
443
  }, [autoplay]);
461
444
  useEffect(() => {
462
- if (circular !== circularShared.value || patchElmNum !== patchElmNumShared.value || displayMultipleItems !== displayMultipleItemsShared.value) {
445
+ if (circular !== circularShared.value) {
463
446
  circularShared.value = circular;
464
- patchElmNumShared.value = patchElmNum;
465
- displayMultipleItemsShared.value = displayMultipleItems;
447
+ patchElmNumShared.value = circular ? (preMargin ? 2 : 1) : 0;
466
448
  offset.value = getOffset(currentIndex.value, step.value);
467
449
  }
468
- }, [circular, patchElmNum, displayMultipleItems]);
450
+ }, [circular, preMargin]);
469
451
  const { gestureHandler } = useMemo(() => {
470
452
  // 基于transdir + 当前offset计算索引
471
453
  function getTargetPosition(eventData) {
@@ -484,8 +466,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
484
466
  const moveToIndex = transdir < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex);
485
467
  // 实际应该定位的索引值
486
468
  if (!circularShared.value) {
487
- const maxIndex = Math.max(0, childrenLength.value - displayMultipleItemsShared.value);
488
- selectedIndex = Math.min(Math.max(moveToIndex, 0), maxIndex);
469
+ selectedIndex = moveToIndex;
489
470
  moveToTargetPos = selectedIndex * step.value;
490
471
  }
491
472
  else {
@@ -521,7 +502,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
521
502
  const gestureMovePos = offset.value + translation;
522
503
  if (!circularShared.value) {
523
504
  // 如果只判断区间,中间非滑动状态(handleResistanceMove)向左滑动,突然改为向右滑动,但是还在非滑动态,本应该可滑动判断为了不可滑动
524
- const posEnd = -step.value * (childrenLength.value - displayMultipleItemsShared.value);
505
+ const posEnd = -step.value * (childrenLength.value - 1);
525
506
  if (transdir < 0) {
526
507
  return gestureMovePos > posEnd;
527
508
  }
@@ -628,7 +609,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
628
609
  const { translation, transdir } = eventData;
629
610
  const moveToOffset = offset.value + translation;
630
611
  const maxOverDrag = Math.floor(step.value / 2);
631
- const maxOffset = translation < 0 ? -(childrenLength.value - displayMultipleItemsShared.value) * step.value : 0;
612
+ const maxOffset = translation < 0 ? -(childrenLength.value - 1) * step.value : 0;
632
613
  let resistance = 0.1;
633
614
  let overDrag = 0;
634
615
  let finalOffset = 0;