|
| 1 | +import { error, dash2hump } from '@mpxjs/utils' |
| 2 | +import { Easing } from 'react-native-reanimated' |
| 3 | +// ms s 单位匹配 |
| 4 | +const secondRegExp = /^\s*(\d*(?:\.\d+)?)(s|ms)\s*$/ |
| 5 | +const cubicBezierExp = /cubic-bezier\(["']?(.*?)["']?\)/ |
| 6 | +const behaviorExp = /^(allow-discrete|normal)$/ |
| 7 | +// const percentExp = /^((-?(\d+(\.\d+)?|\.\d+))%)$/ |
| 8 | +const defaultValueExp = /^(inherit|initial|revert|revert-layer|unset)$/ |
| 9 | +// Todo linear() https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/easing-function/linear |
| 10 | +const timingFunctionExp = /^(step-start|step-end|steps|(linear\())/ |
| 11 | +const directionExp = /^(normal|reverse|alternate|alternate-reverse)$/ |
| 12 | +const fillModeExp = /^(none|forwards|backwards|both)$/ |
| 13 | +const easingKey = { |
| 14 | + linear: Easing.linear, |
| 15 | + ease: Easing.inOut(Easing.ease), |
| 16 | + 'ease-in': Easing.in(Easing.poly(3)), |
| 17 | + 'ease-in-out': Easing.inOut(Easing.poly(3)), |
| 18 | + 'ease-out': Easing.out(Easing.poly(3)) |
| 19 | + // 'step-start': '', |
| 20 | + // 'step-end': '' |
| 21 | +} |
| 22 | +// cubic-bezier 参数解析 |
| 23 | +function getBezierParams (str) { |
| 24 | + // ease 0.25, 0.1, 0.25, 1.0 |
| 25 | + return str.match(cubicBezierExp)?.[1]?.split(',').map(item => +item) |
| 26 | +} |
| 27 | +// 解析动画时长 |
| 28 | +function getUnit (duration) { |
| 29 | + const match = secondRegExp.exec(duration) |
| 30 | + return match ? match[2] === 's' ? +match[1] * 1000 : +match[1] : 0 |
| 31 | +} |
| 32 | +// 多value解析 |
| 33 | +function parseValues (str, char = ' ') { |
| 34 | + let stack = 0 |
| 35 | + let temp = '' |
| 36 | + const result = [] |
| 37 | + for (let i = 0; i < str.length; i++) { |
| 38 | + if (str[i] === '(') { |
| 39 | + stack++ |
| 40 | + } else if (str[i] === ')') { |
| 41 | + stack-- |
| 42 | + } |
| 43 | + // 非括号内 或者 非分隔字符且非空 |
| 44 | + if (stack !== 0 || str[i] !== char) { |
| 45 | + temp += str[i] |
| 46 | + } |
| 47 | + if ((stack === 0 && str[i] === char) || i === str.length - 1) { |
| 48 | + result.push(temp.trim()) |
| 49 | + temp = '' |
| 50 | + } |
| 51 | + } |
| 52 | + return result |
| 53 | +} |
| 54 | +// 解析 animation-prop |
| 55 | +function parseAnimationSingleProp (vals, property = '') { |
| 56 | + let setDuration = false |
| 57 | + return vals.map(value => { |
| 58 | + // global values |
| 59 | + if (defaultValueExp.test(value)) { |
| 60 | + error('[Mpx runtime error]: global values is not supported') |
| 61 | + return undefined |
| 62 | + } |
| 63 | + if (timingFunctionExp.test(value)) { |
| 64 | + error('[Mpx runtime error]: the timingFunction in step-start,step-end,steps(),liner() is not supported') |
| 65 | + return undefined |
| 66 | + } |
| 67 | + // timingFunction |
| 68 | + if (Object.keys(easingKey).includes(value) || cubicBezierExp.test(value)) { |
| 69 | + const bezierParams = getBezierParams(value) |
| 70 | + return { |
| 71 | + prop: 'animationTimingFunction', |
| 72 | + value: bezierParams?.length |
| 73 | + ? Easing.bezier(bezierParams[0], bezierParams[1], bezierParams[2], bezierParams[3]) |
| 74 | + : easingKey[value] || Easing.inOut(Easing.ease) |
| 75 | + } |
| 76 | + } |
| 77 | + // direction |
| 78 | + if (directionExp.test(value)) { |
| 79 | + return { |
| 80 | + prop: 'animationDirection', |
| 81 | + value |
| 82 | + } |
| 83 | + } |
| 84 | + // fill-mode |
| 85 | + if (fillModeExp.test(value)) { |
| 86 | + return { |
| 87 | + prop: 'animationFillMode', |
| 88 | + value |
| 89 | + } |
| 90 | + } |
| 91 | + // duration & delay |
| 92 | + if (secondRegExp.test(value)) { |
| 93 | + const newProperty = property || (!setDuration ? 'animationDuration' : 'animationDelay') |
| 94 | + setDuration = true |
| 95 | + return { |
| 96 | + prop: newProperty, |
| 97 | + value: getUnit(value) |
| 98 | + } |
| 99 | + } |
| 100 | + if (!isNaN(+value)) { |
| 101 | + return { |
| 102 | + prop: 'animationIterationCount', |
| 103 | + value: +value |
| 104 | + } |
| 105 | + } |
| 106 | + // name |
| 107 | + return { |
| 108 | + prop: 'animationName', |
| 109 | + value |
| 110 | + } |
| 111 | + }).filter(item => item !== undefined) |
| 112 | +} |
| 113 | +// 解析 transition-prop |
| 114 | +function parseTransitionSingleProp (vals, property = '') { |
| 115 | + let setDuration = false |
| 116 | + return vals.map(value => { |
| 117 | + // global values |
| 118 | + if (defaultValueExp.test(value)) { |
| 119 | + error('[Mpx runtime error]: global values is not supported') |
| 120 | + return undefined |
| 121 | + } |
| 122 | + if (timingFunctionExp.test(value)) { |
| 123 | + error('[Mpx runtime error]: the timingFunction in step-start,step-end,steps() is not supported') |
| 124 | + return undefined |
| 125 | + } |
| 126 | + // timingFunction |
| 127 | + if (Object.keys(easingKey).includes(value) || cubicBezierExp.test(value)) { |
| 128 | + const bezierParams = getBezierParams(value) |
| 129 | + return { |
| 130 | + prop: 'transitionTimingFunction', |
| 131 | + value: bezierParams?.length ? Easing.bezier(bezierParams[0], bezierParams[1], bezierParams[2], bezierParams[3]) : easingKey[val] || Easing.inOut(Easing.ease) |
| 132 | + } |
| 133 | + } |
| 134 | + // behavior |
| 135 | + if (behaviorExp.test(value)) { |
| 136 | + return { |
| 137 | + prop: 'transitionBehavior', |
| 138 | + value |
| 139 | + } |
| 140 | + } |
| 141 | + // duration & delay |
| 142 | + if (secondRegExp.test(value)) { |
| 143 | + const newProperty = property || (!setDuration ? 'transitionDuration' : 'transitionDelay') |
| 144 | + setDuration = true |
| 145 | + return { |
| 146 | + prop: newProperty, |
| 147 | + value: getUnit(value) |
| 148 | + } |
| 149 | + } |
| 150 | + // property |
| 151 | + return { |
| 152 | + prop: 'transitionProperty', |
| 153 | + value: dash2hump(value) |
| 154 | + } |
| 155 | + }).filter(item => item !== undefined) |
| 156 | +} |
| 157 | +// animation & transition 解析 |
| 158 | +export function parseAnimationStyle (originalStyle, cssProp = 'animation') { |
| 159 | + let animationData = {} |
| 160 | + Object.entries(originalStyle).forEach(([propName, value]) => { |
| 161 | + if (!propName.includes(cssProp)) return |
| 162 | + if (propName === cssProp) { |
| 163 | + const vals = parseValues(value, ',').reduce((map, item, idx) => { |
| 164 | + (cssProp === 'animation' ? parseAnimationSingleProp(parseValues(item)) : parseTransitionSingleProp(parseValues(item))).forEach(({ prop, value }) => { |
| 165 | + if (map[prop]) { |
| 166 | + map[prop].push(value) |
| 167 | + } else { |
| 168 | + map[prop] = [value] |
| 169 | + } |
| 170 | + }) |
| 171 | + return map |
| 172 | + }, {}) |
| 173 | + Object.entries(vals).forEach(([prop, vals]) => { |
| 174 | + if (animationData[prop]?.length) { |
| 175 | + (animationData[prop].length >= vals.length ? animationData[prop] : vals).forEach((item,idx) => { |
| 176 | + if (animationData[prop][idx] && vals[idx]) { |
| 177 | + animationData[prop][idx] = vals[idx] |
| 178 | + } else if (vals[idx]) { |
| 179 | + animationData[prop].push(vals[idx]) |
| 180 | + } |
| 181 | + }) |
| 182 | + } else { |
| 183 | + console.error(prop, vals, 999333) |
| 184 | + animationData[prop] = vals |
| 185 | + } |
| 186 | + }) |
| 187 | + console.error('animation style, ', vals) |
| 188 | + } else { |
| 189 | + const vals = (cssProp === 'transition' ? parseTransitionSingleProp(parseValues(value, ','), propName) : parseAnimationSingleProp(parseValues(value, ','), propName)).reduce((acc, { prop, value }) => { |
| 190 | + acc.push(value) |
| 191 | + return acc |
| 192 | + }, []) |
| 193 | + console.error(`${propName} style, `, vals) |
| 194 | + if (animationData[propName]?.length) { |
| 195 | + (animationData[propName].length >= vals.length ? animationData[propName] : vals).forEach((item,idx) => { |
| 196 | + if (animationData[propName][idx] && vals[idx]) { |
| 197 | + animationData[propName][idx] = vals[idx] |
| 198 | + } else if (vals[idx]) { |
| 199 | + animationData[propName].push(vals[idx]) |
| 200 | + } |
| 201 | + }) |
| 202 | + // console.error(animationData[prop], 999111) |
| 203 | + } else { |
| 204 | + animationData[propName] = vals |
| 205 | + } |
| 206 | + } |
| 207 | + }) |
| 208 | + return animationData |
| 209 | +} |
0 commit comments