@mpxjs/webpack-plugin 2.9.70 → 2.9.71

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 (60) hide show
  1. package/lib/platform/template/wx/component-config/movable-view.js +8 -1
  2. package/lib/platform/template/wx/component-config/picker-view.js +1 -5
  3. package/lib/platform/template/wx/component-config/scroll-view.js +1 -1
  4. package/lib/platform/template/wx/index.js +0 -4
  5. package/lib/runtime/components/react/context.ts +8 -0
  6. package/lib/runtime/components/react/dist/context.js +2 -0
  7. package/lib/runtime/components/react/dist/getInnerListeners.js +34 -31
  8. package/lib/runtime/components/react/dist/mpx-button.jsx +16 -44
  9. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +93 -58
  10. package/lib/runtime/components/react/dist/mpx-navigator.jsx +1 -1
  11. package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +35 -0
  12. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +151 -127
  13. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +38 -34
  14. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -11
  15. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +11 -4
  16. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +31 -8
  17. package/lib/runtime/components/react/dist/mpx-swiper.jsx +670 -0
  18. package/lib/runtime/components/react/dist/mpx-view.jsx +15 -53
  19. package/lib/runtime/components/react/dist/pickerFaces.js +7 -6
  20. package/lib/runtime/components/react/dist/pickerVIewContext.js +14 -0
  21. package/lib/runtime/components/react/dist/pickerViewIndicator.jsx +23 -0
  22. package/lib/runtime/components/react/dist/pickerViewMask.jsx +18 -0
  23. package/lib/runtime/components/react/dist/useAnimationHooks.js +20 -2
  24. package/lib/runtime/components/react/dist/utils.jsx +74 -11
  25. package/lib/runtime/components/react/getInnerListeners.ts +43 -32
  26. package/lib/runtime/components/react/mpx-button.tsx +20 -57
  27. package/lib/runtime/components/react/mpx-movable-view.tsx +119 -74
  28. package/lib/runtime/components/react/mpx-navigator.tsx +1 -1
  29. package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +76 -0
  30. package/lib/runtime/components/react/mpx-picker-view-column.tsx +206 -183
  31. package/lib/runtime/components/react/mpx-picker-view.tsx +49 -48
  32. package/lib/runtime/components/react/mpx-rich-text/index.tsx +12 -18
  33. package/lib/runtime/components/react/mpx-scroll-view.tsx +21 -10
  34. package/lib/runtime/components/react/mpx-swiper-item.tsx +45 -11
  35. package/lib/runtime/components/react/mpx-swiper.tsx +742 -0
  36. package/lib/runtime/components/react/mpx-view.tsx +18 -65
  37. package/lib/runtime/components/react/pickerFaces.ts +10 -7
  38. package/lib/runtime/components/react/pickerVIewContext.ts +27 -0
  39. package/lib/runtime/components/react/pickerViewIndicator.tsx +34 -0
  40. package/lib/runtime/components/react/pickerViewMask.tsx +30 -0
  41. package/lib/runtime/components/react/types/{getInnerListeners.ts → getInnerListeners.d.ts} +4 -5
  42. package/lib/runtime/components/react/types/global.d.ts +10 -0
  43. package/lib/runtime/components/react/useAnimationHooks.ts +24 -3
  44. package/lib/runtime/components/react/utils.tsx +85 -12
  45. package/lib/runtime/components/web/mpx-checkbox.vue +1 -1
  46. package/lib/runtime/components/web/mpx-picker-view-column.vue +9 -4
  47. package/lib/template-compiler/compiler.js +61 -13
  48. package/lib/wxss/loader.js +15 -2
  49. package/package.json +3 -3
  50. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +0 -480
  51. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +0 -68
  52. package/lib/runtime/components/react/dist/mpx-swiper/type.js +0 -1
  53. package/lib/runtime/components/react/dist/pickerOverlay.jsx +0 -21
  54. package/lib/runtime/components/react/dist/types/common.js +0 -1
  55. package/lib/runtime/components/react/dist/types/getInnerListeners.js +0 -1
  56. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +0 -527
  57. package/lib/runtime/components/react/mpx-swiper/index.tsx +0 -80
  58. package/lib/runtime/components/react/mpx-swiper/type.ts +0 -87
  59. package/lib/runtime/components/react/pickerOverlay.tsx +0 -32
  60. /package/lib/runtime/components/react/types/{common.ts → common.d.ts} +0 -0
