index.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. import curves from './config/curves'
  2. const defaultTransitionBC = 'linear'
  3. /**
  4. * @description Get the N-frame animation state by the start and end state
  5. * of the animation and the easing curve
  6. * @param {String|Array} tBC Easing curve name or data
  7. * @param {Number|Array|Object} startState Animation start state
  8. * @param {Number|Array|Object} endState Animation end state
  9. * @param {Number} frameNum Number of Animation frames
  10. * @param {Boolean} deep Whether to use recursive mode
  11. * @return {Array|Boolean} State of each frame of the animation (Invalid input will return false)
  12. */
  13. export function transition (tBC, startState = null, endState = null, frameNum = 30, deep = false) {
  14. if (!checkParams(...arguments)) return false
  15. try {
  16. // Get the transition bezier curve
  17. const bezierCurve = getBezierCurve(tBC)
  18. // Get the progress of each frame state
  19. const frameStateProgress = getFrameStateProgress(bezierCurve, frameNum)
  20. // If the recursion mode is not enabled or the state type is Number, the shallow state calculation is performed directly.
  21. if (!deep || typeof endState === 'number') return getTransitionState(startState, endState, frameStateProgress)
  22. return recursionTransitionState(startState, endState, frameStateProgress)
  23. } catch(e) {
  24. console.warn('Transition parameter may be abnormal!')
  25. return [endState]
  26. }
  27. }
  28. /**
  29. * @description Check if the parameters are legal
  30. * @param {String} tBC Name of transition bezier curve
  31. * @param {Any} startState Transition start state
  32. * @param {Any} endState Transition end state
  33. * @param {Number} frameNum Number of transition frames
  34. * @return {Boolean} Is the parameter legal
  35. */
  36. function checkParams (tBC, startState = false, endState = false, frameNum = 30) {
  37. if (!tBC || startState === false || endState === false || !frameNum) {
  38. console.error('transition: Missing Parameters!')
  39. return false
  40. }
  41. if (typeof startState !== typeof endState) {
  42. console.error('transition: Inconsistent Status Types!')
  43. return false
  44. }
  45. const stateType = typeof endState
  46. if (stateType === 'string' || stateType === 'boolean' || !tBC.length) {
  47. console.error('transition: Unsupported Data Type of State!')
  48. return false
  49. }
  50. if (!curves.has(tBC) && !(tBC instanceof Array)) {
  51. // console.warn('transition: Transition curve not found, default curve will be used!')
  52. }
  53. return true
  54. }
  55. /**
  56. * @description Get the transition bezier curve
  57. * @param {String} tBC Name of transition bezier curve
  58. * @return {Array} Bezier curve data
  59. */
  60. function getBezierCurve (tBC) {
  61. let bezierCurve = ''
  62. if (curves.has(tBC)) {
  63. bezierCurve = curves.get(tBC)
  64. } else if (tBC instanceof Array) {
  65. bezierCurve = tBC
  66. } else {
  67. bezierCurve = curves.get(defaultTransitionBC)
  68. }
  69. return bezierCurve
  70. }
  71. /**
  72. * @description Get the progress of each frame state
  73. * @param {Array} bezierCurve Transition bezier curve
  74. * @param {Number} frameNum Number of transition frames
  75. * @return {Array} Progress of each frame state
  76. */
  77. function getFrameStateProgress (bezierCurve, frameNum) {
  78. const tMinus = 1 / (frameNum - 1)
  79. const tState = new Array(frameNum).fill(0).map((t, i) => i * tMinus)
  80. const frameState = tState.map(t => getFrameStateFromT(bezierCurve, t))
  81. return frameState
  82. }
  83. /**
  84. * @description Get the progress of the corresponding frame according to t
  85. * @param {Array} bezierCurve Transition bezier curve
  86. * @param {Number} t Current frame t
  87. * @return {Number} Progress of current frame
  88. */
  89. function getFrameStateFromT (bezierCurve, t) {
  90. const tBezierCurvePoint = getBezierCurvePointFromT(bezierCurve, t)
  91. const bezierCurvePointT = getBezierCurvePointTFromReT(tBezierCurvePoint, t)
  92. return getBezierCurveTState(tBezierCurvePoint, bezierCurvePointT)
  93. }
  94. /**
  95. * @description Get the corresponding sub-curve according to t
  96. * @param {Array} bezierCurve Transition bezier curve
  97. * @param {Number} t Current frame t
  98. * @return {Array} Sub-curve of t
  99. */
  100. function getBezierCurvePointFromT (bezierCurve, t) {
  101. const lastIndex = bezierCurve.length - 1
  102. let [begin, end] = ['', '']
  103. bezierCurve.findIndex((item, i) => {
  104. if (i === lastIndex) return
  105. begin = item
  106. end = bezierCurve[i + 1]
  107. const currentMainPointX = begin[0][0]
  108. const nextMainPointX = end[0][0]
  109. return t >= currentMainPointX && t < nextMainPointX
  110. })
  111. const p0 = begin[0]
  112. const p1 = begin[2] || begin[0]
  113. const p2 = end[1] || end[0]
  114. const p3 = end[0]
  115. return [p0, p1, p2, p3]
  116. }
  117. /**
  118. * @description Get local t based on t and sub-curve
  119. * @param {Array} bezierCurve Sub-curve
  120. * @param {Number} t Current frame t
  121. * @return {Number} local t of sub-curve
  122. */
  123. function getBezierCurvePointTFromReT (bezierCurve, t) {
  124. const reBeginX = bezierCurve[0][0]
  125. const reEndX = bezierCurve[3][0]
  126. const xMinus = reEndX - reBeginX
  127. const tMinus = t - reBeginX
  128. return tMinus / xMinus
  129. }
  130. /**
  131. * @description Get the curve progress of t
  132. * @param {Array} bezierCurve Sub-curve
  133. * @param {Number} t Current frame t
  134. * @return {Number} Progress of current frame
  135. */
  136. function getBezierCurveTState ([[, p0], [, p1], [, p2], [, p3]], t) {
  137. const { pow } = Math
  138. const tMinus = 1 - t
  139. const result1 = p0 * pow(tMinus, 3)
  140. const result2 = 3 * p1 * t * pow(tMinus, 2)
  141. const result3 = 3 * p2 * pow(t, 2) * tMinus
  142. const result4 = p3 * pow(t, 3)
  143. return 1 - (result1 + result2 + result3 + result4)
  144. }
  145. /**
  146. * @description Get transition state according to frame progress
  147. * @param {Any} startState Transition start state
  148. * @param {Any} endState Transition end state
  149. * @param {Array} frameState Frame state progress
  150. * @return {Array} Transition frame state
  151. */
  152. function getTransitionState (begin, end, frameState) {
  153. let stateType = 'object'
  154. if (typeof begin === 'number') stateType = 'number'
  155. if (begin instanceof Array) stateType = 'array'
  156. if (stateType === 'number') return getNumberTransitionState(begin, end, frameState)
  157. if (stateType === 'array') return getArrayTransitionState(begin, end, frameState)
  158. if (stateType === 'object') return getObjectTransitionState(begin, end, frameState)
  159. return frameState.map(t => end)
  160. }
  161. /**
  162. * @description Get the transition data of the number type
  163. * @param {Number} startState Transition start state
  164. * @param {Number} endState Transition end state
  165. * @param {Array} frameState Frame state progress
  166. * @return {Array} Transition frame state
  167. */
  168. function getNumberTransitionState (begin, end, frameState) {
  169. const minus = end - begin
  170. return frameState.map(s => begin + minus * s)
  171. }
  172. /**
  173. * @description Get the transition data of the array type
  174. * @param {Array} startState Transition start state
  175. * @param {Array} endState Transition end state
  176. * @param {Array} frameState Frame state progress
  177. * @return {Array} Transition frame state
  178. */
  179. function getArrayTransitionState (begin, end, frameState) {
  180. const minus = end.map((v, i) => {
  181. if (typeof v !== 'number') return false
  182. return v - begin[i]
  183. })
  184. return frameState.map(s =>
  185. minus.map((v, i) => {
  186. if (v === false) return end[i]
  187. return begin[i] + v * s
  188. }))
  189. }
  190. /**
  191. * @description Get the transition data of the object type
  192. * @param {Object} startState Transition start state
  193. * @param {Object} endState Transition end state
  194. * @param {Array} frameState Frame state progress
  195. * @return {Array} Transition frame state
  196. */
  197. function getObjectTransitionState (begin, end, frameState) {
  198. const keys = Object.keys(end)
  199. const beginValue = keys.map(k => begin[k])
  200. const endValue = keys.map(k => end[k])
  201. const arrayState = getArrayTransitionState(beginValue, endValue, frameState)
  202. return arrayState.map(item => {
  203. const frameData = {}
  204. item.forEach((v, i) => (frameData[keys[i]] = v))
  205. return frameData
  206. })
  207. }
  208. /**
  209. * @description Get the transition state data by recursion
  210. * @param {Array|Object} startState Transition start state
  211. * @param {Array|Object} endState Transition end state
  212. * @param {Array} frameState Frame state progress
  213. * @return {Array} Transition frame state
  214. */
  215. function recursionTransitionState (begin, end, frameState) {
  216. const state = getTransitionState(begin, end, frameState)
  217. for (let key in end) {
  218. const bTemp = begin[key]
  219. const eTemp = end[key]
  220. if (typeof eTemp !== 'object') continue
  221. const data = recursionTransitionState(bTemp, eTemp, frameState)
  222. state.forEach((fs, i) => (fs[key] = data[i]))
  223. }
  224. return state
  225. }
  226. /**
  227. * @description Inject new curve into curves as config
  228. * @param {Any} key The key of curve
  229. * @param {Array} curve Bezier curve data
  230. * @return {Undefined} No return
  231. */
  232. export function injectNewCurve (key, curve) {
  233. if (!key || !curve) {
  234. console.error('InjectNewCurve Missing Parameters!')
  235. return
  236. }
  237. curves.set(key, curve)
  238. }
  239. export default transition