PathProxy.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. import * as vec2 from './vector';
  2. import BoundingRect from './BoundingRect';
  3. import { devicePixelRatio as dpr } from '../config';
  4. import { fromLine, fromCubic, fromQuadratic, fromArc } from './bbox';
  5. import { cubicAt, cubicLength, cubicSubdivide, quadraticLength, quadraticSubdivide } from './curve';
  6. var CMD = {
  7. M: 1,
  8. L: 2,
  9. C: 3,
  10. Q: 4,
  11. A: 5,
  12. Z: 6,
  13. R: 7
  14. };
  15. var tmpOutX = [];
  16. var tmpOutY = [];
  17. var min = [];
  18. var max = [];
  19. var min2 = [];
  20. var max2 = [];
  21. var mathMin = Math.min;
  22. var mathMax = Math.max;
  23. var mathCos = Math.cos;
  24. var mathSin = Math.sin;
  25. var mathSqrt = Math.sqrt;
  26. var mathAbs = Math.abs;
  27. var PI = Math.PI;
  28. var PI2 = PI * 2;
  29. var hasTypedArray = typeof Float32Array !== 'undefined';
  30. var tmpAngles = [];
  31. function modPI2(radian) {
  32. var n = Math.round(radian / PI * 1e8) / 1e8;
  33. return (n % 2) * PI;
  34. }
  35. export function normalizeArcAngles(angles, anticlockwise) {
  36. var newStartAngle = modPI2(angles[0]);
  37. if (newStartAngle < 0) {
  38. newStartAngle += PI2;
  39. }
  40. var delta = newStartAngle - angles[0];
  41. var newEndAngle = angles[1];
  42. newEndAngle += delta;
  43. if (!anticlockwise && newEndAngle - newStartAngle >= PI2) {
  44. newEndAngle = newStartAngle + PI2;
  45. }
  46. else if (anticlockwise && newStartAngle - newEndAngle >= PI2) {
  47. newEndAngle = newStartAngle - PI2;
  48. }
  49. else if (!anticlockwise && newStartAngle > newEndAngle) {
  50. newEndAngle = newStartAngle + (PI2 - modPI2(newStartAngle - newEndAngle));
  51. }
  52. else if (anticlockwise && newStartAngle < newEndAngle) {
  53. newEndAngle = newStartAngle - (PI2 - modPI2(newEndAngle - newStartAngle));
  54. }
  55. angles[0] = newStartAngle;
  56. angles[1] = newEndAngle;
  57. }
  58. var PathProxy = (function () {
  59. function PathProxy(notSaveData) {
  60. this.dpr = 1;
  61. this._xi = 0;
  62. this._yi = 0;
  63. this._x0 = 0;
  64. this._y0 = 0;
  65. this._len = 0;
  66. if (notSaveData) {
  67. this._saveData = false;
  68. }
  69. if (this._saveData) {
  70. this.data = [];
  71. }
  72. }
  73. PathProxy.prototype.increaseVersion = function () {
  74. this._version++;
  75. };
  76. PathProxy.prototype.getVersion = function () {
  77. return this._version;
  78. };
  79. PathProxy.prototype.setScale = function (sx, sy, segmentIgnoreThreshold) {
  80. segmentIgnoreThreshold = segmentIgnoreThreshold || 0;
  81. if (segmentIgnoreThreshold > 0) {
  82. this._ux = mathAbs(segmentIgnoreThreshold / dpr / sx) || 0;
  83. this._uy = mathAbs(segmentIgnoreThreshold / dpr / sy) || 0;
  84. }
  85. };
  86. PathProxy.prototype.setDPR = function (dpr) {
  87. this.dpr = dpr;
  88. };
  89. PathProxy.prototype.setContext = function (ctx) {
  90. this._ctx = ctx;
  91. };
  92. PathProxy.prototype.getContext = function () {
  93. return this._ctx;
  94. };
  95. PathProxy.prototype.beginPath = function () {
  96. this._ctx && this._ctx.beginPath();
  97. this.reset();
  98. return this;
  99. };
  100. PathProxy.prototype.reset = function () {
  101. if (this._saveData) {
  102. this._len = 0;
  103. }
  104. if (this._lineDash) {
  105. this._lineDash = null;
  106. this._dashOffset = 0;
  107. }
  108. if (this._pathSegLen) {
  109. this._pathSegLen = null;
  110. this._pathLen = 0;
  111. }
  112. this._version++;
  113. };
  114. PathProxy.prototype.moveTo = function (x, y) {
  115. this._drawPendingPt();
  116. this.addData(CMD.M, x, y);
  117. this._ctx && this._ctx.moveTo(x, y);
  118. this._x0 = x;
  119. this._y0 = y;
  120. this._xi = x;
  121. this._yi = y;
  122. return this;
  123. };
  124. PathProxy.prototype.lineTo = function (x, y) {
  125. var dx = mathAbs(x - this._xi);
  126. var dy = mathAbs(y - this._yi);
  127. var exceedUnit = dx > this._ux || dy > this._uy;
  128. this.addData(CMD.L, x, y);
  129. if (this._ctx && exceedUnit) {
  130. this._needsDash ? this._dashedLineTo(x, y)
  131. : this._ctx.lineTo(x, y);
  132. }
  133. if (exceedUnit) {
  134. this._xi = x;
  135. this._yi = y;
  136. this._pendingPtDist = 0;
  137. }
  138. else {
  139. var d2 = dx * dx + dy * dy;
  140. if (d2 > this._pendingPtDist) {
  141. this._pendingPtX = x;
  142. this._pendingPtY = y;
  143. this._pendingPtDist = d2;
  144. }
  145. }
  146. return this;
  147. };
  148. PathProxy.prototype.bezierCurveTo = function (x1, y1, x2, y2, x3, y3) {
  149. this._drawPendingPt();
  150. this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
  151. if (this._ctx) {
  152. this._needsDash ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3)
  153. : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
  154. }
  155. this._xi = x3;
  156. this._yi = y3;
  157. return this;
  158. };
  159. PathProxy.prototype.quadraticCurveTo = function (x1, y1, x2, y2) {
  160. this._drawPendingPt();
  161. this.addData(CMD.Q, x1, y1, x2, y2);
  162. if (this._ctx) {
  163. this._needsDash ? this._dashedQuadraticTo(x1, y1, x2, y2)
  164. : this._ctx.quadraticCurveTo(x1, y1, x2, y2);
  165. }
  166. this._xi = x2;
  167. this._yi = y2;
  168. return this;
  169. };
  170. PathProxy.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {
  171. this._drawPendingPt();
  172. tmpAngles[0] = startAngle;
  173. tmpAngles[1] = endAngle;
  174. normalizeArcAngles(tmpAngles, anticlockwise);
  175. startAngle = tmpAngles[0];
  176. endAngle = tmpAngles[1];
  177. var delta = endAngle - startAngle;
  178. this.addData(CMD.A, cx, cy, r, r, startAngle, delta, 0, anticlockwise ? 0 : 1);
  179. this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
  180. this._xi = mathCos(endAngle) * r + cx;
  181. this._yi = mathSin(endAngle) * r + cy;
  182. return this;
  183. };
  184. PathProxy.prototype.arcTo = function (x1, y1, x2, y2, radius) {
  185. this._drawPendingPt();
  186. if (this._ctx) {
  187. this._ctx.arcTo(x1, y1, x2, y2, radius);
  188. }
  189. return this;
  190. };
  191. PathProxy.prototype.rect = function (x, y, w, h) {
  192. this._drawPendingPt();
  193. this._ctx && this._ctx.rect(x, y, w, h);
  194. this.addData(CMD.R, x, y, w, h);
  195. return this;
  196. };
  197. PathProxy.prototype.closePath = function () {
  198. this._drawPendingPt();
  199. this.addData(CMD.Z);
  200. var ctx = this._ctx;
  201. var x0 = this._x0;
  202. var y0 = this._y0;
  203. if (ctx) {
  204. this._needsDash && this._dashedLineTo(x0, y0);
  205. ctx.closePath();
  206. }
  207. this._xi = x0;
  208. this._yi = y0;
  209. return this;
  210. };
  211. PathProxy.prototype.fill = function (ctx) {
  212. ctx && ctx.fill();
  213. this.toStatic();
  214. };
  215. PathProxy.prototype.stroke = function (ctx) {
  216. ctx && ctx.stroke();
  217. this.toStatic();
  218. };
  219. PathProxy.prototype.setLineDash = function (lineDash) {
  220. if (lineDash instanceof Array) {
  221. this._lineDash = lineDash;
  222. this._dashIdx = 0;
  223. var lineDashSum = 0;
  224. for (var i = 0; i < lineDash.length; i++) {
  225. lineDashSum += lineDash[i];
  226. }
  227. this._dashSum = lineDashSum;
  228. this._needsDash = true;
  229. }
  230. else {
  231. this._lineDash = null;
  232. this._needsDash = false;
  233. }
  234. return this;
  235. };
  236. PathProxy.prototype.setLineDashOffset = function (offset) {
  237. this._dashOffset = offset;
  238. return this;
  239. };
  240. PathProxy.prototype.len = function () {
  241. return this._len;
  242. };
  243. PathProxy.prototype.setData = function (data) {
  244. var len = data.length;
  245. if (!(this.data && this.data.length === len) && hasTypedArray) {
  246. this.data = new Float32Array(len);
  247. }
  248. for (var i = 0; i < len; i++) {
  249. this.data[i] = data[i];
  250. }
  251. this._len = len;
  252. };
  253. PathProxy.prototype.appendPath = function (path) {
  254. if (!(path instanceof Array)) {
  255. path = [path];
  256. }
  257. var len = path.length;
  258. var appendSize = 0;
  259. var offset = this._len;
  260. for (var i = 0; i < len; i++) {
  261. appendSize += path[i].len();
  262. }
  263. if (hasTypedArray && (this.data instanceof Float32Array)) {
  264. this.data = new Float32Array(offset + appendSize);
  265. }
  266. for (var i = 0; i < len; i++) {
  267. var appendPathData = path[i].data;
  268. for (var k = 0; k < appendPathData.length; k++) {
  269. this.data[offset++] = appendPathData[k];
  270. }
  271. }
  272. this._len = offset;
  273. };
  274. PathProxy.prototype.addData = function (cmd, a, b, c, d, e, f, g, h) {
  275. if (!this._saveData) {
  276. return;
  277. }
  278. var data = this.data;
  279. if (this._len + arguments.length > data.length) {
  280. this._expandData();
  281. data = this.data;
  282. }
  283. for (var i = 0; i < arguments.length; i++) {
  284. data[this._len++] = arguments[i];
  285. }
  286. };
  287. PathProxy.prototype._drawPendingPt = function () {
  288. if (this._pendingPtDist > 0) {
  289. this._ctx && this._ctx.lineTo(this._pendingPtX, this._pendingPtY);
  290. this._pendingPtDist = 0;
  291. }
  292. };
  293. PathProxy.prototype._expandData = function () {
  294. if (!(this.data instanceof Array)) {
  295. var newData = [];
  296. for (var i = 0; i < this._len; i++) {
  297. newData[i] = this.data[i];
  298. }
  299. this.data = newData;
  300. }
  301. };
  302. PathProxy.prototype._dashedLineTo = function (x1, y1) {
  303. var dashSum = this._dashSum;
  304. var lineDash = this._lineDash;
  305. var ctx = this._ctx;
  306. var offset = this._dashOffset;
  307. var x0 = this._xi;
  308. var y0 = this._yi;
  309. var dx = x1 - x0;
  310. var dy = y1 - y0;
  311. var dist = mathSqrt(dx * dx + dy * dy);
  312. var x = x0;
  313. var y = y0;
  314. var nDash = lineDash.length;
  315. var dash;
  316. var idx;
  317. dx /= dist;
  318. dy /= dist;
  319. if (offset < 0) {
  320. offset = dashSum + offset;
  321. }
  322. offset %= dashSum;
  323. x -= offset * dx;
  324. y -= offset * dy;
  325. while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1)
  326. || (dx === 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) {
  327. idx = this._dashIdx;
  328. dash = lineDash[idx];
  329. x += dx * dash;
  330. y += dy * dash;
  331. this._dashIdx = (idx + 1) % nDash;
  332. if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) {
  333. continue;
  334. }
  335. ctx[idx % 2 ? 'moveTo' : 'lineTo'](dx >= 0 ? mathMin(x, x1) : mathMax(x, x1), dy >= 0 ? mathMin(y, y1) : mathMax(y, y1));
  336. }
  337. dx = x - x1;
  338. dy = y - y1;
  339. this._dashOffset = -mathSqrt(dx * dx + dy * dy);
  340. };
  341. PathProxy.prototype._dashedBezierTo = function (x1, y1, x2, y2, x3, y3) {
  342. var ctx = this._ctx;
  343. var dashSum = this._dashSum;
  344. var offset = this._dashOffset;
  345. var lineDash = this._lineDash;
  346. var x0 = this._xi;
  347. var y0 = this._yi;
  348. var bezierLen = 0;
  349. var idx = this._dashIdx;
  350. var nDash = lineDash.length;
  351. var t;
  352. var dx;
  353. var dy;
  354. var x;
  355. var y;
  356. var tmpLen = 0;
  357. if (offset < 0) {
  358. offset = dashSum + offset;
  359. }
  360. offset %= dashSum;
  361. for (t = 0; t < 1; t += 0.1) {
  362. dx = cubicAt(x0, x1, x2, x3, t + 0.1)
  363. - cubicAt(x0, x1, x2, x3, t);
  364. dy = cubicAt(y0, y1, y2, y3, t + 0.1)
  365. - cubicAt(y0, y1, y2, y3, t);
  366. bezierLen += mathSqrt(dx * dx + dy * dy);
  367. }
  368. for (; idx < nDash; idx++) {
  369. tmpLen += lineDash[idx];
  370. if (tmpLen > offset) {
  371. break;
  372. }
  373. }
  374. t = (tmpLen - offset) / bezierLen;
  375. while (t <= 1) {
  376. x = cubicAt(x0, x1, x2, x3, t);
  377. y = cubicAt(y0, y1, y2, y3, t);
  378. idx % 2 ? ctx.moveTo(x, y)
  379. : ctx.lineTo(x, y);
  380. t += lineDash[idx] / bezierLen;
  381. idx = (idx + 1) % nDash;
  382. }
  383. (idx % 2 !== 0) && ctx.lineTo(x3, y3);
  384. dx = x3 - x;
  385. dy = y3 - y;
  386. this._dashOffset = -mathSqrt(dx * dx + dy * dy);
  387. };
  388. PathProxy.prototype._dashedQuadraticTo = function (x1, y1, x2, y2) {
  389. var x3 = x2;
  390. var y3 = y2;
  391. x2 = (x2 + 2 * x1) / 3;
  392. y2 = (y2 + 2 * y1) / 3;
  393. x1 = (this._xi + 2 * x1) / 3;
  394. y1 = (this._yi + 2 * y1) / 3;
  395. this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
  396. };
  397. PathProxy.prototype.toStatic = function () {
  398. if (!this._saveData) {
  399. return;
  400. }
  401. this._drawPendingPt();
  402. var data = this.data;
  403. if (data instanceof Array) {
  404. data.length = this._len;
  405. if (hasTypedArray && this._len > 11) {
  406. this.data = new Float32Array(data);
  407. }
  408. }
  409. };
  410. PathProxy.prototype.getBoundingRect = function () {
  411. min[0] = min[1] = min2[0] = min2[1] = Number.MAX_VALUE;
  412. max[0] = max[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
  413. var data = this.data;
  414. var xi = 0;
  415. var yi = 0;
  416. var x0 = 0;
  417. var y0 = 0;
  418. var i;
  419. for (i = 0; i < this._len;) {
  420. var cmd = data[i++];
  421. var isFirst = i === 1;
  422. if (isFirst) {
  423. xi = data[i];
  424. yi = data[i + 1];
  425. x0 = xi;
  426. y0 = yi;
  427. }
  428. switch (cmd) {
  429. case CMD.M:
  430. xi = x0 = data[i++];
  431. yi = y0 = data[i++];
  432. min2[0] = x0;
  433. min2[1] = y0;
  434. max2[0] = x0;
  435. max2[1] = y0;
  436. break;
  437. case CMD.L:
  438. fromLine(xi, yi, data[i], data[i + 1], min2, max2);
  439. xi = data[i++];
  440. yi = data[i++];
  441. break;
  442. case CMD.C:
  443. fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);
  444. xi = data[i++];
  445. yi = data[i++];
  446. break;
  447. case CMD.Q:
  448. fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);
  449. xi = data[i++];
  450. yi = data[i++];
  451. break;
  452. case CMD.A:
  453. var cx = data[i++];
  454. var cy = data[i++];
  455. var rx = data[i++];
  456. var ry = data[i++];
  457. var startAngle = data[i++];
  458. var endAngle = data[i++] + startAngle;
  459. i += 1;
  460. var anticlockwise = !data[i++];
  461. if (isFirst) {
  462. x0 = mathCos(startAngle) * rx + cx;
  463. y0 = mathSin(startAngle) * ry + cy;
  464. }
  465. fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);
  466. xi = mathCos(endAngle) * rx + cx;
  467. yi = mathSin(endAngle) * ry + cy;
  468. break;
  469. case CMD.R:
  470. x0 = xi = data[i++];
  471. y0 = yi = data[i++];
  472. var width = data[i++];
  473. var height = data[i++];
  474. fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
  475. break;
  476. case CMD.Z:
  477. xi = x0;
  478. yi = y0;
  479. break;
  480. }
  481. vec2.min(min, min, min2);
  482. vec2.max(max, max, max2);
  483. }
  484. if (i === 0) {
  485. min[0] = min[1] = max[0] = max[1] = 0;
  486. }
  487. return new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);
  488. };
  489. PathProxy.prototype._calculateLength = function () {
  490. var data = this.data;
  491. var len = this._len;
  492. var ux = this._ux;
  493. var uy = this._uy;
  494. var xi = 0;
  495. var yi = 0;
  496. var x0 = 0;
  497. var y0 = 0;
  498. if (!this._pathSegLen) {
  499. this._pathSegLen = [];
  500. }
  501. var pathSegLen = this._pathSegLen;
  502. var pathTotalLen = 0;
  503. var segCount = 0;
  504. for (var i = 0; i < len;) {
  505. var cmd = data[i++];
  506. var isFirst = i === 1;
  507. if (isFirst) {
  508. xi = data[i];
  509. yi = data[i + 1];
  510. x0 = xi;
  511. y0 = yi;
  512. }
  513. var l = -1;
  514. switch (cmd) {
  515. case CMD.M:
  516. xi = x0 = data[i++];
  517. yi = y0 = data[i++];
  518. break;
  519. case CMD.L: {
  520. var x2 = data[i++];
  521. var y2 = data[i++];
  522. var dx = x2 - xi;
  523. var dy = y2 - yi;
  524. if (mathAbs(dx) > ux || mathAbs(dy) > uy || i === len - 1) {
  525. l = Math.sqrt(dx * dx + dy * dy);
  526. xi = x2;
  527. yi = y2;
  528. }
  529. break;
  530. }
  531. case CMD.C: {
  532. var x1 = data[i++];
  533. var y1 = data[i++];
  534. var x2 = data[i++];
  535. var y2 = data[i++];
  536. var x3 = data[i++];
  537. var y3 = data[i++];
  538. l = cubicLength(xi, yi, x1, y1, x2, y2, x3, y3, 10);
  539. xi = x3;
  540. yi = y3;
  541. break;
  542. }
  543. case CMD.Q: {
  544. var x1 = data[i++];
  545. var y1 = data[i++];
  546. var x2 = data[i++];
  547. var y2 = data[i++];
  548. l = quadraticLength(xi, yi, x1, y1, x2, y2, 10);
  549. xi = x2;
  550. yi = y2;
  551. break;
  552. }
  553. case CMD.A:
  554. var cx = data[i++];
  555. var cy = data[i++];
  556. var rx = data[i++];
  557. var ry = data[i++];
  558. var startAngle = data[i++];
  559. var delta = data[i++];
  560. var endAngle = delta + startAngle;
  561. i += 1;
  562. var anticlockwise = !data[i++];
  563. if (isFirst) {
  564. x0 = mathCos(startAngle) * rx + cx;
  565. y0 = mathSin(startAngle) * ry + cy;
  566. }
  567. l = mathMax(rx, ry) * mathMin(PI2, Math.abs(delta));
  568. xi = mathCos(endAngle) * rx + cx;
  569. yi = mathSin(endAngle) * ry + cy;
  570. break;
  571. case CMD.R: {
  572. x0 = xi = data[i++];
  573. y0 = yi = data[i++];
  574. var width = data[i++];
  575. var height = data[i++];
  576. l = width * 2 + height * 2;
  577. break;
  578. }
  579. case CMD.Z: {
  580. var dx = x0 - xi;
  581. var dy = y0 - yi;
  582. l = Math.sqrt(dx * dx + dy * dy);
  583. xi = x0;
  584. yi = y0;
  585. break;
  586. }
  587. }
  588. if (l >= 0) {
  589. pathSegLen[segCount++] = l;
  590. pathTotalLen += l;
  591. }
  592. }
  593. this._pathLen = pathTotalLen;
  594. return pathTotalLen;
  595. };
  596. PathProxy.prototype.rebuildPath = function (ctx, percent) {
  597. var d = this.data;
  598. var ux = this._ux;
  599. var uy = this._uy;
  600. var len = this._len;
  601. var x0;
  602. var y0;
  603. var xi;
  604. var yi;
  605. var x;
  606. var y;
  607. var drawPart = percent < 1;
  608. var pathSegLen;
  609. var pathTotalLen;
  610. var accumLength = 0;
  611. var segCount = 0;
  612. var displayedLength;
  613. var pendingPtDist = 0;
  614. var pendingPtX;
  615. var pendingPtY;
  616. if (drawPart) {
  617. if (!this._pathSegLen) {
  618. this._calculateLength();
  619. }
  620. pathSegLen = this._pathSegLen;
  621. pathTotalLen = this._pathLen;
  622. displayedLength = percent * pathTotalLen;
  623. if (!displayedLength) {
  624. return;
  625. }
  626. }
  627. lo: for (var i = 0; i < len;) {
  628. var cmd = d[i++];
  629. var isFirst = i === 1;
  630. if (isFirst) {
  631. xi = d[i];
  632. yi = d[i + 1];
  633. x0 = xi;
  634. y0 = yi;
  635. }
  636. if (cmd !== CMD.L && pendingPtDist > 0) {
  637. ctx.lineTo(pendingPtX, pendingPtY);
  638. pendingPtDist = 0;
  639. }
  640. switch (cmd) {
  641. case CMD.M:
  642. x0 = xi = d[i++];
  643. y0 = yi = d[i++];
  644. ctx.moveTo(xi, yi);
  645. break;
  646. case CMD.L: {
  647. x = d[i++];
  648. y = d[i++];
  649. var dx = mathAbs(x - xi);
  650. var dy = mathAbs(y - yi);
  651. if (dx > ux || dy > uy) {
  652. if (drawPart) {
  653. var l = pathSegLen[segCount++];
  654. if (accumLength + l > displayedLength) {
  655. var t = (displayedLength - accumLength) / l;
  656. ctx.lineTo(xi * (1 - t) + x * t, yi * (1 - t) + y * t);
  657. break lo;
  658. }
  659. accumLength += l;
  660. }
  661. ctx.lineTo(x, y);
  662. xi = x;
  663. yi = y;
  664. pendingPtDist = 0;
  665. }
  666. else {
  667. var d2 = dx * dx + dy * dy;
  668. if (d2 > pendingPtDist) {
  669. pendingPtX = x;
  670. pendingPtY = y;
  671. pendingPtDist = d2;
  672. }
  673. }
  674. break;
  675. }
  676. case CMD.C: {
  677. var x1 = d[i++];
  678. var y1 = d[i++];
  679. var x2 = d[i++];
  680. var y2 = d[i++];
  681. var x3 = d[i++];
  682. var y3 = d[i++];
  683. if (drawPart) {
  684. var l = pathSegLen[segCount++];
  685. if (accumLength + l > displayedLength) {
  686. var t = (displayedLength - accumLength) / l;
  687. cubicSubdivide(xi, x1, x2, x3, t, tmpOutX);
  688. cubicSubdivide(yi, y1, y2, y3, t, tmpOutY);
  689. ctx.bezierCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2], tmpOutX[3], tmpOutY[3]);
  690. break lo;
  691. }
  692. accumLength += l;
  693. }
  694. ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
  695. xi = x3;
  696. yi = y3;
  697. break;
  698. }
  699. case CMD.Q: {
  700. var x1 = d[i++];
  701. var y1 = d[i++];
  702. var x2 = d[i++];
  703. var y2 = d[i++];
  704. if (drawPart) {
  705. var l = pathSegLen[segCount++];
  706. if (accumLength + l > displayedLength) {
  707. var t = (displayedLength - accumLength) / l;
  708. quadraticSubdivide(xi, x1, x2, t, tmpOutX);
  709. quadraticSubdivide(yi, y1, y2, t, tmpOutY);
  710. ctx.quadraticCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2]);
  711. break lo;
  712. }
  713. accumLength += l;
  714. }
  715. ctx.quadraticCurveTo(x1, y1, x2, y2);
  716. xi = x2;
  717. yi = y2;
  718. break;
  719. }
  720. case CMD.A:
  721. var cx = d[i++];
  722. var cy = d[i++];
  723. var rx = d[i++];
  724. var ry = d[i++];
  725. var startAngle = d[i++];
  726. var delta = d[i++];
  727. var psi = d[i++];
  728. var anticlockwise = !d[i++];
  729. var r = (rx > ry) ? rx : ry;
  730. var isEllipse = mathAbs(rx - ry) > 1e-3;
  731. var endAngle = startAngle + delta;
  732. var breakBuild = false;
  733. if (drawPart) {
  734. var l = pathSegLen[segCount++];
  735. if (accumLength + l > displayedLength) {
  736. endAngle = startAngle + delta * (displayedLength - accumLength) / l;
  737. breakBuild = true;
  738. }
  739. accumLength += l;
  740. }
  741. if (isEllipse && ctx.ellipse) {
  742. ctx.ellipse(cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise);
  743. }
  744. else {
  745. ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
  746. }
  747. if (breakBuild) {
  748. break lo;
  749. }
  750. if (isFirst) {
  751. x0 = mathCos(startAngle) * rx + cx;
  752. y0 = mathSin(startAngle) * ry + cy;
  753. }
  754. xi = mathCos(endAngle) * rx + cx;
  755. yi = mathSin(endAngle) * ry + cy;
  756. break;
  757. case CMD.R:
  758. x0 = xi = d[i];
  759. y0 = yi = d[i + 1];
  760. x = d[i++];
  761. y = d[i++];
  762. var width = d[i++];
  763. var height = d[i++];
  764. if (drawPart) {
  765. var l = pathSegLen[segCount++];
  766. if (accumLength + l > displayedLength) {
  767. var d_1 = displayedLength - accumLength;
  768. ctx.moveTo(x, y);
  769. ctx.lineTo(x + mathMin(d_1, width), y);
  770. d_1 -= width;
  771. if (d_1 > 0) {
  772. ctx.lineTo(x + width, y + mathMin(d_1, height));
  773. }
  774. d_1 -= height;
  775. if (d_1 > 0) {
  776. ctx.lineTo(x + mathMax(width - d_1, 0), y + height);
  777. }
  778. d_1 -= width;
  779. if (d_1 > 0) {
  780. ctx.lineTo(x, y + mathMax(height - d_1, 0));
  781. }
  782. break lo;
  783. }
  784. accumLength += l;
  785. }
  786. ctx.rect(x, y, width, height);
  787. break;
  788. case CMD.Z:
  789. if (drawPart) {
  790. var l = pathSegLen[segCount++];
  791. if (accumLength + l > displayedLength) {
  792. var t = (displayedLength - accumLength) / l;
  793. ctx.lineTo(xi * (1 - t) + x0 * t, yi * (1 - t) + y0 * t);
  794. break lo;
  795. }
  796. accumLength += l;
  797. }
  798. ctx.closePath();
  799. xi = x0;
  800. yi = y0;
  801. }
  802. }
  803. };
  804. PathProxy.prototype.clone = function () {
  805. var newProxy = new PathProxy();
  806. var data = this.data;
  807. newProxy.data = data.slice ? data.slice()
  808. : Array.prototype.slice.call(data);
  809. newProxy._len = this._len;
  810. return newProxy;
  811. };
  812. PathProxy.CMD = CMD;
  813. PathProxy.initDefaultProps = (function () {
  814. var proto = PathProxy.prototype;
  815. proto._saveData = true;
  816. proto._needsDash = false;
  817. proto._dashOffset = 0;
  818. proto._dashIdx = 0;
  819. proto._dashSum = 0;
  820. proto._ux = 0;
  821. proto._uy = 0;
  822. proto._pendingPtDist = 0;
  823. proto._version = 0;
  824. })();
  825. return PathProxy;
  826. }());
  827. export default PathProxy;