PathProxy.js 18 KB

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