@mpxjs/webpack-plugin 2.10.17-beta.2 → 2.10.17-beta.4

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 (98) hide show
  1. package/lib/config.js +60 -0
  2. package/lib/file-loader.js +4 -1
  3. package/lib/global.d.ts +16 -0
  4. package/lib/index.js +22 -2
  5. package/lib/json-compiler/index.js +13 -4
  6. package/lib/platform/json/wx/index.js +6 -0
  7. package/lib/platform/style/wx/index.js +57 -33
  8. package/lib/platform/template/wx/component-config/ad.js +5 -0
  9. package/lib/platform/template/wx/component-config/button.js +9 -2
  10. package/lib/platform/template/wx/component-config/camera.js +25 -3
  11. package/lib/platform/template/wx/component-config/canvas.js +8 -1
  12. package/lib/platform/template/wx/component-config/cover-image.js +7 -2
  13. package/lib/platform/template/wx/component-config/cover-view.js +3 -1
  14. package/lib/platform/template/wx/component-config/form.js +27 -2
  15. package/lib/platform/template/wx/component-config/image.js +5 -0
  16. package/lib/platform/template/wx/component-config/input.js +10 -0
  17. package/lib/platform/template/wx/component-config/label.js +10 -2
  18. package/lib/platform/template/wx/component-config/map.js +11 -0
  19. package/lib/platform/template/wx/component-config/movable-area.js +4 -1
  20. package/lib/platform/template/wx/component-config/movable-view.js +17 -2
  21. package/lib/platform/template/wx/component-config/navigator.js +26 -0
  22. package/lib/platform/template/wx/component-config/picker-view.js +12 -0
  23. package/lib/platform/template/wx/component-config/picker.js +3 -1
  24. package/lib/platform/template/wx/component-config/progress.js +11 -1
  25. package/lib/platform/template/wx/component-config/rich-text.js +5 -0
  26. package/lib/platform/template/wx/component-config/scroll-view.js +12 -1
  27. package/lib/platform/template/wx/component-config/slider.js +8 -0
  28. package/lib/platform/template/wx/component-config/swiper-item.js +5 -2
  29. package/lib/platform/template/wx/component-config/swiper.js +10 -0
  30. package/lib/platform/template/wx/component-config/text.js +5 -0
  31. package/lib/platform/template/wx/component-config/textarea.js +19 -2
  32. package/lib/platform/template/wx/component-config/unsupported.js +10 -1
  33. package/lib/platform/template/wx/component-config/video.js +10 -0
  34. package/lib/platform/template/wx/index.js +21 -1
  35. package/lib/react/LoadAsyncChunkModule.js +1 -1
  36. package/lib/react/processStyles.js +21 -9
  37. package/lib/react/style-helper.js +76 -13
  38. package/lib/resolver/AddModePlugin.js +23 -8
  39. package/lib/runtime/components/react/animationHooks/index.ts +75 -0
  40. package/lib/runtime/components/react/animationHooks/useAnimationAPIHooks.ts +198 -0
  41. package/lib/runtime/components/react/animationHooks/useTransitionHooks.ts +297 -0
  42. package/lib/runtime/components/react/animationHooks/utils.ts +196 -0
  43. package/lib/runtime/components/react/context.ts +7 -1
  44. package/lib/runtime/components/react/dist/animationHooks/index.d.ts +16 -0
  45. package/lib/runtime/components/react/dist/animationHooks/index.d.ts.map +1 -0
  46. package/lib/runtime/components/react/dist/animationHooks/index.js +67 -0
  47. package/lib/runtime/components/react/dist/animationHooks/useAnimationAPIHooks.d.ts +4 -0
  48. package/lib/runtime/components/react/dist/animationHooks/useAnimationAPIHooks.d.ts.map +1 -0
  49. package/lib/runtime/components/react/dist/animationHooks/useAnimationAPIHooks.js +182 -0
  50. package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.d.ts +4 -0
  51. package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.d.ts.map +1 -0
  52. package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.js +274 -0
  53. package/lib/runtime/components/react/dist/animationHooks/utils.d.ts +110 -0
  54. package/lib/runtime/components/react/dist/animationHooks/utils.d.ts.map +1 -0
  55. package/lib/runtime/components/react/dist/animationHooks/utils.js +150 -0
  56. package/lib/runtime/components/react/dist/context.d.ts +6 -1
  57. package/lib/runtime/components/react/dist/context.d.ts.map +1 -1
  58. package/lib/runtime/components/react/dist/mpx-camera.d.ts +32 -0
  59. package/lib/runtime/components/react/dist/mpx-camera.d.ts.map +1 -0
  60. package/lib/runtime/components/react/dist/mpx-camera.jsx +236 -0
  61. package/lib/runtime/components/react/dist/mpx-input.d.ts +2 -0
  62. package/lib/runtime/components/react/dist/mpx-input.d.ts.map +1 -1
  63. package/lib/runtime/components/react/dist/mpx-input.jsx +21 -10
  64. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.d.ts.map +1 -1
  65. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +3 -0
  66. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +2 -2
  67. package/lib/runtime/components/react/dist/mpx-swiper.d.ts +10 -0
  68. package/lib/runtime/components/react/dist/mpx-swiper.d.ts.map +1 -1
  69. package/lib/runtime/components/react/dist/mpx-swiper.jsx +28 -16
  70. package/lib/runtime/components/react/dist/mpx-view.d.ts +3 -2
  71. package/lib/runtime/components/react/dist/mpx-view.d.ts.map +1 -1
  72. package/lib/runtime/components/react/dist/mpx-view.jsx +2 -2
  73. package/lib/runtime/components/react/dist/mpx-web-view.jsx +1 -1
  74. package/lib/runtime/components/react/dist/utils.d.ts +1 -0
  75. package/lib/runtime/components/react/dist/utils.d.ts.map +1 -1
  76. package/lib/runtime/components/react/dist/utils.jsx +34 -13
  77. package/lib/runtime/components/react/mpx-camera.tsx +327 -0
  78. package/lib/runtime/components/react/mpx-input.tsx +26 -10
  79. package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +3 -0
  80. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +2 -2
  81. package/lib/runtime/components/react/mpx-swiper.tsx +43 -15
  82. package/lib/runtime/components/react/mpx-view.tsx +4 -5
  83. package/lib/runtime/components/react/mpx-web-view.tsx +1 -1
  84. package/lib/runtime/components/react/types/global.d.ts +1 -0
  85. package/lib/runtime/components/react/utils.tsx +34 -16
  86. package/lib/runtime/optionProcessor.js +5 -0
  87. package/lib/runtime/optionProcessorReact.js +7 -0
  88. package/lib/runtime/stringify.wxs +2 -2
  89. package/lib/style-compiler/strip-conditional-loader/rebaseUrl.js +225 -0
  90. package/lib/style-compiler/strip-conditional-loader.js +55 -180
  91. package/lib/template-compiler/compiler.js +1 -3
  92. package/lib/utils/dom-tag-config.js +1 -1
  93. package/lib/utils/string.js +25 -1
  94. package/package.json +2 -1
  95. package/lib/runtime/components/react/dist/useAnimationHooks.d.ts +0 -33
  96. package/lib/runtime/components/react/dist/useAnimationHooks.d.ts.map +0 -1
  97. package/lib/runtime/components/react/dist/useAnimationHooks.js +0 -289
  98. package/lib/runtime/components/react/useAnimationHooks.ts +0 -320
