@mpxjs/webpack-plugin 2.10.15-5 → 2.10.15-prelease.1
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 +1 -1
- package/lib/dependencies/ImportDependency.js +102 -0
- package/lib/{retry-runtime-module.js → dependencies/RetryRuntimeModule.js} +1 -1
- package/lib/index.js +13 -15
- package/lib/platform/template/wx/component-config/progress.js +12 -0
- package/lib/platform/template/wx/component-config/slider.js +12 -0
- package/lib/platform/template/wx/component-config/unsupported.js +1 -1
- package/lib/platform/template/wx/index.js +3 -1
- package/lib/resolver/AddEnvPlugin.js +13 -0
- package/lib/resolver/AddModePlugin.js +18 -0
- package/lib/runtime/components/react/dist/getInnerListeners.js +35 -21
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +102 -34
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +3 -5
- package/lib/runtime/components/react/dist/mpx-progress.jsx +163 -0
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +38 -7
- package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +9 -16
- package/lib/runtime/components/react/dist/mpx-view.jsx +7 -10
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +20 -1
- package/lib/runtime/components/react/getInnerListeners.ts +41 -22
- package/lib/runtime/components/react/mpx-movable-view.tsx +156 -48
- package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +4 -8
- package/lib/runtime/components/react/mpx-progress.tsx +259 -0
- package/lib/runtime/components/react/mpx-scroll-view.tsx +39 -7
- package/lib/runtime/components/react/mpx-slider.tsx +444 -0
- package/lib/runtime/components/react/mpx-swiper.tsx +9 -16
- package/lib/runtime/components/react/mpx-view.tsx +7 -10
- package/lib/runtime/components/react/mpx-web-view.tsx +22 -1
- package/lib/runtime/components/react/types/getInnerListeners.d.ts +7 -2
- package/lib/runtime/components/web/mpx-input.vue +14 -0
- package/lib/runtime/components/web/mpx-movable-area.vue +43 -19
- package/lib/runtime/components/web/mpx-movable-view.vue +93 -3
- package/lib/runtime/components/web/mpx-scroll-view.vue +7 -1
- package/lib/runtime/components/web/mpx-swiper.vue +1 -2
- package/lib/runtime/components/web/mpx-video.vue +12 -1
- package/lib/runtime/components/web/mpx-web-view.vue +3 -3
- package/lib/runtime/optionProcessor.js +3 -1
- package/lib/runtime/optionProcessorReact.js +4 -2
- package/lib/template-compiler/compiler.js +69 -35
- package/lib/utils/chain-assign.js +47 -0
- package/lib/utils/check-core-version-match.js +75 -15
- package/lib/wxs/pre-loader.js +5 -5
- package/lib/wxss/utils.js +1 -1
- package/package.json +3 -2
- package/lib/dependencies/ImportDependencyTemplate.js +0 -50
|
@@ -6,7 +6,7 @@ const toPosix = require('../utils/to-posix')
|
|
|
6
6
|
const async = require('async')
|
|
7
7
|
const parseRequest = require('../utils/parse-request')
|
|
8
8
|
const hasOwn = require('../utils/has-own')
|
|
9
|
-
const { RetryRuntimeGlobal } = require('
|
|
9
|
+
const { RetryRuntimeGlobal } = require('./RetryRuntimeModule')
|
|
10
10
|
|
|
11
11
|
class DynamicEntryDependency extends NullDependency {
|
|
12
12
|
constructor (range, request, entryType, outputPath = '', packageRoot = '', relativePath = '', context = '', extraOptions = {}) {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const Dependency = require('webpack/lib/Dependency')
|
|
2
|
+
const makeSerializable = require('webpack/lib/util/makeSerializable')
|
|
3
|
+
const ModuleDependency = require('webpack/lib/dependencies/ModuleDependency')
|
|
4
|
+
const { RetryRuntimeGlobal } = require('./RetryRuntimeModule')
|
|
5
|
+
|
|
6
|
+
class ImportDependency extends ModuleDependency {
|
|
7
|
+
/**
|
|
8
|
+
* @param {string} request the request
|
|
9
|
+
* @param {[number, number]} range expression range
|
|
10
|
+
* @param {string[][]=} referencedExports list of referenced exports
|
|
11
|
+
*/
|
|
12
|
+
constructor (request, range, referencedExports, extraOptions) {
|
|
13
|
+
super(request)
|
|
14
|
+
this.range = range
|
|
15
|
+
this.referencedExports = referencedExports
|
|
16
|
+
this.extraOptions = extraOptions
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get type () {
|
|
20
|
+
return 'import()'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get category () {
|
|
24
|
+
return 'esm'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Returns list of exports referenced by this dependency
|
|
29
|
+
* @param {ModuleGraph} moduleGraph module graph
|
|
30
|
+
* @param {RuntimeSpec} runtime the runtime for which the module is analysed
|
|
31
|
+
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
|
32
|
+
*/
|
|
33
|
+
getReferencedExports (moduleGraph, runtime) {
|
|
34
|
+
return this.referencedExports
|
|
35
|
+
? this.referencedExports.map((e) => ({
|
|
36
|
+
name: e,
|
|
37
|
+
canMangle: false
|
|
38
|
+
}))
|
|
39
|
+
: Dependency.EXPORTS_OBJECT_REFERENCED
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
serialize (context) {
|
|
43
|
+
context.write(this.range)
|
|
44
|
+
context.write(this.referencedExports)
|
|
45
|
+
context.write(this.extraOptions)
|
|
46
|
+
super.serialize(context)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
deserialize (context) {
|
|
50
|
+
this.range = context.read()
|
|
51
|
+
this.referencedExports = context.read()
|
|
52
|
+
this.extraOptions = context.read()
|
|
53
|
+
super.deserialize(context)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
makeSerializable(ImportDependency, '@mpxjs/webpack-plugin/lib/dependencies/ImportDependency')
|
|
58
|
+
|
|
59
|
+
ImportDependency.Template = class ImportDependencyTemplate extends (
|
|
60
|
+
ModuleDependency.Template
|
|
61
|
+
) {
|
|
62
|
+
/**
|
|
63
|
+
* @param {Dependency} dependency the dependency for which the template should be applied
|
|
64
|
+
* @param {ReplaceSource} source the current replace source which can be modified
|
|
65
|
+
* @param {DependencyTemplateContext} templateContext the context object
|
|
66
|
+
* @returns {void}
|
|
67
|
+
*/
|
|
68
|
+
apply (
|
|
69
|
+
dependency,
|
|
70
|
+
source,
|
|
71
|
+
{ runtimeTemplate, module, moduleGraph, chunkGraph, runtimeRequirements }
|
|
72
|
+
) {
|
|
73
|
+
const dep = /** @type {ImportDependency} */ (dependency)
|
|
74
|
+
const block = /** @type {AsyncDependenciesBlock} */ (
|
|
75
|
+
moduleGraph.getParentBlock(dep)
|
|
76
|
+
)
|
|
77
|
+
let content = runtimeTemplate.moduleNamespacePromise({
|
|
78
|
+
chunkGraph,
|
|
79
|
+
block: block,
|
|
80
|
+
module: /** @type {Module} */ (moduleGraph.getModule(dep)),
|
|
81
|
+
request: dep.request,
|
|
82
|
+
strict: /** @type {BuildMeta} */ (module.buildMeta).strictHarmonyModule,
|
|
83
|
+
message: 'import()',
|
|
84
|
+
runtimeRequirements
|
|
85
|
+
})
|
|
86
|
+
// replace fakeType by 9 to fix require.async to commonjs2 module like 'module.exports = function(){...}'
|
|
87
|
+
content = content.replace(/(__webpack_require__\.t\.bind\(.+,\s*)(\d+)(\s*\))/, (_, p1, p2, p3) => {
|
|
88
|
+
return p1 + '9' + p3
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// require.async 的场景且配置了重试次数才注入 RetryRuntimeModule
|
|
92
|
+
const extraOptions = dep.extraOptions || {}
|
|
93
|
+
if (extraOptions.isRequireAsync && extraOptions.retryRequireAsync && extraOptions.retryRequireAsync.times > 0) {
|
|
94
|
+
runtimeRequirements.add(RetryRuntimeGlobal)
|
|
95
|
+
content = `${RetryRuntimeGlobal}(function() { return ${content} }, ${extraOptions.retryRequireAsync.times}, ${extraOptions.retryRequireAsync.interval})`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
source.replace(dep.range[0], dep.range[1] - 1, content)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = ImportDependency
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const Template = require('webpack/lib/Template')
|
|
2
2
|
const RuntimeModule = require('webpack/lib/RuntimeModule')
|
|
3
3
|
|
|
4
|
-
const RetryRuntimeGlobal = '__webpack_require__.
|
|
4
|
+
const RetryRuntimeGlobal = '__webpack_require__.mpxR'
|
|
5
5
|
|
|
6
6
|
class RetryRuntimeModule extends RuntimeModule {
|
|
7
7
|
constructor () {
|
package/lib/index.js
CHANGED
|
@@ -14,8 +14,7 @@ const EntryPlugin = require('webpack/lib/EntryPlugin')
|
|
|
14
14
|
const JavascriptModulesPlugin = require('webpack/lib/javascript/JavascriptModulesPlugin')
|
|
15
15
|
const FlagEntryExportAsUsedPlugin = require('webpack/lib/FlagEntryExportAsUsedPlugin')
|
|
16
16
|
const FileSystemInfo = require('webpack/lib/FileSystemInfo')
|
|
17
|
-
const ImportDependency = require('
|
|
18
|
-
const ImportDependencyTemplate = require('./dependencies/ImportDependencyTemplate')
|
|
17
|
+
const ImportDependency = require('./dependencies/ImportDependency')
|
|
19
18
|
const AsyncDependenciesBlock = require('webpack/lib/AsyncDependenciesBlock')
|
|
20
19
|
const ProvidePlugin = require('webpack/lib/ProvidePlugin')
|
|
21
20
|
const normalize = require('./utils/normalize')
|
|
@@ -77,8 +76,10 @@ const VirtualModulesPlugin = require('webpack-virtual-modules')
|
|
|
77
76
|
const RuntimeGlobals = require('webpack/lib/RuntimeGlobals')
|
|
78
77
|
const LoadAsyncChunkModule = require('./react/LoadAsyncChunkModule')
|
|
79
78
|
const ExternalModule = require('webpack/lib/ExternalModule')
|
|
80
|
-
const { RetryRuntimeModule, RetryRuntimeGlobal } = require('./
|
|
81
|
-
require('./utils/check-core-version-match')
|
|
79
|
+
const { RetryRuntimeModule, RetryRuntimeGlobal } = require('./dependencies/RetryRuntimeModule')
|
|
80
|
+
const checkVersionCompatibility = require('./utils/check-core-version-match')
|
|
81
|
+
|
|
82
|
+
checkVersionCompatibility()
|
|
82
83
|
|
|
83
84
|
const isProductionLikeMode = options => {
|
|
84
85
|
return options.mode === 'production' || !options.mode
|
|
@@ -696,7 +697,8 @@ class MpxWebpackPlugin {
|
|
|
696
697
|
compilation.dependencyFactories.set(RequireExternalDependency, new NullFactory())
|
|
697
698
|
compilation.dependencyTemplates.set(RequireExternalDependency, new RequireExternalDependency.Template())
|
|
698
699
|
|
|
699
|
-
compilation.
|
|
700
|
+
compilation.dependencyFactories.set(ImportDependency, normalModuleFactory)
|
|
701
|
+
compilation.dependencyTemplates.set(ImportDependency, new ImportDependency.Template())
|
|
700
702
|
})
|
|
701
703
|
|
|
702
704
|
compiler.hooks.thisCompilation.tap('MpxWebpackPlugin', (compilation, { normalModuleFactory }) => {
|
|
@@ -784,7 +786,7 @@ class MpxWebpackPlugin {
|
|
|
784
786
|
removedChunks: [],
|
|
785
787
|
forceProxyEventRules: this.options.forceProxyEventRules,
|
|
786
788
|
// 若配置disableRequireAsync=true, 则全平台构建不支持异步分包
|
|
787
|
-
supportRequireAsync: !this.options.disableRequireAsync && (this.options.mode === 'wx' || this.options.mode === 'ali' || this.options.mode === 'tt' || isWeb(this.options.mode) || isReact(this.options.mode)),
|
|
789
|
+
supportRequireAsync: !this.options.disableRequireAsync && (this.options.mode === 'wx' || this.options.mode === 'ali' || this.options.mode === 'tt' || isWeb(this.options.mode) || (isReact(this.options.mode) && this.options.rnConfig.supportSubpackage)),
|
|
788
790
|
partialCompileRules: this.options.partialCompileRules,
|
|
789
791
|
collectDynamicEntryInfo: ({ resource, packageName, filename, entryType, hasAsync }) => {
|
|
790
792
|
const curInfo = mpx.dynamicEntryInfo[packageName] = mpx.dynamicEntryInfo[packageName] || {
|
|
@@ -1448,16 +1450,9 @@ class MpxWebpackPlugin {
|
|
|
1448
1450
|
// 删除root query
|
|
1449
1451
|
if (queryObj.root) request = addQuery(request, {}, false, ['root'])
|
|
1450
1452
|
// wx、ali和web平台支持require.async,其余平台使用CommonJsAsyncDependency进行模拟抹平
|
|
1451
|
-
|
|
1452
|
-
? (mpx.supportRequireAsync && this.options.rnConfig.supportSubpackage)
|
|
1453
|
-
: mpx.supportRequireAsync
|
|
1454
|
-
if (supportRequireAsync) {
|
|
1453
|
+
if (mpx.supportRequireAsync) {
|
|
1455
1454
|
if (isWeb(mpx.mode) || isReact(mpx.mode)) {
|
|
1456
1455
|
if (isReact(mpx.mode)) tarRoot = transSubpackage(mpx.transSubpackageRules, tarRoot)
|
|
1457
|
-
request = addQuery(request, {
|
|
1458
|
-
isRequireAsync: true,
|
|
1459
|
-
retryRequireAsync: JSON.stringify(this.options.retryRequireAsync)
|
|
1460
|
-
})
|
|
1461
1456
|
const depBlock = new AsyncDependenciesBlock(
|
|
1462
1457
|
{
|
|
1463
1458
|
name: tarRoot + '/index'
|
|
@@ -1465,7 +1460,10 @@ class MpxWebpackPlugin {
|
|
|
1465
1460
|
expr.loc,
|
|
1466
1461
|
request
|
|
1467
1462
|
)
|
|
1468
|
-
const dep = new ImportDependency(request, expr.range
|
|
1463
|
+
const dep = new ImportDependency(request, expr.range, undefined, {
|
|
1464
|
+
isRequireAsync: true,
|
|
1465
|
+
retryRequireAsync: this.options.retryRequireAsync
|
|
1466
|
+
})
|
|
1469
1467
|
dep.loc = expr.loc
|
|
1470
1468
|
depBlock.addDependency(dep)
|
|
1471
1469
|
parser.state.current.addBlock(depBlock)
|
|
@@ -17,6 +17,18 @@ module.exports = function ({ print }) {
|
|
|
17
17
|
el.isBuiltIn = true
|
|
18
18
|
return 'mpx-progress'
|
|
19
19
|
},
|
|
20
|
+
android (tag, { el }) {
|
|
21
|
+
el.isBuiltIn = true
|
|
22
|
+
return 'mpx-progress'
|
|
23
|
+
},
|
|
24
|
+
harmony (tag, { el }) {
|
|
25
|
+
el.isBuiltIn = true
|
|
26
|
+
return 'mpx-progress'
|
|
27
|
+
},
|
|
28
|
+
ios (tag, { el }) {
|
|
29
|
+
el.isBuiltIn = true
|
|
30
|
+
return 'mpx-progress'
|
|
31
|
+
},
|
|
20
32
|
props: [
|
|
21
33
|
{
|
|
22
34
|
test: /^(border-radius|font-size|color|active-mode|duration)$/,
|
|
@@ -8,6 +8,18 @@ module.exports = function ({ print }) {
|
|
|
8
8
|
el.isBuiltIn = true
|
|
9
9
|
return 'mpx-slider'
|
|
10
10
|
},
|
|
11
|
+
android (tag, { el }) {
|
|
12
|
+
el.isBuiltIn = true
|
|
13
|
+
return 'mpx-slider'
|
|
14
|
+
},
|
|
15
|
+
harmony (tag, { el }) {
|
|
16
|
+
el.isBuiltIn = true
|
|
17
|
+
return 'mpx-slider'
|
|
18
|
+
},
|
|
19
|
+
ios (tag, { el }) {
|
|
20
|
+
el.isBuiltIn = true
|
|
21
|
+
return 'mpx-slider'
|
|
22
|
+
},
|
|
11
23
|
props: [
|
|
12
24
|
{
|
|
13
25
|
test: /^color$/,
|
|
@@ -11,7 +11,7 @@ const JD_UNSUPPORTED_TAG_NAME_ARR = ['functional-page-navigator', 'live-pusher',
|
|
|
11
11
|
// 快应用不支持的标签集合
|
|
12
12
|
const QA_UNSUPPORTED_TAG_NAME_ARR = ['movable-view', 'movable-area', 'open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'cover-image']
|
|
13
13
|
// RN不支持的标签集合
|
|
14
|
-
const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', '
|
|
14
|
+
const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'audio', 'camera', 'match-media', 'page-container', 'editor', 'keyboard-accessory', 'map']
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* @param {function(object): function} print
|
|
@@ -463,7 +463,9 @@ module.exports = function getSpec ({ warn, error }) {
|
|
|
463
463
|
ali (prefix) {
|
|
464
464
|
const prefixMap = {
|
|
465
465
|
bind: 'on',
|
|
466
|
-
catch: 'catch'
|
|
466
|
+
catch: 'catch',
|
|
467
|
+
'capture-catch': 'capture-catch',
|
|
468
|
+
'capture-bind': 'capture-on'
|
|
467
469
|
}
|
|
468
470
|
if (!prefixMap[prefix]) {
|
|
469
471
|
error(`Ali environment does not support [${prefix}] event handling!`)
|
|
@@ -16,6 +16,8 @@ module.exports = class AddEnvPlugin {
|
|
|
16
16
|
apply (resolver) {
|
|
17
17
|
const target = resolver.ensureHook(this.target)
|
|
18
18
|
const env = this.env
|
|
19
|
+
const envPattern = new RegExp(`\\.${env}(\\.|$)`)
|
|
20
|
+
|
|
19
21
|
resolver.getHook(this.source).tapAsync('AddEnvPlugin', (request, resolveContext, callback) => {
|
|
20
22
|
if (request.env) {
|
|
21
23
|
return callback()
|
|
@@ -32,11 +34,22 @@ module.exports = class AddEnvPlugin {
|
|
|
32
34
|
}
|
|
33
35
|
// 当前资源没有后缀名或者路径不符合fileConditionRules规则时,直接返回
|
|
34
36
|
if (!extname || !matchCondition(resourcePath, this.fileConditionRules)) return callback()
|
|
37
|
+
|
|
35
38
|
const queryObj = parseQuery(request.query || '?')
|
|
36
39
|
queryObj.infix = `${queryObj.infix || ''}.${env}`
|
|
40
|
+
|
|
41
|
+
const resourceBasename = path.basename(resourcePath)
|
|
42
|
+
// 如果 infix 与 resourcePath 无法匹配,则认为未命中env
|
|
43
|
+
if (envPattern.test(resourceBasename) && resourceBasename.includes(queryObj.infix)) {
|
|
44
|
+
request.query = stringifyQuery(queryObj)
|
|
45
|
+
request.env = obj.env
|
|
46
|
+
return callback()
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
obj.query = stringifyQuery(queryObj)
|
|
38
50
|
obj.path = addInfix(resourcePath, env, extname)
|
|
39
51
|
obj.relativePath = request.relativePath && addInfix(request.relativePath, env, extname)
|
|
52
|
+
|
|
40
53
|
resolver.doResolve(target, Object.assign({}, request, obj), 'add env: ' + env, resolveContext, callback)
|
|
41
54
|
})
|
|
42
55
|
}
|
|
@@ -17,6 +17,9 @@ module.exports = class AddModePlugin {
|
|
|
17
17
|
const target = resolver.ensureHook(this.target)
|
|
18
18
|
const { options = {}, mode } = this
|
|
19
19
|
const { defaultMode, fileConditionRules, implicitMode } = options
|
|
20
|
+
const modePattern = new RegExp(`\\.${mode}(\\.|$)`)
|
|
21
|
+
const defaultModePattern = new RegExp(`\\.${defaultMode}(\\.|$)`)
|
|
22
|
+
|
|
20
23
|
resolver.getHook(this.source).tapAsync('AddModePlugin', (request, resolveContext, callback) => {
|
|
21
24
|
if (request.mode || request.env) {
|
|
22
25
|
return callback()
|
|
@@ -33,13 +36,28 @@ module.exports = class AddModePlugin {
|
|
|
33
36
|
}
|
|
34
37
|
// 当前资源没有后缀名或者路径不符合fileConditionRules规则时,直接返回
|
|
35
38
|
if (!extname || !matchCondition(resourcePath, fileConditionRules)) return callback()
|
|
39
|
+
|
|
36
40
|
const queryObj = parseQuery(request.query || '?')
|
|
37
41
|
const queryInfix = queryObj.infix
|
|
38
42
|
if (!implicitMode) queryObj.mode = mode
|
|
39
43
|
queryObj.infix = `${queryInfix || ''}.${mode}`
|
|
44
|
+
|
|
45
|
+
// 如果已经确认是mode后缀的文件,添加query与mode后直接返回
|
|
46
|
+
if (modePattern.test(path.basename(resourcePath))) {
|
|
47
|
+
request.query = stringifyQuery(queryObj)
|
|
48
|
+
request.mode = obj.mode
|
|
49
|
+
return callback()
|
|
50
|
+
} else if (defaultMode && defaultModePattern.test(path.basename(resourcePath))) {
|
|
51
|
+
queryObj.infix = `${queryInfix || ''}.${defaultMode}`
|
|
52
|
+
request.query = stringifyQuery(queryObj)
|
|
53
|
+
request.mode = obj.mode
|
|
54
|
+
return callback()
|
|
55
|
+
}
|
|
56
|
+
|
|
40
57
|
obj.query = stringifyQuery(queryObj)
|
|
41
58
|
obj.path = addInfix(resourcePath, mode, extname)
|
|
42
59
|
obj.relativePath = request.relativePath && addInfix(request.relativePath, mode, extname)
|
|
60
|
+
|
|
43
61
|
resolver.doResolve(target, Object.assign({}, request, obj), 'add mode: ' + mode, resolveContext, (err, result) => {
|
|
44
62
|
if (defaultMode && !result) {
|
|
45
63
|
queryObj.infix = `${queryInfix || ''}.${defaultMode}`
|
|
@@ -3,7 +3,8 @@ import { collectDataset } from '@mpxjs/utils';
|
|
|
3
3
|
import { omit, extendObject, useNavigation } from './utils';
|
|
4
4
|
import eventConfigMap from './event.config';
|
|
5
5
|
const globalEventState = {
|
|
6
|
-
needPress: true
|
|
6
|
+
needPress: true,
|
|
7
|
+
identifier: null
|
|
7
8
|
};
|
|
8
9
|
const getTouchEvent = (type, event, config) => {
|
|
9
10
|
const { navigation, propsRef, layoutRef } = config;
|
|
@@ -94,36 +95,48 @@ function checkIsNeedPress(e, type, ref) {
|
|
|
94
95
|
ref.current.startTimer[type] = null;
|
|
95
96
|
}
|
|
96
97
|
}
|
|
98
|
+
function shouldHandleTapEvent(e, eventConfig) {
|
|
99
|
+
const { identifier } = e.nativeEvent.changedTouches[0];
|
|
100
|
+
return eventConfig.tap && globalEventState.identifier === identifier;
|
|
101
|
+
}
|
|
97
102
|
function handleTouchstart(e, type, eventConfig) {
|
|
98
|
-
// 阻止事件被释放放回对象池,导致对象复用 _stoppedEventTypes 状态被保留
|
|
99
103
|
e.persist();
|
|
100
104
|
const { innerRef } = eventConfig;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
const touch = e.nativeEvent.changedTouches[0];
|
|
106
|
+
const { identifier } = touch;
|
|
107
|
+
const isSingle = e.nativeEvent.touches.length <= 1;
|
|
108
|
+
if (isSingle) {
|
|
109
|
+
// 仅在 touchstart 记录第一个单指触摸点
|
|
110
|
+
globalEventState.identifier = identifier;
|
|
111
|
+
globalEventState.needPress = true;
|
|
112
|
+
innerRef.current.mpxPressInfo.detail = {
|
|
113
|
+
x: touch.pageX,
|
|
114
|
+
y: touch.pageY
|
|
115
|
+
};
|
|
116
|
+
}
|
|
106
117
|
handleEmitEvent('touchstart', e, type, eventConfig);
|
|
107
118
|
if (eventConfig.longpress) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
// 只有单指触摸时才启动长按定时器
|
|
120
|
+
if (isSingle) {
|
|
121
|
+
if (e._stoppedEventTypes?.has('longpress')) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (eventConfig.longpress.hasCatch) {
|
|
125
|
+
e._stoppedEventTypes = e._stoppedEventTypes || new Set();
|
|
126
|
+
e._stoppedEventTypes.add('longpress');
|
|
127
|
+
}
|
|
128
|
+
innerRef.current.startTimer[type] && clearTimeout(innerRef.current.startTimer[type]);
|
|
129
|
+
innerRef.current.startTimer[type] = setTimeout(() => {
|
|
130
|
+
globalEventState.needPress = false;
|
|
131
|
+
handleEmitEvent('longpress', e, type, eventConfig);
|
|
132
|
+
}, 350);
|
|
114
133
|
}
|
|
115
|
-
innerRef.current.startTimer[type] && clearTimeout(innerRef.current.startTimer[type]);
|
|
116
|
-
innerRef.current.startTimer[type] = setTimeout(() => {
|
|
117
|
-
// 只要触发过longpress, 全局就不再触发tap
|
|
118
|
-
globalEventState.needPress = false;
|
|
119
|
-
handleEmitEvent('longpress', e, type, eventConfig);
|
|
120
|
-
}, 350);
|
|
121
134
|
}
|
|
122
135
|
}
|
|
123
136
|
function handleTouchmove(e, type, eventConfig) {
|
|
124
137
|
const { innerRef } = eventConfig;
|
|
125
138
|
handleEmitEvent('touchmove', e, type, eventConfig);
|
|
126
|
-
if (eventConfig
|
|
139
|
+
if (shouldHandleTapEvent(e, eventConfig)) {
|
|
127
140
|
checkIsNeedPress(e, type, innerRef);
|
|
128
141
|
}
|
|
129
142
|
}
|
|
@@ -131,7 +144,8 @@ function handleTouchend(e, type, eventConfig) {
|
|
|
131
144
|
const { innerRef, disableTap } = eventConfig;
|
|
132
145
|
handleEmitEvent('touchend', e, type, eventConfig);
|
|
133
146
|
innerRef.current.startTimer[type] && clearTimeout(innerRef.current.startTimer[type]);
|
|
134
|
-
|
|
147
|
+
// 只有单指触摸结束时才触发 tap
|
|
148
|
+
if (shouldHandleTapEvent(e, eventConfig)) {
|
|
135
149
|
checkIsNeedPress(e, type, innerRef);
|
|
136
150
|
if (!globalEventState.needPress || (type === 'bubble' && disableTap) || e._stoppedEventTypes?.has('tap')) {
|
|
137
151
|
return;
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* ✔ out-of-bounds
|
|
5
5
|
* ✔ x
|
|
6
6
|
* ✔ y
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* ✔ damping
|
|
8
|
+
* ✔ friction
|
|
9
9
|
* ✔ disabled
|
|
10
10
|
* ✘ scale
|
|
11
11
|
* ✘ scale-min
|
|
@@ -24,8 +24,96 @@ import useNodesRef from './useNodesRef';
|
|
|
24
24
|
import { MovableAreaContext } from './context';
|
|
25
25
|
import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, flatGesture, extendObject, omit, useNavigation, useRunOnJSCallback } from './utils';
|
|
26
26
|
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
|
|
27
|
-
import Animated, { useSharedValue, useAnimatedStyle,
|
|
27
|
+
import Animated, { useSharedValue, useAnimatedStyle, runOnJS, runOnUI, withTiming, Easing } from 'react-native-reanimated';
|
|
28
28
|
import { collectDataset, noop } from '@mpxjs/utils';
|
|
29
|
+
// 超出边界处理函数,参考微信小程序的超出边界衰减效果
|
|
30
|
+
const applyBoundaryDecline = (newValue, range) => {
|
|
31
|
+
'worklet';
|
|
32
|
+
const decline = (distance) => {
|
|
33
|
+
'worklet';
|
|
34
|
+
return Math.sqrt(Math.abs(distance));
|
|
35
|
+
};
|
|
36
|
+
if (newValue < range[0]) {
|
|
37
|
+
const overDistance = range[0] - newValue;
|
|
38
|
+
return range[0] - decline(overDistance);
|
|
39
|
+
}
|
|
40
|
+
else if (newValue > range[1]) {
|
|
41
|
+
const overDistance = newValue - range[1];
|
|
42
|
+
return range[1] + decline(overDistance);
|
|
43
|
+
}
|
|
44
|
+
return newValue;
|
|
45
|
+
};
|
|
46
|
+
// 参考微信小程序的弹簧阻尼系统实现
|
|
47
|
+
const withWechatSpring = (toValue, dampingParam = 20, callback) => {
|
|
48
|
+
'worklet';
|
|
49
|
+
// 弹簧参数计算
|
|
50
|
+
const m = 1; // 质量
|
|
51
|
+
const k = 9 * Math.pow(dampingParam, 2) / 40; // 弹簧系数
|
|
52
|
+
const c = dampingParam; // 阻尼系数
|
|
53
|
+
// 判别式:r = c² - 4mk
|
|
54
|
+
const discriminant = c * c - 4 * m * k;
|
|
55
|
+
// 计算动画持续时间和缓动函数
|
|
56
|
+
let duration;
|
|
57
|
+
let easingFunction;
|
|
58
|
+
if (Math.abs(discriminant) < 0.01) {
|
|
59
|
+
// 临界阻尼 (discriminant ≈ 0)
|
|
60
|
+
// 使用cubic-out模拟临界阻尼的平滑过渡
|
|
61
|
+
duration = Math.max(350, Math.min(800, 2000 / dampingParam));
|
|
62
|
+
easingFunction = Easing.out(Easing.cubic);
|
|
63
|
+
}
|
|
64
|
+
else if (discriminant > 0) {
|
|
65
|
+
// 过阻尼 (discriminant > 0)
|
|
66
|
+
// 使用指数缓动模拟过阻尼的缓慢收敛
|
|
67
|
+
duration = Math.max(450, Math.min(1000, 2500 / dampingParam));
|
|
68
|
+
easingFunction = Easing.out(Easing.exp);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// 欠阻尼 (discriminant < 0) - 会产生振荡
|
|
72
|
+
// 计算振荡频率和衰减率
|
|
73
|
+
const dampingRatio = c / (2 * Math.sqrt(m * k)); // 阻尼比
|
|
74
|
+
// 根据阻尼比调整动画参数
|
|
75
|
+
if (dampingRatio < 0.7) {
|
|
76
|
+
// 明显振荡
|
|
77
|
+
duration = Math.max(600, Math.min(1200, 3000 / dampingParam));
|
|
78
|
+
// 创建带振荡的贝塞尔曲线
|
|
79
|
+
easingFunction = Easing.bezier(0.175, 0.885, 0.32, 1.275);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// 轻微振荡
|
|
83
|
+
duration = Math.max(400, Math.min(800, 2000 / dampingParam));
|
|
84
|
+
easingFunction = Easing.bezier(0.25, 0.46, 0.45, 0.94);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return withTiming(toValue, {
|
|
88
|
+
duration,
|
|
89
|
+
easing: easingFunction
|
|
90
|
+
}, callback);
|
|
91
|
+
};
|
|
92
|
+
// 参考微信小程序friction的惯性动画
|
|
93
|
+
const withWechatDecay = (velocity, currentPosition, clampRange, frictionValue = 2, callback) => {
|
|
94
|
+
'worklet';
|
|
95
|
+
// 微信小程序friction算法: delta = -1.5 * v² / a, 其中 a = -f * v / |v|
|
|
96
|
+
// 如果friction小于等于0,设置为默认值2
|
|
97
|
+
const validFriction = frictionValue <= 0 ? 2 : frictionValue;
|
|
98
|
+
const f = 1000 * validFriction;
|
|
99
|
+
const acceleration = velocity !== 0 ? -f * velocity / Math.abs(velocity) : 0;
|
|
100
|
+
const delta = acceleration !== 0 ? (-1.5 * velocity * velocity) / acceleration : 0;
|
|
101
|
+
let finalPosition = currentPosition + delta;
|
|
102
|
+
// 边界限制
|
|
103
|
+
if (finalPosition < clampRange[0]) {
|
|
104
|
+
finalPosition = clampRange[0];
|
|
105
|
+
}
|
|
106
|
+
else if (finalPosition > clampRange[1]) {
|
|
107
|
+
finalPosition = clampRange[1];
|
|
108
|
+
}
|
|
109
|
+
// 计算动画时长
|
|
110
|
+
const distance = Math.abs(finalPosition - currentPosition);
|
|
111
|
+
const duration = Math.min(1500, Math.max(200, distance * 8));
|
|
112
|
+
return withTiming(finalPosition, {
|
|
113
|
+
duration,
|
|
114
|
+
easing: Easing.out(Easing.cubic)
|
|
115
|
+
}, callback);
|
|
116
|
+
};
|
|
29
117
|
const styles = StyleSheet.create({
|
|
30
118
|
container: {
|
|
31
119
|
position: 'absolute',
|
|
@@ -41,7 +129,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
41
129
|
const hasLayoutRef = useRef(false);
|
|
42
130
|
const propsRef = useRef({});
|
|
43
131
|
propsRef.current = (props || {});
|
|
44
|
-
const { x = 0, y = 0, inertia = false, disabled = false, animation = true, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'disable-event-passthrough': disableEventPassthrough = false, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, changeThrottleTime = 60, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend, bindchange } = props;
|
|
132
|
+
const { x = 0, y = 0, inertia = false, disabled = false, animation = true, damping = 20, friction = 2, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'disable-event-passthrough': disableEventPassthrough = false, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, changeThrottleTime = 60, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend, bindchange } = props;
|
|
45
133
|
const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(Object.assign({}, style, styles.container), { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
46
134
|
const navigation = useNavigation();
|
|
47
135
|
const prevSimultaneousHandlersRef = useRef(originSimultaneousHandlers || []);
|
|
@@ -106,18 +194,12 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
106
194
|
const { x: newX, y: newY } = checkBoundaryPosition({ positionX: Number(x), positionY: Number(y) });
|
|
107
195
|
if (direction === 'horizontal' || direction === 'all') {
|
|
108
196
|
offsetX.value = animation
|
|
109
|
-
?
|
|
110
|
-
duration: 1500,
|
|
111
|
-
dampingRatio: 0.8
|
|
112
|
-
})
|
|
197
|
+
? withWechatSpring(newX, damping)
|
|
113
198
|
: newX;
|
|
114
199
|
}
|
|
115
200
|
if (direction === 'vertical' || direction === 'all') {
|
|
116
201
|
offsetY.value = animation
|
|
117
|
-
?
|
|
118
|
-
duration: 1500,
|
|
119
|
-
dampingRatio: 0.8
|
|
120
|
-
})
|
|
202
|
+
? withWechatSpring(newY, damping)
|
|
121
203
|
: newY;
|
|
122
204
|
}
|
|
123
205
|
if (bindchange) {
|
|
@@ -293,7 +375,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
293
375
|
triggerEndOnJS
|
|
294
376
|
});
|
|
295
377
|
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef);
|
|
296
|
-
// 节流版本的
|
|
378
|
+
// 节流版本的change事件触发
|
|
297
379
|
const handleTriggerChangeThrottled = useCallback(({ x, y, type }) => {
|
|
298
380
|
'worklet';
|
|
299
381
|
const now = Date.now();
|
|
@@ -357,7 +439,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
357
439
|
offsetX.value = x;
|
|
358
440
|
}
|
|
359
441
|
else {
|
|
360
|
-
offsetX.value = newX;
|
|
442
|
+
offsetX.value = applyBoundaryDecline(newX, draggableXRange.value);
|
|
361
443
|
}
|
|
362
444
|
}
|
|
363
445
|
if (direction === 'vertical' || direction === 'all') {
|
|
@@ -367,7 +449,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
367
449
|
offsetY.value = y;
|
|
368
450
|
}
|
|
369
451
|
else {
|
|
370
|
-
offsetY.value = newY;
|
|
452
|
+
offsetY.value = applyBoundaryDecline(newY, draggableYRange.value);
|
|
371
453
|
}
|
|
372
454
|
}
|
|
373
455
|
if (bindchange) {
|
|
@@ -397,18 +479,12 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
397
479
|
if (x !== offsetX.value || y !== offsetY.value) {
|
|
398
480
|
if (x !== offsetX.value) {
|
|
399
481
|
offsetX.value = animation
|
|
400
|
-
?
|
|
401
|
-
duration: 1500,
|
|
402
|
-
dampingRatio: 0.8
|
|
403
|
-
})
|
|
482
|
+
? withWechatSpring(x, damping)
|
|
404
483
|
: x;
|
|
405
484
|
}
|
|
406
485
|
if (y !== offsetY.value) {
|
|
407
486
|
offsetY.value = animation
|
|
408
|
-
?
|
|
409
|
-
duration: 1500,
|
|
410
|
-
dampingRatio: 0.8
|
|
411
|
-
})
|
|
487
|
+
? withWechatSpring(y, damping)
|
|
412
488
|
: y;
|
|
413
489
|
}
|
|
414
490
|
if (bindchange) {
|
|
@@ -420,14 +496,10 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
420
496
|
}
|
|
421
497
|
}
|
|
422
498
|
else if (inertia) {
|
|
423
|
-
// 惯性处理
|
|
499
|
+
// 惯性处理 - 使用微信小程序friction算法
|
|
424
500
|
if (direction === 'horizontal' || direction === 'all') {
|
|
425
501
|
xInertialMotion.value = true;
|
|
426
|
-
offsetX.value =
|
|
427
|
-
velocity: e.velocityX / 10,
|
|
428
|
-
rubberBandEffect: outOfBounds,
|
|
429
|
-
clamp: draggableXRange.value
|
|
430
|
-
}, () => {
|
|
502
|
+
offsetX.value = withWechatDecay(e.velocityX / 10, offsetX.value, draggableXRange.value, friction, () => {
|
|
431
503
|
xInertialMotion.value = false;
|
|
432
504
|
if (bindchange) {
|
|
433
505
|
runOnJS(runOnJSCallback)('handleTriggerChange', {
|
|
@@ -439,11 +511,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
439
511
|
}
|
|
440
512
|
if (direction === 'vertical' || direction === 'all') {
|
|
441
513
|
yInertialMotion.value = true;
|
|
442
|
-
offsetY.value =
|
|
443
|
-
velocity: e.velocityY / 10,
|
|
444
|
-
rubberBandEffect: outOfBounds,
|
|
445
|
-
clamp: draggableYRange.value
|
|
446
|
-
}, () => {
|
|
514
|
+
offsetY.value = withWechatDecay(e.velocityY / 10, offsetY.value, draggableYRange.value, friction, () => {
|
|
447
515
|
yInertialMotion.value = false;
|
|
448
516
|
if (bindchange) {
|
|
449
517
|
runOnJS(runOnJSCallback)('handleTriggerChange', {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { useState, useCallback, forwardRef, useImperativeHandle } from 'react';
|
|
2
|
-
import { View, StyleSheet } from 'react-native';
|
|
1
|
+
import { useState, useCallback, forwardRef, useImperativeHandle, Fragment } from 'react';
|
|
3
2
|
const _PortalManager = forwardRef((props, ref) => {
|
|
4
3
|
const [state, setState] = useState({
|
|
5
4
|
portals: []
|
|
@@ -31,10 +30,9 @@ const _PortalManager = forwardRef((props, ref) => {
|
|
|
31
30
|
portals: state.portals
|
|
32
31
|
}));
|
|
33
32
|
return (<>
|
|
34
|
-
{state.portals.map(({ key, children }
|
|
35
|
-
style={[StyleSheet.absoluteFill, { zIndex: 1000 + i, pointerEvents: 'box-none' }]}>
|
|
33
|
+
{state.portals.map(({ key, children }) => (<Fragment key={key}>
|
|
36
34
|
{children}
|
|
37
|
-
</
|
|
35
|
+
</Fragment>))}
|
|
38
36
|
</>);
|
|
39
37
|
});
|
|
40
38
|
export default _PortalManager;
|