PathProxy.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. var curve = require("./curve");
  2. var vec2 = require("./vector");
  3. var bbox = require("./bbox");
  4. var BoundingRect = require("./BoundingRect");
  5. var _config = require("../config");
  6. var dpr = _config.devicePixelRatio;
  7. /**
  8. * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中
  9. * 可以用于 isInsidePath 判断以及获取boundingRect
  10. *
  11. * @module zrender/core/PathProxy
  12. * @author Yi Shen (http://www.github.com/pissang)
  13. */
  14. // TODO getTotalLength, getPointAtLength
  15. /* global Float32Array */
  16. var CMD = {
  17. M: 1,
  18. L: 2,
  19. C: 3,
  20. Q: 4,
  21. A: 5,
  22. Z: 6,
  23. // Rect
  24. R: 7
  25. }; // var CMD_MEM_SIZE = {
  26. // M: 3,
  27. // L: 3,
  28. // C: 7,
  29. // Q: 5,
  30. // A: 9,
  31. // R: 5,
  32. // Z: 1
  33. // };
  34. var min = [];
  35. var max = [];
  36. var min2 = [];
  37. var max2 = [];
  38. var mathMin = Math.min;
  39. var mathMax = Math.max;
  40. var mathCos = Math.cos;
  41. var mathSin = Math.sin;
  42. var mathSqrt = Math.sqrt;
  43. var mathAbs = Math.abs;
  44. var hasTypedArray = typeof Float32Array !== 'undefined';
  45. /**
  46. * @alias module:zrender/core/PathProxy
  47. * @constructor
  48. */
  49. var PathProxy = function (notSaveData) {
  50. this._saveData = !(notSaveData || false);
  51. if (this._saveData) {
  52. /**
  53. * Path data. Stored as flat array
  54. * @type {Array.<Object>}
  55. */
  56. this.data = [];
  57. }
  58. this._ctx = null;
  59. };
  60. /**
  61. * 快速计算Path包围盒(并不是最小包围盒)
  62. * @return {Object}
  63. */
  64. PathProxy.prototype = {
  65. constructor: PathProxy,
  66. _xi: 0,
  67. _yi: 0,
  68. _x0: 0,
  69. _y0: 0,
  70. // Unit x, Unit y. Provide for avoiding drawing that too short line segment
  71. _ux: 0,
  72. _uy: 0,
  73. _len: 0,
  74. _lineDash: null,
  75. _dashOffset: 0,
  76. _dashIdx: 0,
  77. _dashSum: 0,
  78. /**
  79. * @readOnly
  80. */
  81. setScale: function (sx, sy, segmentIgnoreThreshold) {
  82. // Compat. Previously there is no segmentIgnoreThreshold.
  83. segmentIgnoreThreshold = segmentIgnoreThreshold || 0;
  84. this._ux = mathAbs(segmentIgnoreThreshold / dpr / sx) || 0;
  85. this._uy = mathAbs(segmentIgnoreThreshold / dpr / sy) || 0;
  86. },
  87. getContext: function () {
  88. return this._ctx;
  89. },
  90. /**
  91. * @param {CanvasRenderingContext2D} ctx
  92. * @return {module:zrender/core/PathProxy}
  93. */
  94. beginPath: function (ctx) {
  95. this._ctx = ctx;
  96. ctx && ctx.beginPath();
  97. ctx && (this.dpr = ctx.dpr); // Reset
  98. if (this._saveData) {
  99. this._len = 0;
  100. }
  101. if (this._lineDash) {
  102. this._lineDash = null;
  103. this._dashOffset = 0;
  104. }
  105. return this;
  106. },
  107. /**
  108. * @param {number} x
  109. * @param {number} y
  110. * @return {module:zrender/core/PathProxy}
  111. */
  112. moveTo: function (x, y) {
  113. this.addData(CMD.M, x, y);
  114. this._ctx && this._ctx.moveTo(x, y); // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用
  115. // xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。
  116. // 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要
  117. // 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持
  118. this._x0 = x;
  119. this._y0 = y;
  120. this._xi = x;
  121. this._yi = y;
  122. return this;
  123. },
  124. /**
  125. * @param {number} x
  126. * @param {number} y
  127. * @return {module:zrender/core/PathProxy}
  128. */
  129. lineTo: function (x, y) {
  130. var exceedUnit = mathAbs(x - this._xi) > this._ux || mathAbs(y - this._yi) > this._uy // Force draw the first segment
  131. || this._len < 5;
  132. this.addData(CMD.L, x, y);
  133. if (this._ctx && exceedUnit) {
  134. this._needsDash() ? this._dashedLineTo(x, y) : this._ctx.lineTo(x, y);
  135. }
  136. if (exceedUnit) {
  137. this._xi = x;
  138. this._yi = y;
  139. }
  140. return this;
  141. },
  142. /**
  143. * @param {number} x1
  144. * @param {number} y1
  145. * @param {number} x2
  146. * @param {number} y2
  147. * @param {number} x3
  148. * @param {number} y3
  149. * @return {module:zrender/core/PathProxy}
  150. */
  151. bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {
  152. this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
  153. if (this._ctx) {
  154. this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3) : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
  155. }
  156. this._xi = x3;
  157. this._yi = y3;
  158. return this;
  159. },
  160. /**
  161. * @param {number} x1
  162. * @param {number} y1
  163. * @param {number} x2
  164. * @param {number} y2
  165. * @return {module:zrender/core/PathProxy}
  166. */
  167. quadraticCurveTo: function (x1, y1, x2, y2) {
  168. this.addData(CMD.Q, x1, y1, x2, y2);
  169. if (this._ctx) {
  170. this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2) : this._ctx.quadraticCurveTo(x1, y1, x2, y2);
  171. }
  172. this._xi = x2;
  173. this._yi = y2;
  174. return this;
  175. },
  176. /**
  177. * @param {number} cx
  178. * @param {number} cy
  179. * @param {number} r
  180. * @param {number} startAngle
  181. * @param {number} endAngle
  182. * @param {boolean} anticlockwise
  183. * @return {module:zrender/core/PathProxy}
  184. */
  185. arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) {
  186. this.addData(CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1);
  187. this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
  188. this._xi = mathCos(endAngle) * r + cx;
  189. this._yi = mathSin(endAngle) * r + cy;
  190. return this;
  191. },
  192. // TODO
  193. arcTo: function (x1, y1, x2, y2, radius) {
  194. if (this._ctx) {
  195. this._ctx.arcTo(x1, y1, x2, y2, radius);
  196. }
  197. return this;
  198. },
  199. // TODO
  200. rect: function (x, y, w, h) {
  201. this._ctx && this._ctx.rect(x, y, w, h);
  202. this.addData(CMD.R, x, y, w, h);
  203. return this;
  204. },
  205. /**
  206. * @return {module:zrender/core/PathProxy}
  207. */
  208. closePath: function () {
  209. this.addData(CMD.Z);
  210. var ctx = this._ctx;
  211. var x0 = this._x0;
  212. var y0 = this._y0;
  213. if (ctx) {
  214. this._needsDash() && this._dashedLineTo(x0, y0);
  215. ctx.closePath();
  216. }
  217. this._xi = x0;
  218. this._yi = y0;
  219. return this;
  220. },
  221. /**
  222. * Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。
  223. * stroke 同样
  224. * @param {CanvasRenderingContext2D} ctx
  225. * @return {module:zrender/core/PathProxy}
  226. */
  227. fill: function (ctx) {
  228. ctx && ctx.fill();
  229. this.toStatic();
  230. },
  231. /**
  232. * @param {CanvasRenderingContext2D} ctx
  233. * @return {module:zrender/core/PathProxy}
  234. */
  235. stroke: function (ctx) {
  236. ctx && ctx.stroke();
  237. this.toStatic();
  238. },
  239. /**
  240. * 必须在其它绘制命令前调用
  241. * Must be invoked before all other path drawing methods
  242. * @return {module:zrender/core/PathProxy}
  243. */
  244. setLineDash: function (lineDash) {
  245. if (lineDash instanceof Array) {
  246. this._lineDash = lineDash;
  247. this._dashIdx = 0;
  248. var lineDashSum = 0;
  249. for (var i = 0; i < lineDash.length; i++) {
  250. lineDashSum += lineDash[i];
  251. }
  252. this._dashSum = lineDashSum;
  253. }
  254. return this;
  255. },
  256. /**
  257. * 必须在其它绘制命令前调用
  258. * Must be invoked before all other path drawing methods
  259. * @return {module:zrender/core/PathProxy}
  260. */
  261. setLineDashOffset: function (offset) {
  262. this._dashOffset = offset;
  263. return this;
  264. },
  265. /**
  266. *
  267. * @return {boolean}
  268. */
  269. len: function () {
  270. return this._len;
  271. },
  272. /**
  273. * 直接设置 Path 数据
  274. */
  275. setData: function (data) {
  276. var len = data.length;
  277. if (!(this.data && this.data.length === len) && hasTypedArray) {
  278. this.data = new Float32Array(len);
  279. }
  280. for (var i = 0; i < len; i++) {
  281. this.data[i] = data[i];
  282. }
  283. this._len = len;
  284. },
  285. /**
  286. * 添加子路径
  287. * @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path
  288. */
  289. appendPath: function (path) {
  290. if (!(path instanceof Array)) {
  291. path = [path];
  292. }
  293. var len = path.length;
  294. var appendSize = 0;
  295. var offset = this._len;
  296. for (var i = 0; i < len; i++) {
  297. appendSize += path[i].len();
  298. }
  299. if (hasTypedArray && this.data instanceof Float32Array) {
  300. this.data = new Float32Array(offset + appendSize);
  301. }
  302. for (var i = 0; i < len; i++) {
  303. var appendPathData = path[i].data;
  304. for (var k = 0; k < appendPathData.length; k++) {
  305. this.data[offset++] = appendPathData[k];
  306. }
  307. }
  308. this._len = offset;
  309. },
  310. /**
  311. * 填充 Path 数据。
  312. * 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。
  313. */
  314. addData: function (cmd) {
  315. if (!this._saveData) {
  316. return;
  317. }
  318. var data = this.data;
  319. if (this._len + arguments.length > data.length) {
  320. // 因为之前的数组已经转换成静态的 Float32Array
  321. // 所以不够用时需要扩展一个新的动态数组
  322. this._expandData();
  323. data = this.data;
  324. }
  325. for (var i = 0; i < arguments.length; i++) {
  326. data[this._len++] = arguments[i];
  327. }
  328. this._prevCmd = cmd;
  329. },
  330. _expandData: function () {
  331. // Only if data is Float32Array
  332. if (!(this.data instanceof Array)) {
  333. var newData = [];
  334. for (var i = 0; i < this._len; i++) {
  335. newData[i] = this.data[i];
  336. }
  337. this.data = newData;
  338. }
  339. },
  340. /**
  341. * If needs js implemented dashed line
  342. * @return {boolean}
  343. * @private
  344. */
  345. _needsDash: function () {
  346. return this._lineDash;
  347. },
  348. _dashedLineTo: function (x1, y1) {
  349. var dashSum = this._dashSum;
  350. var offset = this._dashOffset;
  351. var lineDash = this._lineDash;
  352. var ctx = this._ctx;
  353. var x0 = this._xi;
  354. var y0 = this._yi;
  355. var dx = x1 - x0;
  356. var dy = y1 - y0;
  357. var dist = mathSqrt(dx * dx + dy * dy);
  358. var x = x0;
  359. var y = y0;
  360. var dash;
  361. var nDash = lineDash.length;
  362. var idx;
  363. dx /= dist;
  364. dy /= dist;
  365. if (offset < 0) {
  366. // Convert to positive offset
  367. offset = dashSum + offset;
  368. }
  369. offset %= dashSum;
  370. x -= offset * dx;
  371. y -= offset * dy;
  372. while (dx > 0 && x <= x1 || dx < 0 && x >= x1 || dx === 0 && (dy > 0 && y <= y1 || dy < 0 && y >= y1)) {
  373. idx = this._dashIdx;
  374. dash = lineDash[idx];
  375. x += dx * dash;
  376. y += dy * dash;
  377. this._dashIdx = (idx + 1) % nDash; // Skip positive offset
  378. if (dx > 0 && x < x0 || dx < 0 && x > x0 || dy > 0 && y < y0 || dy < 0 && y > y0) {
  379. continue;
  380. }
  381. ctx[idx % 2 ? 'moveTo' : 'lineTo'](dx >= 0 ? mathMin(x, x1) : mathMax(x, x1), dy >= 0 ? mathMin(y, y1) : mathMax(y, y1));
  382. } // Offset for next lineTo
  383. dx = x - x1;
  384. dy = y - y1;
  385. this._dashOffset = -mathSqrt(dx * dx + dy * dy);
  386. },
  387. // Not accurate dashed line to
  388. _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) {
  389. var dashSum = this._dashSum;
  390. var offset = this._dashOffset;
  391. var lineDash = this._lineDash;
  392. var ctx = this._ctx;
  393. var x0 = this._xi;
  394. var y0 = this._yi;
  395. var t;
  396. var dx;
  397. var dy;
  398. var cubicAt = curve.cubicAt;
  399. var bezierLen = 0;
  400. var idx = this._dashIdx;
  401. var nDash = lineDash.length;
  402. var x;
  403. var y;
  404. var tmpLen = 0;
  405. if (offset < 0) {
  406. // Convert to positive offset
  407. offset = dashSum + offset;
  408. }
  409. offset %= dashSum; // Bezier approx length
  410. for (t = 0; t < 1; t += 0.1) {
  411. dx = cubicAt(x0, x1, x2, x3, t + 0.1) - cubicAt(x0, x1, x2, x3, t);
  412. dy = cubicAt(y0, y1, y2, y3, t + 0.1) - cubicAt(y0, y1, y2, y3, t);
  413. bezierLen += mathSqrt(dx * dx + dy * dy);
  414. } // Find idx after add offset
  415. for (; idx < nDash; idx++) {
  416. tmpLen += lineDash[idx];
  417. if (tmpLen > offset) {
  418. break;
  419. }
  420. }
  421. t = (tmpLen - offset) / bezierLen;
  422. while (t <= 1) {
  423. x = cubicAt(x0, x1, x2, x3, t);
  424. y = cubicAt(y0, y1, y2, y3, t); // Use line to approximate dashed bezier
  425. // Bad result if dash is long
  426. idx % 2 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
  427. t += lineDash[idx] / bezierLen;
  428. idx = (idx + 1) % nDash;
  429. } // Finish the last segment and calculate the new offset
  430. idx % 2 !== 0 && ctx.lineTo(x3, y3);
  431. dx = x3 - x;
  432. dy = y3 - y;
  433. this._dashOffset = -mathSqrt(dx * dx + dy * dy);
  434. },
  435. _dashedQuadraticTo: function (x1, y1, x2, y2) {
  436. // Convert quadratic to cubic using degree elevation
  437. var x3 = x2;
  438. var y3 = y2;
  439. x2 = (x2 + 2 * x1) / 3;
  440. y2 = (y2 + 2 * y1) / 3;
  441. x1 = (this._xi + 2 * x1) / 3;
  442. y1 = (this._yi + 2 * y1) / 3;
  443. this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
  444. },
  445. /**
  446. * 转成静态的 Float32Array 减少堆内存占用
  447. * Convert dynamic array to static Float32Array
  448. */
  449. toStatic: function () {
  450. var data = this.data;
  451. if (data instanceof Array) {
  452. data.length = this._len;
  453. if (hasTypedArray) {
  454. this.data = new Float32Array(data);
  455. }
  456. }
  457. },
  458. /**
  459. * @return {module:zrender/core/BoundingRect}
  460. */
  461. getBoundingRect: function () {
  462. min[0] = min[1] = min2[0] = min2[1] = Number.MAX_VALUE;
  463. max[0] = max[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
  464. var data = this.data;
  465. var xi = 0;
  466. var yi = 0;
  467. var x0 = 0;
  468. var y0 = 0;
  469. for (var i = 0; i < data.length;) {
  470. var cmd = data[i++];
  471. if (i === 1) {
  472. // 如果第一个命令是 L, C, Q
  473. // 则 previous point 同绘制命令的第一个 point
  474. //
  475. // 第一个命令为 Arc 的情况下会在后面特殊处理
  476. xi = data[i];
  477. yi = data[i + 1];
  478. x0 = xi;
  479. y0 = yi;
  480. }
  481. switch (cmd) {
  482. case CMD.M:
  483. // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
  484. // 在 closePath 的时候使用
  485. x0 = data[i++];
  486. y0 = data[i++];
  487. xi = x0;
  488. yi = y0;
  489. min2[0] = x0;
  490. min2[1] = y0;
  491. max2[0] = x0;
  492. max2[1] = y0;
  493. break;
  494. case CMD.L:
  495. bbox.fromLine(xi, yi, data[i], data[i + 1], min2, max2);
  496. xi = data[i++];
  497. yi = data[i++];
  498. break;
  499. case CMD.C:
  500. bbox.fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);
  501. xi = data[i++];
  502. yi = data[i++];
  503. break;
  504. case CMD.Q:
  505. bbox.fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);
  506. xi = data[i++];
  507. yi = data[i++];
  508. break;
  509. case CMD.A:
  510. // TODO Arc 判断的开销比较大
  511. var cx = data[i++];
  512. var cy = data[i++];
  513. var rx = data[i++];
  514. var ry = data[i++];
  515. var startAngle = data[i++];
  516. var endAngle = data[i++] + startAngle; // TODO Arc 旋转
  517. i += 1;
  518. var anticlockwise = 1 - data[i++];
  519. if (i === 1) {
  520. // 直接使用 arc 命令
  521. // 第一个命令起点还未定义
  522. x0 = mathCos(startAngle) * rx + cx;
  523. y0 = mathSin(startAngle) * ry + cy;
  524. }
  525. bbox.fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);
  526. xi = mathCos(endAngle) * rx + cx;
  527. yi = mathSin(endAngle) * ry + cy;
  528. break;
  529. case CMD.R:
  530. x0 = xi = data[i++];
  531. y0 = yi = data[i++];
  532. var width = data[i++];
  533. var height = data[i++]; // Use fromLine
  534. bbox.fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
  535. break;
  536. case CMD.Z:
  537. xi = x0;
  538. yi = y0;
  539. break;
  540. } // Union
  541. vec2.min(min, min, min2);
  542. vec2.max(max, max, max2);
  543. } // No data
  544. if (i === 0) {
  545. min[0] = min[1] = max[0] = max[1] = 0;
  546. }
  547. return new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);
  548. },
  549. /**
  550. * Rebuild path from current data
  551. * Rebuild path will not consider javascript implemented line dash.
  552. * @param {CanvasRenderingContext2D} ctx
  553. */
  554. rebuildPath: function (ctx) {
  555. var d = this.data;
  556. var x0;
  557. var y0;
  558. var xi;
  559. var yi;
  560. var x;
  561. var y;
  562. var ux = this._ux;
  563. var uy = this._uy;
  564. var len = this._len;
  565. for (var i = 0; i < len;) {
  566. var cmd = d[i++];
  567. if (i === 1) {
  568. // 如果第一个命令是 L, C, Q
  569. // 则 previous point 同绘制命令的第一个 point
  570. //
  571. // 第一个命令为 Arc 的情况下会在后面特殊处理
  572. xi = d[i];
  573. yi = d[i + 1];
  574. x0 = xi;
  575. y0 = yi;
  576. }
  577. switch (cmd) {
  578. case CMD.M:
  579. x0 = xi = d[i++];
  580. y0 = yi = d[i++];
  581. ctx.moveTo(xi, yi);
  582. break;
  583. case CMD.L:
  584. x = d[i++];
  585. y = d[i++]; // Not draw too small seg between
  586. if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len - 1) {
  587. ctx.lineTo(x, y);
  588. xi = x;
  589. yi = y;
  590. }
  591. break;
  592. case CMD.C:
  593. ctx.bezierCurveTo(d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]);
  594. xi = d[i - 2];
  595. yi = d[i - 1];
  596. break;
  597. case CMD.Q:
  598. ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);
  599. xi = d[i - 2];
  600. yi = d[i - 1];
  601. break;
  602. case CMD.A:
  603. var cx = d[i++];
  604. var cy = d[i++];
  605. var rx = d[i++];
  606. var ry = d[i++];
  607. var theta = d[i++];
  608. var dTheta = d[i++];
  609. var psi = d[i++];
  610. var fs = d[i++];
  611. var r = rx > ry ? rx : ry;
  612. var scaleX = rx > ry ? 1 : rx / ry;
  613. var scaleY = rx > ry ? ry / rx : 1;
  614. var isEllipse = Math.abs(rx - ry) > 1e-3;
  615. var endAngle = theta + dTheta;
  616. if (isEllipse) {
  617. ctx.translate(cx, cy);
  618. ctx.rotate(psi);
  619. ctx.scale(scaleX, scaleY);
  620. ctx.arc(0, 0, r, theta, endAngle, 1 - fs);
  621. ctx.scale(1 / scaleX, 1 / scaleY);
  622. ctx.rotate(-psi);
  623. ctx.translate(-cx, -cy);
  624. } else {
  625. ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);
  626. }
  627. if (i === 1) {
  628. // 直接使用 arc 命令
  629. // 第一个命令起点还未定义
  630. x0 = mathCos(theta) * rx + cx;
  631. y0 = mathSin(theta) * ry + cy;
  632. }
  633. xi = mathCos(endAngle) * rx + cx;
  634. yi = mathSin(endAngle) * ry + cy;
  635. break;
  636. case CMD.R:
  637. x0 = xi = d[i];
  638. y0 = yi = d[i + 1];
  639. ctx.rect(d[i++], d[i++], d[i++], d[i++]);
  640. break;
  641. case CMD.Z:
  642. ctx.closePath();
  643. xi = x0;
  644. yi = y0;
  645. }
  646. }
  647. }
  648. };
  649. PathProxy.CMD = CMD;
  650. var _default = PathProxy;
  651. module.exports = _default;