@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.
- package/lib/platform/template/wx/component-config/movable-view.js +8 -1
- package/lib/platform/template/wx/component-config/picker-view.js +1 -5
- package/lib/platform/template/wx/component-config/scroll-view.js +1 -1
- package/lib/platform/template/wx/index.js +0 -4
- package/lib/runtime/components/react/context.ts +8 -0
- package/lib/runtime/components/react/dist/context.js +2 -0
- package/lib/runtime/components/react/dist/getInnerListeners.js +34 -31
- package/lib/runtime/components/react/dist/mpx-button.jsx +16 -44
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +93 -58
- package/lib/runtime/components/react/dist/mpx-navigator.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +35 -0
- package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +151 -127
- package/lib/runtime/components/react/dist/mpx-picker-view.jsx +38 -34
- package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -11
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +11 -4
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +31 -8
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +670 -0
- package/lib/runtime/components/react/dist/mpx-view.jsx +15 -53
- package/lib/runtime/components/react/dist/pickerFaces.js +7 -6
- package/lib/runtime/components/react/dist/pickerVIewContext.js +14 -0
- package/lib/runtime/components/react/dist/pickerViewIndicator.jsx +23 -0
- package/lib/runtime/components/react/dist/pickerViewMask.jsx +18 -0
- package/lib/runtime/components/react/dist/useAnimationHooks.js +20 -2
- package/lib/runtime/components/react/dist/utils.jsx +74 -11
- package/lib/runtime/components/react/getInnerListeners.ts +43 -32
- package/lib/runtime/components/react/mpx-button.tsx +20 -57
- package/lib/runtime/components/react/mpx-movable-view.tsx +119 -74
- package/lib/runtime/components/react/mpx-navigator.tsx +1 -1
- package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +76 -0
- package/lib/runtime/components/react/mpx-picker-view-column.tsx +206 -183
- package/lib/runtime/components/react/mpx-picker-view.tsx +49 -48
- package/lib/runtime/components/react/mpx-rich-text/index.tsx +12 -18
- package/lib/runtime/components/react/mpx-scroll-view.tsx +21 -10
- package/lib/runtime/components/react/mpx-swiper-item.tsx +45 -11
- package/lib/runtime/components/react/mpx-swiper.tsx +742 -0
- package/lib/runtime/components/react/mpx-view.tsx +18 -65
- package/lib/runtime/components/react/pickerFaces.ts +10 -7
- package/lib/runtime/components/react/pickerVIewContext.ts +27 -0
- package/lib/runtime/components/react/pickerViewIndicator.tsx +34 -0
- package/lib/runtime/components/react/pickerViewMask.tsx +30 -0
- package/lib/runtime/components/react/types/{getInnerListeners.ts → getInnerListeners.d.ts} +4 -5
- package/lib/runtime/components/react/types/global.d.ts +10 -0
- package/lib/runtime/components/react/useAnimationHooks.ts +24 -3
- package/lib/runtime/components/react/utils.tsx +85 -12
- package/lib/runtime/components/web/mpx-checkbox.vue +1 -1
- package/lib/runtime/components/web/mpx-picker-view-column.vue +9 -4
- package/lib/template-compiler/compiler.js +61 -13
- package/lib/wxss/loader.js +15 -2
- package/package.json +3 -3
- package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +0 -480
- package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +0 -68
- package/lib/runtime/components/react/dist/mpx-swiper/type.js +0 -1
- package/lib/runtime/components/react/dist/pickerOverlay.jsx +0 -21
- package/lib/runtime/components/react/dist/types/common.js +0 -1
- package/lib/runtime/components/react/dist/types/getInnerListeners.js +0 -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
- package/lib/runtime/components/react/pickerOverlay.tsx +0 -32
- /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
|
|
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 (
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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)
|
package/lib/wxss/loader.js
CHANGED
|
@@ -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',
|
|
169
|
+
preset: ['cssnano-preset-default', presetOptimisation]
|
|
157
170
|
}
|
|
158
171
|
if (minimizeOptions.advanced) {
|
|
159
172
|
cssnanoConfig = {
|
|
160
|
-
preset: ['cssnano-preset-advanced',
|
|
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.
|
|
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.
|
|
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": "
|
|
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;
|