@@ -3,31 +3,70 @@ const selectorParser = require('postcss-selector-parser')
3
3
  const { MPX_TAG_PAGE_SELECTOR } = require('../utils/const')
4
4
  const getRulesRunner = require('../platform/index')
5
5
  const dash2hump = require('../utils/hump-dash').dash2hump
6
- const unitRegExp = /^\s*(-?\d+(?:\.\d+)?)(rpx|vw|vh)\s*$/
7
- const numberRegExp = /^\s*(-?\d+(\.\d+)?)(px)?\s*$/
6
+ const parseValues = require('../utils/string').parseValues
7
+ const unitRegExp = /^\s*(-?\d+(?:\.\d+)?)(rpx|vw|vh|px)?\s*$/
8
8
  const hairlineRegExp = /^\s*hairlineWidth\s*$/
9
9
  const varRegExp = /^--/
10
10
  const cssPrefixExp = /^-(webkit|moz|ms|o)-/
11
- function getClassMap ({ content, filename, mode, srcMode, ctorType, warn, error }) {
12
- const classMap = ctorType === 'page' ? { [MPX_TAG_PAGE_SELECTOR]: { flex: 1 } } : {}
11
+ function getClassMap ({ content, filename, mode, srcMode, ctorType, formatValueName, warn, error }) {
12
+ const classMap = ctorType === 'page'
13
+ ? { [MPX_TAG_PAGE_SELECTOR]: { flex: 1, height: "'100%'" } }
14
+ : {}
13
15
 
14
16
  const root = postcss.parse(content, {
15
17
  from: filename
16
18
  })
17
19
 
18
20
  function formatValue (value) {
19
- let matched
20
21
  let needStringify = true
21
- if ((matched = numberRegExp.exec(value))) {
22
- value = matched[1]
23
- needStringify = false
24
- } else if (unitRegExp.test(value) || hairlineRegExp.test(value)) {
25
- value = `global.__formatValue(${JSON.stringify(value)})`
22
+ const matched = unitRegExp.exec(value)
23
+ if (matched) {
24
+ if (!matched[2] || matched[2] === 'px') {
25
+ value = matched[1]
26
+ needStringify = false
27
+ } else {
28
+ value = `${formatValueName}(${+matched[1]}, '${matched[2]}')`
29
+ needStringify = false
30
+ }
31
+ }
32
+ if (hairlineRegExp.test(value)) {
33
+ value = `${formatValueName}(${JSON.stringify(value)}, 'hairlineWidth')`
26
34
  needStringify = false
27
35
  }
28
36
  return needStringify ? JSON.stringify(value) : value
29
37
  }
30
38
 
39
+ function getMediaOptions (params) {
40
+ return parseValues(params).reduce((option, item) => {
41
+ if (['all', 'print'].includes(item)) {
42
+ if (item === 'media') {
43
+ option.type = item
44
+ } else {
45
+ error('not supported ', item)
46
+ return option
47
+ }
48
+ }
49
+ if (['not', 'only', 'or', ','].includes(item)) {
50
+ if (item === 'and') {
51
+ option.logical_operators = item
52
+ } else {
53
+ error('not supported ', item)
54
+ return option
55
+ }
56
+ }
57
+ const bracketsExp = /\((.+?)\)/
58
+ if (bracketsExp.test(item)) {
59
+ const range = parseValues((item.match(bracketsExp)?.[1] || ''), ':')
60
+ if (range.length < 2) {
61
+ return option
62
+ } else {
63
+ option[dash2hump(range[0])] = +formatValue(range[1])
64
+ }
65
+ }
66
+ return option
67
+ }, {})
68
+ }
69
+
31
70
  const rulesRunner = getRulesRunner({
32
71
  mode,
33
72
  srcMode,
@@ -41,13 +80,15 @@ function getClassMap ({ content, filename, mode, srcMode, ctorType, warn, error
41
80
  root.walkAtRules(rule => {
42
81
  if (rule.name !== 'media') {
43
82
  warn(`Only @media rule is supported in react native mode temporarily, but got @${rule.name}`)
83
+ // 删除不支持的 AtRule,防止其影响后续解析
84
+ rule.remove()
44
85
  }
45
86
  })
46
87
 
47
88
  root.walkRules(rule => {
48
89
  const classMapValue = {}
49
90
  rule.walkDecls(({ prop, value }) => {
50
- if (cssPrefixExp.test(prop) || cssPrefixExp.test(value)) return
91
+ if (value === 'undefined' || cssPrefixExp.test(prop) || cssPrefixExp.test(value)) return
51
92
  let newData = rulesRunner({ prop, value, selector: rule.selector })
52
93
  if (!newData) return
53
94
  if (!Array.isArray(newData)) {
@@ -79,7 +120,8 @@ function getClassMap ({ content, filename, mode, srcMode, ctorType, warn, error
79
120
  })
80
121
 
81
122
  const classMapKeys = []
82
-
123
+ const options = getMediaOptions(rule.parent.params || '')
124
+ const isMedia = options.maxWidth || options.minWidth
83
125
  selectorParser(selectors => {
84
126
  selectors.each(selector => {
85
127
  if (selector.nodes.length === 1 && selector.nodes[0].type === 'class') {
@@ -93,7 +135,28 @@ function getClassMap ({ content, filename, mode, srcMode, ctorType, warn, error
93
135
  if (classMapKeys.length) {
94
136
  classMapKeys.forEach((key) => {
95
137
  if (Object.keys(classMapValue).length) {
96
- classMap[key] = Object.assign(classMap[key] || {}, classMapValue)
138
+ let _default = classMap[key]?._default
139
+ let _media = classMap[key]?._media
140
+ if (isMedia) {
141
+ // 当前是媒体查询
142
+ _default = _default || {}
143
+ _media = _media || []
144
+ _media.push({
145
+ options,
146
+ value: classMapValue
147
+ })
148
+ classMap[key] = {
149
+ _media,
150
+ _default
151
+ }
152
+ } else if (_default) {
153
+ // 已有媒体查询数据,此次非媒体查询
154
+ Object.assign(_default, classMapValue)
155
+ } else {
156
+ // 无媒体查询
157
+ const val = classMap[key] || {}
158
+ classMap[key] = Object.assign(val, classMapValue)
159
+ }
97
160
  }
98
161
  })
99
162
  }
@@ -24,6 +24,12 @@ module.exports = class AddModePlugin {
24
24
  if (request.mode || request.env) {
25
25
  return callback()
26
26
  }
27
+
28
+ const queryObj = parseQuery(request.query || '?')
29
+ if (queryObj.mode) {
30
+ return callback()
31
+ }
32
+
27
33
  const obj = {
28
34
  mode
29
35
  }
@@ -37,23 +43,32 @@ module.exports = class AddModePlugin {
37
43
  // 当前资源没有后缀名或者路径不符合fileConditionRules规则时,直接返回
38
44
  if (!extname || !matchCondition(resourcePath, fileConditionRules)) return callback()
39
45
 
40
- const queryObj = parseQuery(request.query || '?')
41
46
  const queryInfix = queryObj.infix
42
- if (!implicitMode) queryObj.mode = mode
43
- queryObj.infix = `${queryInfix || ''}.${mode}`
44
47
 
45
48
  // 如果已经确认是mode后缀的文件,添加query与mode后直接返回
46
49
  if (modePattern.test(path.basename(resourcePath))) {
47
- request.query = stringifyQuery(queryObj)
48
- request.mode = obj.mode
50
+ // 已经被resolved到对应mode的文件,避免重复添加mode
51
+ const isResolved = (implicitMode || queryObj.mode === mode) && modePattern.test(queryObj.infix)
52
+ if (!isResolved) {
53
+ queryObj.infix = `${queryInfix || ''}.${mode}`
54
+ if (!implicitMode) queryObj.mode = mode
55
+ request.query = stringifyQuery(queryObj)
56
+ request.mode = obj.mode
57
+ }
49
58
  return callback()
50
59
  } else if (defaultMode && defaultModePattern.test(path.basename(resourcePath))) {
51
- queryObj.infix = `${queryInfix || ''}.${defaultMode}`
52
- request.query = stringifyQuery(queryObj)
53
- request.mode = obj.mode
60
+ const isResolved = (implicitMode || queryObj.mode === mode) && defaultModePattern.test(queryObj.infix)
61
+ if (!isResolved) {
62
+ queryObj.infix = `${queryInfix || ''}.${defaultMode}`
63
+ if (!implicitMode) queryObj.mode = mode
64
+ request.query = stringifyQuery(queryObj)
65
+ request.mode = obj.mode
66
+ }
54
67
  return callback()
55
68
  }
56
69
 
70
+ if (!implicitMode) queryObj.mode = mode
71
+ queryObj.infix = `${queryInfix || ''}.${mode}`
57
72
  obj.query = stringifyQuery(queryObj)
58
73
  obj.path = addInfix(resourcePath, mode, extname)
59
74
  obj.relativePath = request.relativePath && addInfix(request.relativePath, mode, extname)
@@ -0,0 +1,75 @@
1
+ import { error, collectDataset, hasOwn } from '@mpxjs/utils'
2
+ import { useRef } from 'react'
3
+ import useAnimationAPIHooks from './useAnimationAPIHooks'
4
+ import useTransitionHooks from './useTransitionHooks'
5
+ import type { AnimatableValue } from 'react-native-reanimated'
6
+ import type { MutableRefObject } from 'react'
7
+ import type { NativeSyntheticEvent } from 'react-native'
8
+ import type { _ViewProps } from '../mpx-view'
9
+
10
+ // 动画类型
11
+ export type AnimationType = 'api'|'animation'|'transition'|'none'
12
+
13
+ export default function useAnimationHooks<T, P> (props: _ViewProps & { enableAnimation?: boolean | AnimationType, layoutRef: MutableRefObject<any>, transitionend?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void }) {
14
+ const { style: originalStyle = {}, enableAnimation, animation, transitionend, layoutRef } = props
15
+ // 记录动画类型
16
+ let animationType = ''
17
+ if (hasOwn(originalStyle, 'animation') || (hasOwn(originalStyle, 'animationName') && hasOwn(originalStyle, 'animationDuration'))) {
18
+ // css animation 只做检测提示
19
+ animationType = 'animation'
20
+ }
21
+ if (!!animation || enableAnimation === true) {
22
+ animationType = 'api'
23
+ }
24
+ // 优先级 css transition > API
25
+ if (hasOwn(originalStyle, 'transition') || (hasOwn(originalStyle, 'transitionProperty') && hasOwn(originalStyle, 'transitionDuration'))) {
26
+ animationType = 'transition'
27
+ }
28
+ // 优先以 enableAnimation 定义类型为准
29
+ if (enableAnimation === 'api' || enableAnimation === 'transition' || enableAnimation === 'animation') {
30
+ animationType = enableAnimation
31
+ }
32
+ const animationTypeRef = useRef(animationType)
33
+ if (animationType! && animationTypeRef.current !== animationType) {
34
+ // 允许 API、CssTransition 到 none,不允许 API、CssTransition 互切,不允许 none 到 API、CssTransition
35
+ error('[Mpx runtime error]: The animation type should be stable in the component lifecycle, or you can set animation type with [enable-animation].')
36
+ }
37
+ if (animationType === 'animation') {
38
+ // 暂不支持 CssAnimation 提示
39
+ error('[Mpx runtime error]: CSS animation is not supported yet')
40
+ return { enableStyleAnimation: false }
41
+ }
42
+ if (!animationTypeRef.current) return { enableStyleAnimation: false }
43
+
44
+ const hooksProps = { style: originalStyle }
45
+ if (transitionend && typeof transitionend === 'function') {
46
+ function withTimingCallback (finished?: boolean, current?: AnimatableValue, duration?: number) {
47
+ const target = {
48
+ id: animation?.id || -1,
49
+ dataset: collectDataset(props),
50
+ offsetLeft: layoutRef?.current?.offsetLeft || 0,
51
+ offsetTop: layoutRef?.current?.offsetTop || 0
52
+ }
53
+ transitionend!({
54
+ type: 'transitionend',
55
+ // elapsedTime 对齐wx 单位s
56
+ detail: { elapsedTime: duration ? duration / 1000 : 0, finished, current },
57
+ target,
58
+ currentTarget: target,
59
+ timeStamp: Date.now()
60
+ })
61
+ }
62
+ Object.assign(hooksProps, { transitionend: withTimingCallback })
63
+ }
64
+ if (animationTypeRef.current === 'api') {
65
+ Object.assign(hooksProps, { animation })
66
+ }
67
+ return {
68
+ enableStyleAnimation: !!animationTypeRef.current,
69
+ animationStyle: animationTypeRef.current === 'api'
70
+ // eslint-disable-next-line react-hooks/rules-of-hooks
71
+ ? useAnimationAPIHooks(hooksProps)
72
+ // eslint-disable-next-line react-hooks/rules-of-hooks
73
+ : useTransitionHooks(hooksProps)
74
+ }
75
+ }
@@ -0,0 +1,198 @@
1
+ import { useMemo, useRef, useEffect } from 'react'
2
+ import { error, hasOwn } from '@mpxjs/utils'
3
+ import {
4
+ Easing,
5
+ withSequence,
6
+ makeMutable,
7
+ runOnJS,
8
+ useSharedValue,
9
+ useAnimatedStyle,
10
+ cancelAnimation
11
+ } from 'react-native-reanimated'
12
+ import {
13
+ easingKey,
14
+ animationAPIInitialValue,
15
+ percentExp,
16
+ isTransform,
17
+ getInitialVal,
18
+ getAnimation,
19
+ getTransformObj,
20
+ formatAnimatedKeys
21
+ } from './utils'
22
+ import { useRunOnJSCallback } from '../utils'
23
+ import type { NativeSyntheticEvent, TransformsStyle } from 'react-native'
24
+ import type { AnimationCallback, SharedValue, AnimatableValue } from 'react-native-reanimated'
25
+ import type { ExtendedViewStyle } from '../types/common'
26
+ import type { AnimationHooksPropsType } from './utils'
27
+
28
+ export default function useAnimationAPIHooks<T, P> (props: AnimationHooksPropsType) {
29
+ // console.log(`useAnimationAPIHooks, props=`, props)
30
+ const { style: originalStyle = {}, animation, transitionend } = props
31
+ // style变更标识(首次render不执行)
32
+ const animationDeps = useRef(-1)
33
+ // animation API 使用 animation.id 为依赖
34
+ if (animation?.id) {
35
+ animationDeps.current = animation.id
36
+ }
37
+ // 有动画样式的 style key(useAnimatedStyle使用)
38
+ const animatedStyleKeys = useSharedValue([] as (string|string[])[])
39
+ // 记录需要执行动画的 propName
40
+ const animatedKeys = useRef([] as string[])
41
+ // 记录上次style map
42
+ const lastStyleRef = useRef({} as {[propName: keyof ExtendedViewStyle]: number|string})
43
+ // ** 全量 style prop sharedValue
44
+ const shareValMap = useMemo(() => {
45
+ return Object.keys(animationAPIInitialValue).reduce((valMap, key) => {
46
+ const defaultVal = getInitialVal(originalStyle, key)
47
+ valMap[key] = makeMutable(defaultVal)
48
+ return valMap
49
+ }, {} as { [propName: keyof ExtendedViewStyle]: SharedValue<string|number> })
50
+ }, [])
51
+ const runOnJSCallbackRef = useRef({})
52
+ if (transitionend) {
53
+ runOnJSCallbackRef.current = {
54
+ transitionend
55
+ }
56
+ }
57
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
58
+ // 设置 lastShareValRef & shareValMap
59
+ function updateStyleVal () {
60
+ Object.keys(shareValMap).forEach(key => {
61
+ let value = originalStyle[key]
62
+ if (isTransform(key)) {
63
+ value = originalStyle.transform
64
+ Object.entries(getTransformObj(value)).forEach(([key, value]) => {
65
+ if (value !== lastStyleRef.current[key]) {
66
+ lastStyleRef.current[key] = value
67
+ shareValMap[key].value = value
68
+ }
69
+ })
70
+ } else {
71
+ if (value !== lastStyleRef.current[key]) {
72
+ lastStyleRef.current[key] = value
73
+ shareValMap[key].value = value
74
+ }
75
+ }
76
+ })
77
+ }
78
+ // 根据 animation action 创建&驱动动画
79
+ function createAnimation (animatedKeys: string[] = []) {
80
+ const actions = animation?.actions || []
81
+ const sequence = {} as { [propName: keyof ExtendedViewStyle]: (string|number)[] }
82
+ const lastValueMap = {} as { [propName: keyof ExtendedViewStyle]: string|number }
83
+ actions.forEach(({ animatedOption, rules, transform }, index) => {
84
+ const { delay, duration, timingFunction, transformOrigin } = animatedOption
85
+ const easing = timingFunction ? easingKey[timingFunction] : Easing.inOut(Easing.quad)
86
+ let needSetCallback = true
87
+ const callback: AnimationCallback = (finished?: boolean, current?: AnimatableValue) => {
88
+ 'worklet'
89
+ // 动画结束后设置下一次transformOrigin
90
+ if (finished) {
91
+ if (index < actions.length - 1) {
92
+ const transformOrigin = actions[index + 1].animatedOption?.transformOrigin
93
+ transformOrigin && (shareValMap.transformOrigin.value = transformOrigin)
94
+ }
95
+ transitionend && runOnJS(runOnJSCallback)('transitionend', finished, current, duration)
96
+ }
97
+ }
98
+ if (index === 0 && transformOrigin) {
99
+ // 设置当次中心
100
+ shareValMap.transformOrigin.value = transformOrigin
101
+ }
102
+ // 添加每个key的多次step动画
103
+ animatedKeys.forEach(key => {
104
+ const shareVal = shareValMap[key].value
105
+ const ruleV = isTransform(key) ? transform.get(key) : rules.get(key)
106
+ // color 设置为 1
107
+ // key不存在,第一轮取shareValMap[key]value,非第一轮取上一轮的
108
+ let toVal = ruleV !== undefined
109
+ ? ruleV
110
+ : index > 0
111
+ ? lastValueMap[key]
112
+ : shareVal
113
+ if (percentExp.test(`${toVal}`) && !percentExp.test(shareVal as string) && !isNaN(+shareVal)) {
114
+ // 获取到的toVal为百分比格式化shareValMap为百分比
115
+ shareValMap[key].value = `${shareVal as number * 100}%`
116
+ } else if (percentExp.test(shareVal as string) && !percentExp.test(toVal as string) && !isNaN(+toVal)) {
117
+ // 初始值为百分比则格式化toVal为百分比
118
+ toVal = `${toVal as number * 100}%`
119
+ } else if (typeof toVal !== typeof shareVal) {
120
+ // 动画起始值和终态值类型不一致报错提示一下
121
+ error(`[Mpx runtime error]: Value types of property ${key} must be consistent during the animation`)
122
+ }
123
+ // Todo 对齐wx
124
+ const animation = getAnimation({ key, value: toVal! }, { delay, duration, easing }, needSetCallback ? callback : undefined)
125
+ needSetCallback = false
126
+ if (!sequence[key]) {
127
+ sequence[key] = [animation]
128
+ } else {
129
+ sequence[key].push(animation)
130
+ }
131
+ // 更新一下 lastValueMap
132
+ lastValueMap[key] = toVal!
133
+ })
134
+ // 赋值驱动动画
135
+ animatedKeys.forEach((key) => {
136
+ const animations = sequence[key]
137
+ shareValMap[key].value = animations.length > 1 ? withSequence(...animations) : animations[0]
138
+ })
139
+ })
140
+ }
141
+ // 循环 animation actions 获取所有有动画的 style prop name
142
+ function getAnimatedStyleKeys () {
143
+ return (animation?.actions || []).reduce((keyMap, action) => {
144
+ const { rules, transform } = action
145
+ const ruleArr = [...rules.keys(), ...transform.keys()]
146
+ ruleArr.forEach(key => {
147
+ keyMap.push(key)
148
+ })
149
+ // console.log('getAnimatedStyleKeys keyMap=', keyMap)
150
+ return keyMap
151
+ }, [] as string[])
152
+ }
153
+ // 获取动画样式&驱动动画
154
+ function startAnimation () {
155
+ // 更新动画样式 key map
156
+ animatedKeys.current = getAnimatedStyleKeys()
157
+ animatedStyleKeys.value = formatAnimatedKeys(['transformOrigin', ...animatedKeys.current])
158
+ // 驱动动画
159
+ createAnimation(animatedKeys.current)
160
+ }
161
+ // ** style 更新
162
+ useEffect(() => {
163
+ // animation api style 更新同步更新 shareVal(默认)
164
+ updateStyleVal()
165
+ }, [originalStyle])
166
+ // ** 获取动画样式prop & 驱动动画
167
+ useEffect(() => {
168
+ if (animationDeps.current <= 0) return
169
+ startAnimation()
170
+ }, [animationDeps.current])
171
+ // ** 清空动画
172
+ useEffect(() => {
173
+ return () => {
174
+ Object.values(shareValMap).forEach((value) => {
175
+ cancelAnimation(value)
176
+ })
177
+ }
178
+ }, [])
179
+ // ** 生成动画样式
180
+ return useAnimatedStyle(() => {
181
+ // console.info(`useAnimatedStyle styles=`, originalStyle)
182
+ return animatedStyleKeys.value.reduce((styles, key) => {
183
+ if (Array.isArray(key)) {
184
+ const transformStyle = getTransformObj(originalStyle.transform || [])
185
+ key.forEach((transformKey) => {
186
+ transformStyle[transformKey] = shareValMap[transformKey].value
187
+ })
188
+ styles.transform = Object.entries(transformStyle).map(([key, value]) => {
189
+ return { [key]: value }
190
+ }) as Extract<'transform', TransformsStyle>
191
+ } else {
192
+ styles[key] = shareValMap[key].value
193
+ }
194
+ // console.log('animationStyle', styles)
195
+ return styles
196
+ }, {} as ExtendedViewStyle)
197
+ })
198
+ }