index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. import arcToBezier from './arc2bezier.js'
  2. import parser from './svg-path-parser.js'
  3. import { sort, sortCurves } from './sort.js'
  4. let pasition = {}
  5. pasition.parser = parser
  6. pasition.lerpCurve = function (pathA, pathB, t) {
  7. return pasition.lerpPoints(pathA[0], pathA[1], pathB[0], pathB[1], t)
  8. .concat(pasition.lerpPoints(pathA[2], pathA[3], pathB[2], pathB[3], t))
  9. .concat(pasition.lerpPoints(pathA[4], pathA[5], pathB[4], pathB[5], t))
  10. .concat(pasition.lerpPoints(pathA[6], pathA[7], pathB[6], pathB[7], t))
  11. }
  12. pasition.lerpPoints = function (x1, y1, x2, y2, t) {
  13. return [x1 + (x2 - x1) * t, y1 + (y2 - y1) * t]
  14. }
  15. pasition.q2b = function (x1, y1, x2, y2, x3, y3) {
  16. return [x1, y1, (x1 + 2 * x2) / 3, (y1 + 2 * y2) / 3, (x3 + 2 * x2 )/ 3, (y3 + 2 * y2) / 3, x3, y3]
  17. }
  18. pasition.path2shapes = function (path) {
  19. //https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial/Paths
  20. //M = moveto
  21. //L = lineto
  22. //H = horizontal lineto
  23. //V = vertical lineto
  24. //C = curveto
  25. //S = smooth curveto
  26. //Q = quadratic Belzier curve
  27. //T = smooth quadratic Belzier curveto
  28. //A = elliptical Arc
  29. //Z = closepath
  30. //以上所有命令均允许小写字母。大写表示绝对定位,小写表示相对定位(从上一个点开始)。
  31. let cmds = pasition.parser(path),
  32. preX = 0,
  33. preY = 0,
  34. j = 0,
  35. len = cmds.length,
  36. shapes = [],
  37. current = null,
  38. closeX,
  39. closeY,
  40. preCX,
  41. preCY,
  42. sLen,
  43. curves,
  44. lastCurve
  45. for (; j < len; j++) {
  46. let item = cmds[j]
  47. let action = item[0]
  48. let preItem = cmds[j - 1]
  49. switch (action) {
  50. case 'm':
  51. sLen = shapes.length
  52. shapes[sLen] = []
  53. current = shapes[sLen]
  54. preX = preX+item[1]
  55. preY = preY+item[2]
  56. break
  57. case 'M':
  58. sLen = shapes.length
  59. shapes[sLen] = []
  60. current = shapes[sLen]
  61. preX = item[1]
  62. preY = item[2]
  63. break
  64. case 'l':
  65. current.push([preX, preY, preX, preY, preX, preY, preX+item[1],preY+item[2]])
  66. preX += item[1]
  67. preY += item[2]
  68. break
  69. case 'L':
  70. current.push([preX, preY, item[1], item[2], item[1], item[2], item[1], item[2]])
  71. preX = item[1]
  72. preY = item[2]
  73. break
  74. case 'h':
  75. current.push([preX, preY, preX, preY, preX, preY, preX + item[1], preY])
  76. preX += item[1]
  77. break
  78. case 'H':
  79. current.push([preX, preY, item[1], preY, item[1], preY, item[1], preY])
  80. preX = item[1]
  81. break
  82. case 'v':
  83. current.push([preX, preY, preX, preY, preX, preY, preX, preY + item[1]])
  84. preY += item[1]
  85. break
  86. case 'V':
  87. current.push([preX, preY, preX, item[1], preX, item[1], preX, item[1]])
  88. preY = item[1]
  89. break
  90. case 'C':
  91. current.push([preX, preY, item[1], item[2], item[3], item[4], item[5], item[6]])
  92. preX = item[5]
  93. preY = item[6]
  94. break
  95. case 'S':
  96. if(preItem[0] ==='C'||preItem[0] ==='c'){
  97. current.push([preX, preY, preX + preItem[5] - preItem[3], preY + preItem[6] - preItem[4], item[1], item[2], item[3], item[4]])
  98. }else if(preItem[0] ==='S'||preItem[0] ==='s'){
  99. current.push([preX, preY, preX+ preItem[3] - preItem[1] ,preY+ preItem[4] - preItem[2], item[1], item[2], item[3], item[4]])
  100. }
  101. preX = item[3]
  102. preY = item[4]
  103. break
  104. case 'c':
  105. current.push([preX, preY, preX + item[1], preY + item[2], preX + item[3], preY + item[4], preX + item[5], preY + item[6]])
  106. preX = preX + item[5]
  107. preY = preY + item[6]
  108. break
  109. case 's':
  110. if(preItem[0] ==='C'||preItem[0] ==='c'){
  111. current.push([preX, preY, preX+ preItem[5] - preItem[3] ,preY+ preItem[6] - preItem[4],preX+ item[1],preY+ item[2], preX+item[3],preY+ item[4]])
  112. }else if(preItem[0] ==='S'||preItem[0] ==='s' ){
  113. current.push([preX, preY, preX+ preItem[3] - preItem[1] ,preY+ preItem[4] - preItem[2],preX+ item[1],preY+ item[2], preX+item[3],preY+ item[4]])
  114. }
  115. preX = preX+item[3]
  116. preY = preY+item[4]
  117. break
  118. case 'a':
  119. curves = arcToBezier({
  120. rx: item[1],
  121. ry: item[2],
  122. px: preX,
  123. py: preY,
  124. xAxisRotation: item[3],
  125. largeArcFlag: item[4],
  126. sweepFlag: item[5],
  127. cx: preX+item[6],
  128. cy: preY+item[7]
  129. })
  130. lastCurve = curves[curves.length-1]
  131. curves.forEach((curve,index)=>{
  132. if(index === 0) {
  133. current.push([preX,preY,curve.x1,curve.y1,curve.x2,curve.y2,curve.x,curve.y])
  134. }else{
  135. current.push([curves[index-1].x,curves[index-1].y,curve.x1,curve.y1,curve.x2,curve.y2,curve.x,curve.y])
  136. }
  137. })
  138. preX = lastCurve.x
  139. preY = lastCurve.y
  140. break
  141. case 'A':
  142. curves = arcToBezier({
  143. rx: item[1],
  144. ry: item[2],
  145. px: preX,
  146. py: preY,
  147. xAxisRotation: item[3],
  148. largeArcFlag: item[4],
  149. sweepFlag: item[5],
  150. cx: item[6],
  151. cy: item[7]
  152. })
  153. lastCurve = curves[curves.length-1]
  154. curves.forEach((curve,index)=>{
  155. if(index === 0) {
  156. current.push([preX,preY,curve.x1,curve.y1,curve.x2,curve.y2,curve.x,curve.y])
  157. }else{
  158. current.push([curves[index-1].x,curves[index-1].y,curve.x1,curve.y1,curve.x2,curve.y2,curve.x,curve.y])
  159. }
  160. })
  161. preX = lastCurve.x
  162. preY = lastCurve.y
  163. break
  164. case 'Q':
  165. current.push(pasition.q2b(preX,preY, item[1], item[2], item[3], item[4]))
  166. preX = item[3]
  167. preY = item[4]
  168. break
  169. case 'q':
  170. current.push(pasition.q2b(preX,preY,preX+item[1], preY+item[2],item[3]+preX,item[4]+preY))
  171. preX += item[3]
  172. preY += item[4]
  173. break
  174. case 'T':
  175. if(preItem[0] ==='Q'|| preItem[0] ==='q') {
  176. preCX = preX + preItem[3] - preItem[1]
  177. preCY = preY + preItem[4] - preItem[2]
  178. current.push(pasition.q2b(preX, preY, preCX, preCY, item[1], item[2]))
  179. }else if(preItem[0] ==='T'|| preItem[0] ==='t' ) {
  180. current.push(pasition.q2b(preX, preY, preX + preX - preCX, preY + preY - preCY, item[1], item[2]))
  181. preCX = preX + preX - preCX
  182. preCY = preY + preY - preCY
  183. }
  184. preX = item[1]
  185. preY = item[2]
  186. break
  187. case 't':
  188. if(preItem[0] ==='Q'|| preItem[0] ==='q') {
  189. preCX = preX + preItem[3] - preItem[1]
  190. preCY = preY + preItem[4] - preItem[2]
  191. current.push(pasition.q2b(preX, preY, preCX, preCY,preX+ item[1],preY+ item[2]))
  192. }else if(preItem[0] ==='T'|| preItem[0] ==='t' ) {
  193. current.push(pasition.q2b(preX, preY, preX + preX - preCX, preY + preY - preCY, preX+item[1], preY+item[2]))
  194. preCX = preX + preX - preCX
  195. preCY = preY + preY - preCY
  196. }
  197. preX += item[1]
  198. preY += item[2]
  199. break
  200. case 'Z':
  201. closeX = current[0][0]
  202. closeY = current[0][1]
  203. current.push([preX, preY, closeX, closeY, closeX, closeY, closeX, closeY])
  204. break
  205. case 'z':
  206. closeX = current[0][0]
  207. closeY = current[0][1]
  208. current.push([preX, preY, closeX, closeY, closeX, closeY, closeX, closeY])
  209. break
  210. }
  211. }
  212. return shapes
  213. }
  214. pasition._upCurves = function (curves, count) {
  215. let i = 0,
  216. index = 0,
  217. len = curves.length
  218. for (; i < count; i++) {
  219. curves.push(curves[index].slice(0))
  220. index++
  221. if(index > len-1) {
  222. index -= len
  223. }
  224. }
  225. }
  226. function split(x1, y1, x2, y2, x3, y3, x4, y4, t) {
  227. return {
  228. left: _split(x1, y1, x2, y2, x3, y3, x4, y4, t),
  229. right: _split(x4, y4, x3, y3, x2, y2, x1, y1, 1 - t, true)
  230. }
  231. }
  232. function _split(x1, y1, x2, y2, x3, y3, x4, y4, t, reverse) {
  233. let x12 = (x2 - x1) * t + x1
  234. let y12 = (y2 - y1) * t + y1
  235. let x23 = (x3 - x2) * t + x2
  236. let y23 = (y3 - y2) * t + y2
  237. let x34 = (x4 - x3) * t + x3
  238. let y34 = (y4 - y3) * t + y3
  239. let x123 = (x23 - x12) * t + x12
  240. let y123 = (y23 - y12) * t + y12
  241. let x234 = (x34 - x23) * t + x23
  242. let y234 = (y34 - y23) * t + y23
  243. let x1234 = (x234 - x123) * t + x123
  244. let y1234 = (y234 - y123) * t + y123
  245. if(reverse) {
  246. return [x1234, y1234, x123, y123, x12, y12, x1, y1]
  247. }
  248. return [x1, y1, x12, y12, x123, y123, x1234, y1234]
  249. }
  250. pasition._splitCurves = function (curves, count) {
  251. let i = 0,
  252. index = 0
  253. for (; i < count; i++) {
  254. let curve = curves[index]
  255. let cs = split(curve[0],curve[1],curve[2],curve[3],curve[4],curve[5],curve[6],curve[7],0.5)
  256. curves.splice(index,1)
  257. curves.splice(index,0,cs.left,cs.right)
  258. index+=2
  259. if(index >= curves.length-1) {
  260. index = 0
  261. }
  262. }
  263. }
  264. function sync(shapes, count) {
  265. for (let i = 0; i < count; i++) {
  266. let shape = shapes[shapes.length - 1]
  267. let newShape = []
  268. shape.forEach(function (curve) {
  269. newShape.push(curve.slice(0))
  270. })
  271. shapes.push(newShape)
  272. }
  273. }
  274. pasition.lerp = function (pathA, pathB, t) {
  275. return pasition._lerp( pasition.path2shapes(pathA), pasition.path2shapes(pathB), t)
  276. }
  277. pasition.MIM_CURVES_COUNT = 100
  278. pasition._preprocessing = function(pathA, pathB) {
  279. let lenA = pathA.length,
  280. lenB = pathB.length,
  281. clonePathA = JSON.parse(JSON.stringify(pathA)),
  282. clonePathB = JSON.parse(JSON.stringify(pathB))
  283. if (lenA > lenB) {
  284. sync(clonePathB, lenA - lenB)
  285. } else if (lenA < lenB) {
  286. sync(clonePathA, lenB - lenA)
  287. }
  288. clonePathA = sort(clonePathA, clonePathB)
  289. clonePathA.forEach(function (curves, index) {
  290. let lenA = curves.length,
  291. lenB = clonePathB[index].length
  292. if (lenA > lenB) {
  293. if (lenA < pasition.MIM_CURVES_COUNT) {
  294. pasition._splitCurves(curves, pasition.MIM_CURVES_COUNT - lenA)
  295. pasition._splitCurves(clonePathB[index], pasition.MIM_CURVES_COUNT - lenB)
  296. } else {
  297. pasition._splitCurves(clonePathB[index], lenA - lenB)
  298. }
  299. } else if (lenA < lenB) {
  300. if (lenB < pasition.MIM_CURVES_COUNT) {
  301. pasition._splitCurves(curves, pasition.MIM_CURVES_COUNT - lenA)
  302. pasition._splitCurves(clonePathB[index], pasition.MIM_CURVES_COUNT - lenB)
  303. } else {
  304. pasition._splitCurves(curves, lenB - lenA)
  305. }
  306. }
  307. })
  308. clonePathA.forEach(function (curves, index) {
  309. clonePathA[index] = sortCurves(curves, clonePathB[index])
  310. })
  311. return [clonePathA, clonePathB]
  312. }
  313. pasition._lerp = function (pathA, pathB, t) {
  314. let shapes = []
  315. pathA.forEach(function (curves, index) {
  316. let newCurves = []
  317. curves.forEach(function (curve, curveIndex) {
  318. newCurves.push(pasition.lerpCurve(curve, pathB[index][curveIndex], t))
  319. })
  320. shapes.push(newCurves)
  321. })
  322. return shapes
  323. }
  324. pasition.animate = function (option) {
  325. let pathA = pasition.path2shapes(option.from)
  326. let pathB = pasition.path2shapes(option.to)
  327. let pathArr = pasition._preprocessing(pathA,pathB)
  328. let beginTime = new Date(),
  329. end = option.end || function () {
  330. },
  331. progress = option.progress || function () {
  332. },
  333. begin = option.begin || function () {
  334. },
  335. easing = option.easing || function (v) {
  336. return v
  337. },
  338. tickId = null,
  339. outShape = null,
  340. time = option.time
  341. begin(pathA)
  342. let tick = function () {
  343. let dt = new Date() - beginTime
  344. if (dt >= time) {
  345. outShape = pathB
  346. progress(outShape, 1)
  347. end(outShape)
  348. cancelAnimationFrame(tickId)
  349. return
  350. }
  351. let percent = easing(dt / time)
  352. outShape = pasition._lerp(pathArr[0], pathArr[1], percent)
  353. progress(outShape, percent)
  354. tickId = requestAnimationFrame(tick)
  355. }
  356. tick()
  357. }
  358. export default pasition