@@ -38,7 +38,7 @@ const endTag = new RegExp(('^<\\/' + qnameCapture + '[^>]*>'))
38
38
  const doctype = /^<!DOCTYPE [^>]+>/i
39
39
  const comment = /^<!--/
40
40
  const conditionalComment = /^<!\[/
41
- const hoverClassReg = /^mpx-((cover-)?view|button|navigator)$/
41
+ const specialClassReg = /^mpx-((cover-)?view|button|navigator|picker-view)$/
42
42
  let IS_REGEX_CAPTURING_BROKEN = false
43
43
  'x'.replace(/x(.)?/g, function (m, g) {
44
44
  IS_REGEX_CAPTURING_BROKEN = g === ''
@@ -1073,12 +1073,6 @@ function processStyleReact (el, options) {
1073
1073
  let staticClass = getAndRemoveAttr(el, 'class').val || ''
1074
1074
  staticClass = staticClass.replace(/\s+/g, ' ')
1075
1075
 
1076
- let staticHoverClass = ''
1077
- if (hoverClassReg.test(el.tag)) {
1078
- staticHoverClass = el.attrsMap['hover-class'] || ''
1079
- staticHoverClass = staticHoverClass.replace(/\s+/g, ' ')
1080
- }
1081
-
1082
1076
  const dynamicStyle = getAndRemoveAttr(el, config[mode].directive.dynamicStyle).val
1083
1077
  let staticStyle = getAndRemoveAttr(el, 'style').val || ''
1084
1078
  staticStyle = staticStyle.replace(/\s+/g, ' ')
@@ -1102,12 +1096,22 @@ function processStyleReact (el, options) {
1102
1096
  }])
1103
1097
  }
1104
1098
 
1105
- if (staticHoverClass && staticHoverClass !== 'none') {
1106
- const staticClassExp = parseMustacheWithContext(staticHoverClass).result
1107
- addAttrs(el, [{
1108
- name: 'hover-style',
1109
- value: `{{this.__getStyle(${staticClassExp})}}`
1110
- }])
1099
+ if (specialClassReg.test(el.tag)) {
1100
+ const staticClassNames = ['hover', 'indicator', 'mask']
1101
+ staticClassNames.forEach((className) => {
1102
+ let staticClass = el.attrsMap[className + '-class'] || ''
1103
+ let staticStyle = getAndRemoveAttr(el, className + '-style').val || ''
1104
+ staticClass = staticClass.replace(/\s+/g, ' ')
1105
+ staticStyle = staticStyle.replace(/\s+/g, ' ')
1106
+ if ((staticClass && staticClass !== 'none') || staticStyle) {
1107
+ const staticClassExp = parseMustacheWithContext(staticClass).result
1108
+ const staticStyleExp = parseMustacheWithContext(staticStyle).result
1109
+ addAttrs(el, [{
1110
+ name: className + '-style',
1111
+ value: `{{this.__getStyle(${staticClassExp}, null, ${staticStyleExp})}}`
1112
+ }])
1113
+ }
1114
+ })
1111
1115
  }
1112
1116
 
1113
1117
  // 处理externalClasses,将其转换为style作为props传递
@@ -1153,6 +1157,49 @@ function getModelConfig (el, match) {
1153
1157
  }
1154
1158
  }
1155
1159
 
1160
+ function processEventWeb (el) {
1161
+ const eventConfigMap = {}
1162
+ el.attrsList.forEach(function ({ name, value }) {
1163
+ if (/^@[a-zA-Z]+$/.test(name)) {
1164
+ const parsedFunc = parseFuncStr(value)
1165
+ if (parsedFunc) {
1166
+ if (!eventConfigMap[name]) {
1167
+ eventConfigMap[name] = {
1168
+ configs: []
1169
+ }
1170
+ }
1171
+ eventConfigMap[name].configs.push(
1172
+ Object.assign({ name, value }, parsedFunc)
1173
+ )
1174
+ }
1175
+ }
1176
+ })
1177
+
1178
+ // let wrapper
1179
+ for (const name in eventConfigMap) {
1180
+ const { configs } = eventConfigMap[name]
1181
+ if (!configs.length) continue
1182
+ configs.forEach(({ name }) => {
1183
+ if (name) {
1184
+ // 清空原始事件绑定
1185
+ let has
1186
+ do {
1187
+ has = getAndRemoveAttr(el, name).has
1188
+ } while (has)
1189
+ }
1190
+ })
1191
+ const value = `(e)=>__invoke(e, [${configs.map(
1192
+ (item) => item.expStr
1193
+ )}])`
1194
+ addAttrs(el, [
1195
+ {
1196
+ name,
1197
+ value
1198
+ }
1199
+ ])
1200
+ }
1201
+ }
1202
+
1156
1203
  function processEventReact (el) {
1157
1204
  const eventConfigMap = {}
1158
1205
  el.attrsList.forEach(function ({ name, value }) {
@@ -2638,6 +2685,7 @@ function processElement (el, root, options, meta) {
2638
2685
  // 预处理代码维度条件编译
2639
2686
  processIfWeb(el)
2640
2687
  processScoped(el)
2688
+ processEventWeb(el)
2641
2689
  // processWebExternalClassesHack(el, options)
2642
2690
  processExternalClasses(el, options)
2643
2691
  processComponentGenericsWeb(el, options, meta)
@@ -32,6 +32,13 @@ const {
32
32
  } = require('./utils')
33
33
  const createHelpers = require('../helpers')
34
34
 
35
+ const RN_PRESET_OPTIMISATION = {
36
+ reduceInitial: false,
37
+ normalizeWhitespace: false,
38
+ minifyFontValues: false,
39
+ convertValues: false
40
+ }
41
+
35
42
  module.exports = async function loader (content, map, meta) {
36
43
  const rawOptions = this.getOptions(schema)
37
44
  const plugins = []
@@ -41,6 +48,7 @@ module.exports = async function loader (content, map, meta) {
41
48
  const externals = mpx.externals
42
49
  const root = mpx.projectRoot
43
50
  const sourceMap = mpx.cssSourceMap || false
51
+ const isRN = ['ios', 'android', 'harmony'].includes(mpx.mode)
44
52
 
45
53
  let options
46
54
 
@@ -152,12 +160,17 @@ module.exports = async function loader (content, map, meta) {
152
160
  if (this.minimize) {
153
161
  const cssnano = require('cssnano')
154
162
  const minimizeOptions = rawOptions.minimize || {}
163
+ const presetOptimisation = Object.assign(
164
+ {},
165
+ isRN ? RN_PRESET_OPTIMISATION : {},
166
+ minimizeOptions.optimisation
167
+ )
155
168
  let cssnanoConfig = {
156
- preset: ['cssnano-preset-default', minimizeOptions.optimisation || {}]
169
+ preset: ['cssnano-preset-default', presetOptimisation]
157
170
  }
158
171
  if (minimizeOptions.advanced) {
159
172
  cssnanoConfig = {
160
- preset: ['cssnano-preset-advanced', minimizeOptions.optimisation || {}]
173
+ preset: ['cssnano-preset-advanced', presetOptimisation]
161
174
  }
162
175
  }
163
176
  plugins.push(cssnano(cssnanoConfig))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/webpack-plugin",
3
- "version": "2.9.70",
3
+ "version": "2.9.71",
4
4
  "description": "mpx compile core",
5
5
  "keywords": [
6
6
  "mpx"
@@ -83,7 +83,7 @@
83
83
  "devDependencies": {
84
84
  "@ant-design/react-native": "^5.2.2",
85
85
  "@d11/react-native-fast-image": "^8.6.12",
86
- "@mpxjs/api-proxy": "^2.9.70",
86
+ "@mpxjs/api-proxy": "^2.9.71",
87
87
  "@types/babel-traverse": "^6.25.4",
88
88
  "@types/babel-types": "^7.0.4",
89
89
  "@types/react": "^18.2.79",
@@ -99,5 +99,5 @@
99
99
  "engines": {
100
100
  "node": ">=14.14.0"
101
101
  },
102
- "gitHead": "ad8f04de95865ffc2b50b8c0a7f87b5952fb8647"
102
+ "gitHead": "38446f301a17e78dbe3ca7dacd771c79eb7a8ede"
103
103
  }
@@ -1,480 +0,0 @@
1
- /**
2
- * swiper 实现
3
- */
4
- import { Animated, View, Dimensions, Platform } from 'react-native';
5
- import { forwardRef, useState, useRef, useEffect } from 'react';
6
- import { getCustomEvent } from '../getInnerListeners';
7
- import useNodesRef from '../useNodesRef'; // 引入辅助函数
8
- import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from '../utils';
9
- /**
10
- * 默认的Style类型
11
- */
12
- const styles = {
13
- slide: {
14
- backgroundColor: 'transparent'
15
- },
16
- container_x: {
17
- position: 'relative'
18
- },
19
- container_y: {
20
- position: 'relative'
21
- },
22
- pagination_x: {
23
- position: 'absolute',
24
- bottom: 25,
25
- left: 0,
26
- right: 0,
27
- flexDirection: 'row',
28
- flex: 1,
29
- justifyContent: 'center',
30
- alignItems: 'center'
31
- },
32
- pagination_y: {
33
- position: 'absolute',
34
- right: 15,
35
- top: 0,
36
- bottom: 0,
37
- flexDirection: 'column',
38
- flex: 1,
39
- justifyContent: 'center',
40
- alignItems: 'center'
41
- }
42
- };
43
- const _Carouse = forwardRef((props, ref) => {
44
- // 默认取水平方向的width
45
- const { width } = Dimensions.get('window');
46
- const { style, previousMargin = 0, nextMargin = 0, enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight } = props;
47
- // 计算transform之类的
48
- const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, {
49
- enableVar,
50
- externalVarContext,
51
- parentFontSize,
52
- parentWidth,
53
- parentHeight
54
- });
55
- const { textStyle, innerStyle } = splitStyle(normalStyle);
56
- const { textProps } = splitProps(props);
57
- const newChild = Array.isArray(props.children) ? props.children.filter(child => child) : props.children;
58
- const totalElements = Array.isArray(newChild) ? newChild.length : (newChild ? 1 : 0);
59
- const defaultHeight = (normalStyle?.height || 150);
60
- const defaultWidth = (normalStyle?.width || width || 375);
61
- const dir = props.horizontal === false ? 'y' : 'x';
62
- // state的offset默认值
63
- // const initIndex = props.circular ? props.current + 1: (props.current || 0)
64
- // 记录真正的下标索引, 不包括循环前后加入的索引, 游标
65
- const initIndex = props.current || 0;
66
- // 这里要排除超过元素个数的设置
67
- const initOffsetIndex = initIndex + (props.circular && totalElements > 1 ? 1 : 0);
68
- const defaultX = (defaultWidth * initOffsetIndex) || 0;
69
- const defaultY = (defaultHeight * initOffsetIndex) || 0;
70
- // 主动scorllTo时是否要出发onScrollEnd
71
- const needTriggerScrollEnd = useRef(true);
72
- // 内部存储上一次的offset值
73
- const autoplayTimerRef = useRef(null);
74
- const scrollViewRef = useRef(null);
75
- useNodesRef(props, ref, scrollViewRef, {
76
- style: normalStyle
77
- });
78
- const {
79
- // 存储layout布局信息
80
- layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout: onWrapperLayout });
81
- // 内部存储上一次的偏移量
82
- const internalsRef = useRef({
83
- offset: {
84
- x: 0,
85
- y: 0
86
- },
87
- isScrolling: false
88
- });
89
- const isDragRef = useRef(false);
90
- const [state, setState] = useState({
91
- width: dir === 'x' && typeof defaultWidth === 'number' ? defaultWidth - previousMargin - nextMargin : defaultWidth,
92
- height: dir === 'y' && typeof defaultHeight === 'number' ? defaultHeight - previousMargin - nextMargin : defaultHeight,
93
- // 真正的游标索引, 从0开始
94
- index: initIndex,
95
- total: totalElements,
96
- offset: {
97
- x: 0,
98
- y: 0
99
- },
100
- dir
101
- });
102
- /**
103
- * @desc: 开启下一次自动轮播
104
- */
105
- function createAutoPlay() {
106
- autoplayTimerRef.current && clearTimeout(autoplayTimerRef.current);
107
- autoplayTimerRef.current = setTimeout(() => {
108
- startAutoPlay();
109
- }, props.interval || 500);
110
- }
111
- useEffect(() => {
112
- // 确认这个是变化的props变化的时候才执行,还是初始化的时候就执行
113
- if (props.autoplay) {
114
- createAutoPlay();
115
- }
116
- }, [props.autoplay, props.current, state.index, state.width, state.height]);
117
- useEffect(() => {
118
- // 确认这个是变化的props变化的时候才执行,还是初始化的时候就执行
119
- if (!props.autoplay && props.current !== undefined && props.current !== state.index) {
120
- const initIndex = props.current || 0;
121
- // 这里要排除超过元素个数的设置
122
- const { nextIndex, nextOffset } = getMultiNextConfig(props.current);
123
- // 1. 安卓需要主动更新下内部状态, 2. IOS不能触发完wcrollTo之后立即updateState, 会造成滑动两次
124
- // 2. setTimeout 是fix 当再渲染过程中触发scrollTo失败的问题
125
- if (Platform.OS === 'ios') {
126
- needTriggerScrollEnd.current = false;
127
- setTimeout(() => {
128
- scrollViewRef.current?.scrollTo({
129
- ...nextOffset,
130
- animated: true
131
- });
132
- }, 50);
133
- }
134
- else {
135
- updateState(nextIndex, nextOffset);
136
- }
137
- }
138
- }, [props.current, state.width, state.height]);
139
- function getMultiNextConfig(target) {
140
- const step = state.dir === 'x' ? state.width : state.height;
141
- const targetPos = step * props.current;
142
- const targetOffset = {
143
- x: dir === 'x' ? targetPos : 0,
144
- y: dir === 'y' ? targetPos : 0
145
- };
146
- return {
147
- nextIndex: target,
148
- nextOffset: targetOffset
149
- };
150
- }
151
- /**
152
- * @desc: 更新状态: index和offset, 并响应索引变化的事件
153
- * scrollViewOffset: 移动到的目标位置
154
- */
155
- function updateIndex(scrollViewOffset, useIndex = false) {
156
- const { nextIndex, nextOffset } = getNextConfig(scrollViewOffset);
157
- updateState(nextIndex, nextOffset);
158
- // 更新完状态之后, 开启新的loop
159
- }
160
- /**
161
- * 更新索引状态
162
- */
163
- function updateState(index, offset) {
164
- internalsRef.current.offset = offset;
165
- setState((preState) => {
166
- const newState = {
167
- ...preState,
168
- index: index,
169
- // offset用来指示当前scrollView的偏移量
170
- offset: offset
171
- };
172
- return newState;
173
- });
174
- internalsRef.current.isScrolling = false;
175
- // getCustomEvent
176
- const eventData = getCustomEvent('change', {}, { detail: { current: index, source: 'touch' }, layoutRef: layoutRef });
177
- props.bindchange && props.bindchange(eventData);
178
- }
179
- /**
180
- * @desc: 获取下一个位置的索引、scrollView的contentOffset、scrollTo到的offset
181
- * @desc: 包括正循环、反向循环、不循环
182
- * 其中循环模式为了实现无缝链接, 会将结合contentOffset, 和 scrollTo的offset,
183
- * 先scrollTo一个位置的坐标, 然后通过updateIndex设置真正的index和内容的offset,视觉上是无缝
184
- */
185
- function getNextConfig(scrollViewOffset) {
186
- const step = state.dir === 'x' ? state.width : state.height;
187
- const currentOffset = state.offset;
188
- let nextIndex = state.index + 1;
189
- let nextOffset = currentOffset;
190
- // autoMoveOffset scrollView 滚动到前后增加的位置
191
- let autoMoveOffset = currentOffset;
192
- let isBack = false;
193
- let isAutoEnd = false;
194
- // 如果是循环反向的
195
- if (scrollViewOffset?.[state.dir] < currentOffset[state.dir]) {
196
- isBack = true;
197
- nextIndex = isBack ? nextIndex - 2 : nextIndex;
198
- }
199
- if (!props.circular) {
200
- nextOffset = Object.assign({}, currentOffset, { [state.dir]: step * nextIndex });
201
- }
202
- else {
203
- if (isBack) {
204
- if (nextIndex < 0) {
205
- // 反向: scollView 滚动到虚拟的位置
206
- autoMoveOffset = Object.assign({}, currentOffset, { [state.dir]: 0 });
207
- nextIndex = state.total - 1;
208
- // 反向: 数组最后一个index
209
- nextOffset = Object.assign({}, currentOffset, { [state.dir]: step * state.total });
210
- isAutoEnd = true;
211
- }
212
- else {
213
- // 反向非最后一个
214
- nextOffset = Object.assign({}, currentOffset, { [state.dir]: (nextIndex + 1) * step });
215
- }
216
- }
217
- else {
218
- if (nextIndex > state.total - 1) {
219
- autoMoveOffset = Object.assign({}, currentOffset, { [state.dir]: step * (nextIndex + 1) });
220
- nextIndex = 0;
221
- nextOffset = Object.assign({}, currentOffset, { [state.dir]: step });
222
- isAutoEnd = true;
223
- }
224
- else {
225
- // nextIndex = nextIndex,
226
- nextOffset = Object.assign({}, currentOffset, { [state.dir]: (nextIndex + 1) * step });
227
- }
228
- }
229
- }
230
- return {
231
- // 下一个要滚动到的实际元素的索引
232
- nextIndex,
233
- // 下一个要滚动到实际元素的offset
234
- nextOffset,
235
- // scrollTo一个位置的坐标, 虚拟元素的位置
236
- autoMoveOffset,
237
- isAutoEnd
238
- };
239
- }
240
- /**
241
- * @desc: 开启自动轮播
242
- */
243
- function startAutoPlay() {
244
- if (state.width && isNaN(+state.width)) {
245
- createAutoPlay();
246
- return;
247
- }
248
- if (!Array.isArray(props.children)) {
249
- return;
250
- }
251
- const step = state.dir === 'x' ? state.width : state.height;
252
- const { nextOffset, autoMoveOffset, isAutoEnd } = getNextConfig(state.offset);
253
- // 这里可以scroll到下一个元素, 但是把scrollView的偏移量在设置为content,视觉效果就没了吧
254
- if (Platform.OS === 'ios') {
255
- if (!isAutoEnd) {
256
- scrollViewRef.current?.scrollTo({ x: nextOffset.x, y: nextOffset.y, animated: true });
257
- }
258
- else {
259
- if (state.dir === 'x') {
260
- scrollViewRef.current?.scrollTo({ x: autoMoveOffset.x, y: autoMoveOffset.x, animated: true });
261
- }
262
- else {
263
- scrollViewRef.current?.scrollTo({ x: autoMoveOffset.y, y: autoMoveOffset.y, animated: true });
264
- }
265
- }
266
- }
267
- else {
268
- if (!isAutoEnd) {
269
- scrollViewRef.current?.scrollTo({ x: nextOffset.x, y: nextOffset.y, animated: true });
270
- onScrollEnd({
271
- nativeEvent: {
272
- contentOffset: {
273
- x: +nextOffset.x,
274
- y: +nextOffset.y
275
- }
276
- }
277
- });
278
- }
279
- else {
280
- // 安卓无法实现视觉的无缝连接, 只能回到真正的位置, 且安卓调用scrollTo不能触发onMomentumScrollEnd,还未找到为啥
281
- if (state.dir === 'x') {
282
- scrollViewRef.current?.scrollTo({ x: step, y: step, animated: true });
283
- }
284
- else {
285
- scrollViewRef.current?.scrollTo({ x: autoMoveOffset.x, y: step, animated: true });
286
- }
287
- updateState(0, nextOffset);
288
- }
289
- }
290
- }
291
- /**
292
- * 当用户开始拖动此视图时调用此函数, 更新当前在滚动态
293
- */
294
- function onScrollBegin() {
295
- internalsRef.current.isScrolling = true;
296
- }
297
- /**
298
- * 当用户开始拖动结束
299
- * 注意: 当手动调用scrollTo的时候, 安卓不会触发onMomentumScrollEnd, IOS会触发onMomentumScrollEnd
300
- */
301
- function onScrollEnd(event) {
302
- if (Platform.OS === 'ios' && !needTriggerScrollEnd.current) {
303
- const { nextIndex, nextOffset } = getMultiNextConfig(props.current);
304
- updateState(nextIndex, nextOffset);
305
- needTriggerScrollEnd.current = true;
306
- return;
307
- }
308
- if (totalElements === 1) {
309
- return;
310
- }
311
- internalsRef.current.isScrolling = false;
312
- // 用户手动滑动更新索引后,如果开启了自动轮播等重新开始
313
- updateIndex(event.nativeEvent.contentOffset, true);
314
- }
315
- /**
316
- * 当拖拽结束时,检测是否可滚动
317
- */
318
- function onScrollEndDrag(event) {
319
- const { contentOffset } = event.nativeEvent;
320
- const { index, total } = state;
321
- isDragRef.current = true;
322
- const internalOffset = internalsRef.current.offset;
323
- const previousOffset = props.horizontal ? internalOffset.x : internalOffset.y;
324
- const moveOffset = props.horizontal ? contentOffset.x : contentOffset.y;
325
- if (previousOffset === moveOffset && (index === 0 || index === total - 1)) {
326
- internalsRef.current.isScrolling = false;
327
- }
328
- }
329
- /**
330
- * @desc: 水平方向时,获取元素的布局,更新, 其中如果传递100%时需要依赖measure计算元算的宽高
331
- */
332
- function onWrapperLayout(e) {
333
- scrollViewRef.current?.measure((x, y, width, height, offsetLeft, offsetTop) => {
334
- layoutRef.current = { x, y, width, height, offsetLeft, offsetTop };
335
- const isWDiff = state.width !== width;
336
- const isHDiff = state.height !== height;
337
- if (isWDiff || isHDiff) {
338
- const changeState = {
339
- width: isWDiff ? width : state.width,
340
- height: isHDiff ? height : state.height
341
- };
342
- const attr = state.dir === 'x' ? 'width' : 'height';
343
- changeState[attr] = changeState[attr] - previousMargin - nextMargin;
344
- const correctOffset = Object.assign({}, state.offset, {
345
- [state.dir]: initOffsetIndex * (state.dir === 'x' ? changeState.width : changeState.height)
346
- });
347
- state.width = changeState.width;
348
- state.height = changeState.height;
349
- // 这里setState之后,会再触发重新渲染, renderScrollView会再次触发onScrollEnd,
350
- setState((preState) => {
351
- return {
352
- ...preState,
353
- width: changeState.width,
354
- height: changeState.height
355
- };
356
- });
357
- }
358
- props.getInnerLayout && props.getInnerLayout(layoutRef);
359
- });
360
- }
361
- function getOffset() {
362
- const step = state.dir === 'x' ? state.width : state.height;
363
- if (!step || Number.isNaN(+step))
364
- return [];
365
- const offsetArray = [];
366
- for (let i = 0; i < totalElements; i++) {
367
- offsetArray.push(i * step);
368
- }
369
- return offsetArray;
370
- }
371
- function renderScrollView(pages) {
372
- const offsetsArray = getOffset();
373
- const scrollElementProps = {
374
- ref: scrollViewRef,
375
- horizontal: props.horizontal,
376
- pagingEnabled: true,
377
- snapToOffsets: offsetsArray,
378
- decelerationRate: 0.99,
379
- showsHorizontalScrollIndicator: false,
380
- showsVerticalScrollIndicator: false,
381
- bounces: false,
382
- scrollsToTop: false,
383
- removeClippedSubviews: true,
384
- automaticallyAdjustContentInsets: false
385
- };
386
- const layoutStyle = dir === 'x' ? { width: defaultWidth, height: defaultHeight } : { width: defaultWidth };
387
- return (<Animated.ScrollView {...scrollElementProps} style={[layoutStyle]} overScrollMode="always" contentOffset={state.offset} onScrollBeginDrag={onScrollBegin} onMomentumScrollEnd={onScrollEnd} onScrollEndDrag={onScrollEndDrag}>
388
- {pages}
389
- </Animated.ScrollView>);
390
- }
391
- function renderPagination() {
392
- if (state.total <= 1)
393
- return null;
394
- const dots = [];
395
- const activeDotStyle = [{
396
- backgroundColor: props.activeDotColor || '#007aff',
397
- width: 8,
398
- height: 8,
399
- borderRadius: 4,
400
- marginLeft: 3,
401
- marginRight: 3,
402
- marginTop: 3,
403
- marginBottom: 3
404
- }];
405
- const dotStyle = [{
406
- backgroundColor: props.dotColor || 'rgba(0,0,0,.2)',
407
- width: 8,
408
- height: 8,
409
- borderRadius: 4,
410
- marginLeft: 3,
411
- marginRight: 3,
412
- marginTop: 3,
413
- marginBottom: 3
414
- }];
415
- for (let i = 0; i < state.total; i++) {
416
- if (i === state.index) {
417
- dots.push(<View style={activeDotStyle} key={i}></View>);
418
- }
419
- else {
420
- dots.push(<View style={dotStyle} key={i}></View>);
421
- }
422
- }
423
- return (<View pointerEvents="none" style={[styles['pagination_' + state.dir]]}>
424
- {dots}
425
- </View>);
426
- }
427
- function renderPages() {
428
- const { width, height } = state;
429
- const { children } = props;
430
- const { circular } = props;
431
- const pageStyle = { width: width, height: height };
432
- // 设置了previousMargin或者nextMargin,
433
- // 1. 元素的宽度是减去这两个数目之和
434
- // 2. previousMargin设置marginLeft正值, nextmargin设置marginRight负值
435
- // 3. 第一个元素设置previousMargin 和 nextMargin, 最后一个元素
436
- if (totalElements > 1 && Array.isArray(children)) {
437
- let arrElements = [];
438
- // pages = ["2", "0", "1", "2", "0"]
439
- const pages = Array.isArray(children) ? Object.keys(children) : [];
440
- /* 无限循环的时候 */
441
- if (circular) {
442
- pages.unshift(totalElements - 1 + '');
443
- pages.push('0');
444
- }
445
- arrElements = pages.map((page, i) => {
446
- const extraStyle = {};
447
- if (i === 0 && dir === 'x' && typeof width === 'number') {
448
- previousMargin && (extraStyle.marginLeft = previousMargin);
449
- }
450
- else if (i === pages.length - 1 && typeof width === 'number') {
451
- nextMargin && (extraStyle.marginRight = nextMargin);
452
- }
453
- return (<View style={[pageStyle, styles.slide, extraStyle]} key={'page' + i}>
454
- {wrapChildren({
455
- children: children[+page]
456
- }, {
457
- hasVarDec,
458
- varContext: varContextRef.current,
459
- textStyle,
460
- textProps
461
- })}
462
- </View>);
463
- });
464
- return arrElements;
465
- }
466
- else {
467
- const realElement = (<View style={pageStyle} key={0}>
468
- {children}
469
- </View>);
470
- return realElement;
471
- }
472
- }
473
- const pages = renderPages();
474
- return (<View style={[normalStyle, innerStyle, layoutStyle]} {...layoutProps}>
475
- {renderScrollView(pages)}
476
- {props.showsPagination && renderPagination()}
477
- </View>);
478
- });
479
- _Carouse.displayName = 'MpxCarouse';
480
- export default _Carouse;