svgpath-umd

A UMD build of svgpath

目前为 2025-01-27 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/525014/1527476/svgpath-umd.js

  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.SvgPath = factory());
  5. })(this, (() => {
  6. const epsilon = 0.0000000001;
  7. const torad = Math.PI / 180;
  8. class Ellipse {
  9. constructor(rx, ry, ax) {
  10. if (!(this instanceof Ellipse)) return new Ellipse(rx, ry, ax);
  11. this.rx = rx;
  12. this.ry = ry;
  13. this.ax = ax;
  14. }
  15. transform(m) {
  16. const c = Math.cos(this.ax * torad);
  17. const s = Math.sin(this.ax * torad);
  18. const ma = [
  19. this.rx * (m[0] * c + m[2] * s),
  20. this.rx * (m[1] * c + m[3] * s),
  21. this.ry * (-m[0] * s + m[2] * c),
  22. this.ry * (-m[1] * s + m[3] * c)
  23. ];
  24. const J = ma[0] * ma[0] + ma[2] * ma[2];
  25. const K = ma[1] * ma[1] + ma[3] * ma[3];
  26. let D = (
  27. (ma[0] - ma[3]) *
  28. (ma[0] - ma[3]) +
  29. (ma[2] + ma[1]) *
  30. (ma[2] + ma[1])
  31. ) * (
  32. (ma[0] + ma[3]) *
  33. (ma[0] + ma[3]) +
  34. (ma[2] - ma[1]) *
  35. (ma[2] - ma[1])
  36. );
  37. const JK = (J + K) / 2;
  38. if (D < epsilon * JK) {
  39. this.rx = this.ry = Math.sqrt(JK);
  40. this.ax = 0;
  41. return this;
  42. }
  43. const L = ma[0] * ma[1] + ma[2] * ma[3];
  44. D = Math.sqrt(D);
  45. const l1 = JK + D / 2;
  46. const l2 = JK - D / 2;
  47. this.ax = (Math.abs(L) < epsilon && Math.abs(l1 - K) < epsilon)
  48. ? 90
  49. : Math.atan(
  50. Math.abs(L) > Math.abs(l1 - K)
  51. ? (l1 - J) / L
  52. : L / (l1 - K)
  53. ) * 180 / Math.PI;
  54. if (this.ax >= 0) {
  55. this.rx = Math.sqrt(l1);
  56. this.ry = Math.sqrt(l2);
  57. } else {
  58. this.ax += 90;
  59. this.rx = Math.sqrt(l2);
  60. this.ry = Math.sqrt(l1);
  61. }
  62. return this;
  63. }
  64. isDegenerate() {
  65. return (this.rx < epsilon * this.ry || this.ry < epsilon * this.rx);
  66. }
  67. }
  68. const TAU = Math.PI * 2;
  69. const unit_vector_angle = (ux, uy, vx, vy) => {
  70. const sign = (ux * vy - uy * vx < 0) ? -1 : 1;
  71. let dot = ux * vx + uy * vy;
  72. if (dot > 1.0) { dot = 1.0; }
  73. if (dot < -1) { dot = -1; }
  74. return sign * Math.acos(dot);
  75. };
  76. const get_arc_center = (x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi) => {
  77. const x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
  78. const y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
  79. const rx_sq = rx * rx;
  80. const ry_sq = ry * ry;
  81. const x1p_sq = x1p * x1p;
  82. const y1p_sq = y1p * y1p;
  83. let radicant = (rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq);
  84. if (radicant < 0) {
  85. radicant = 0;
  86. }
  87. radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq);
  88. radicant = Math.sqrt(radicant) * (fa === fs ? -1 : 1);
  89. const cxp = radicant * rx/ry * y1p;
  90. const cyp = radicant * -ry/rx * x1p;
  91. const cx = cos_phi*cxp - sin_phi*cyp + (x1+x2)/2;
  92. const cy = sin_phi*cxp + cos_phi*cyp + (y1+y2)/2;
  93. const v1x = (x1p - cxp) / rx;
  94. const v1y = (y1p - cyp) / ry;
  95. const v2x = (-x1p - cxp) / rx;
  96. const v2y = (-y1p - cyp) / ry;
  97. const theta1 = unit_vector_angle(1, 0, v1x, v1y);
  98. let delta_theta = unit_vector_angle(v1x, v1y, v2x, v2y);
  99. if (fs === 0 && delta_theta > 0) {
  100. delta_theta -= TAU;
  101. }
  102. if (fs === 1 && delta_theta < 0) {
  103. delta_theta += TAU;
  104. }
  105. return [ cx, cy, theta1, delta_theta ];
  106. };
  107. const approximate_unit_arc = (theta1, delta_theta) => {
  108. const alpha = 4/3 * Math.tan(delta_theta/4);
  109. const x1 = Math.cos(theta1);
  110. const y1 = Math.sin(theta1);
  111. const x2 = Math.cos(theta1 + delta_theta);
  112. const y2 = Math.sin(theta1 + delta_theta);
  113. return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ];
  114. };
  115. const a2c = (x1, y1, x2, y2, fa, fs, rx, ry, phi) => {
  116. const sin_phi = Math.sin(phi * TAU / 360);
  117. const cos_phi = Math.cos(phi * TAU / 360);
  118. const x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
  119. const y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
  120. if (x1p === 0 && y1p === 0) return [];
  121. if (rx === 0 || ry === 0) return [];
  122. rx = Math.abs(rx);
  123. ry = Math.abs(ry);
  124. const lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
  125. if (lambda > 1) {
  126. rx *= Math.sqrt(lambda);
  127. ry *= Math.sqrt(lambda);
  128. }
  129. const cc = get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi);
  130. const result = [];
  131. let theta1 = cc[2];
  132. let delta_theta = cc[3];
  133. const segments = Math.max(Math.ceil(Math.abs(delta_theta) / (TAU / 4)), 1);
  134. delta_theta /= segments;
  135. for (let i = 0; i < segments; i++) {
  136. result.push(approximate_unit_arc(theta1, delta_theta));
  137. theta1 += delta_theta;
  138. }
  139. return result.map(curve => {
  140. for (let i = 0; i < curve.length; i += 2) {
  141. let x = curve[i + 0];
  142. let y = curve[i + 1];
  143. x *= rx;
  144. y *= ry;
  145. const xp = cos_phi*x - sin_phi*y;
  146. const yp = sin_phi*x + cos_phi*y;
  147. curve[i + 0] = xp + cc[0];
  148. curve[i + 1] = yp + cc[1];
  149. }
  150. return curve;
  151. });
  152. };
  153. const combine = (m1, m2) => {
  154. return [
  155. m1[0] * m2[0] + m1[2] * m2[1],
  156. m1[1] * m2[0] + m1[3] * m2[1],
  157. m1[0] * m2[2] + m1[2] * m2[3],
  158. m1[1] * m2[2] + m1[3] * m2[3],
  159. m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
  160. m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
  161. ];
  162. };
  163. class Matrix {
  164. constructor() {
  165. if (!(this instanceof Matrix)) return new Matrix();
  166. this.queue = [];
  167. this.cache = null;
  168. }
  169. matrix(m) {
  170. if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0) return this;
  171. this.cache = null;
  172. this.queue.push(m);
  173. return this;
  174. }
  175. translate(tx, ty) {
  176. if (tx !== 0 || ty !== 0) {
  177. this.cache = null;
  178. this.queue.push([1, 0, 0, 1, tx, ty]);
  179. }
  180. return this;
  181. }
  182. scale(sx, sy) {
  183. if (sx !== 1 || sy !== 1) {
  184. this.cache = null;
  185. this.queue.push([sx, 0, 0, sy, 0, 0]);
  186. }
  187. return this;
  188. }
  189. rotate(angle, rx, ry) {
  190. if (angle !== 0) {
  191. this.translate(rx, ry);
  192. const rad = angle * Math.PI / 180;
  193. const cos = Math.cos(rad);
  194. const sin = Math.sin(rad);
  195. this.queue.push([cos, sin, -sin, cos, 0, 0]);
  196. this.cache = null;
  197. this.translate(-rx, -ry);
  198. }
  199. return this;
  200. }
  201. skewX(angle) {
  202. if (angle !== 0) {
  203. this.cache = null;
  204. this.queue.push([1, 0, Math.tan(angle * Math.PI / 180), 1, 0, 0]);
  205. }
  206. return this;
  207. }
  208. skewY(angle) {
  209. if (angle !== 0) {
  210. this.cache = null;
  211. this.queue.push([1, Math.tan(angle * Math.PI / 180), 0, 1, 0, 0]);
  212. }
  213. return this;
  214. }
  215. toArray() {
  216. if (this.cache) return this.cache;
  217. if (!this.queue.length) {
  218. this.cache = [1, 0, 0, 1, 0, 0];
  219. return this.cache;
  220. }
  221. this.cache = this.queue[0];
  222. if (this.queue.length === 1) {
  223. return this.cache;
  224. }
  225. for (let i = 1; i < this.queue.length; i++) {
  226. this.cache = combine(this.cache, this.queue[i]);
  227. }
  228. return this.cache;
  229. }
  230. calc(x, y, isRelative) {
  231. if (!this.queue.length) return [x, y];
  232. if (!this.cache) {
  233. this.cache = this.toArray();
  234. }
  235. const m = this.cache;
  236. return [
  237. x * m[0] + y * m[2] + (isRelative ? 0 : m[4]),
  238. x * m[1] + y * m[3] + (isRelative ? 0 : m[5])
  239. ];
  240. }
  241. }
  242. const paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };
  243. const SPECIAL_SPACES = [
  244. 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
  245. 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF
  246. ];
  247. const isSpace = ch => (
  248. (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) ||
  249. (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
  250. (ch >= 0x1680 && SPECIAL_SPACES.indexOf(ch) >= 0)
  251. );
  252. const isCommand = code => {
  253. switch (code | 0x20) {
  254. case 0x6D:
  255. case 0x7A:
  256. case 0x6C:
  257. case 0x68:
  258. case 0x76:
  259. case 0x63:
  260. case 0x73:
  261. case 0x71:
  262. case 0x74:
  263. case 0x61:
  264. case 0x72:
  265. return true;
  266. }
  267. return false;
  268. };
  269. const isArc = code => (code | 0x20) === 0x61;
  270. const isDigit = code => (code >= 48 && code <= 57);
  271. const isDigitStart = code => (
  272. (code >= 48 && code <= 57) ||
  273. code === 0x2B ||
  274. code === 0x2D ||
  275. code === 0x2E
  276. );
  277. class State {
  278. constructor(path) {
  279. this.index = 0;
  280. this.path = path;
  281. this.max = path.length;
  282. this.result = [];
  283. this.param = 0.0;
  284. this.err = '';
  285. this.segmentStart = 0;
  286. this.data = [];
  287. }
  288. }
  289. const skipSpaces = state => {
  290. while (state.index < state.max && isSpace(state.path.charCodeAt(state.index))) state.index++;
  291. };
  292. const scanFlag = state => {
  293. const ch = state.path.charCodeAt(state.index);
  294. if (ch === 0x30) {
  295. state.param = 0;
  296. state.index++;
  297. return;
  298. }
  299. if (ch === 0x31) {
  300. state.param = 1;
  301. state.index++;
  302. return;
  303. }
  304. state.err = `SvgPath: arc flag can be 0 or 1 only (at pos ${ state.index })`;
  305. };
  306. const scanParam = state => {
  307. const start = state.index;
  308. let index = start;
  309. const max = state.max;
  310. let zeroFirst = false;
  311. let hasCeiling = false;
  312. let hasDecimal = false;
  313. let hasDot = false;
  314. let ch;
  315. if (index >= max) {
  316. state.err = `SvgPath: missed param (at pos ${ index })`;
  317. return;
  318. }
  319. ch = state.path.charCodeAt(index);
  320. if (ch === 0x2B || ch === 0x2D) {
  321. index++;
  322. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  323. }
  324. if (!isDigit(ch) && ch !== 0x2E) {
  325. state.err = `SvgPath: param should start with 0..9 or \`.\` (at pos ${ index })`;
  326. return;
  327. }
  328. if (ch !== 0x2E) {
  329. zeroFirst = (ch === 0x30);
  330. index++;
  331. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  332. if (zeroFirst && index < max) {
  333. if (ch && isDigit(ch)) {
  334. state.err = `SvgPath: numbers started with \`0\` such as \`09\` are illegal (at pos ${ start })`;
  335. return;
  336. }
  337. }
  338. while (index < max && isDigit(state.path.charCodeAt(index))) {
  339. index++;
  340. hasCeiling = true;
  341. }
  342. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  343. }
  344. if (ch === 0x2E) {
  345. hasDot = true;
  346. index++;
  347. while (isDigit(state.path.charCodeAt(index))) {
  348. index++;
  349. hasDecimal = true;
  350. }
  351. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  352. }
  353. if (ch === 0x65 || ch === 0x45) {
  354. if (hasDot && !hasCeiling && !hasDecimal) {
  355. state.err = `SvgPath: invalid float exponent (at pos ${ index })`;
  356. return;
  357. }
  358. index++;
  359. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  360. if (ch === 0x2B || ch === 0x2D) index++;
  361. if (index < max && isDigit(state.path.charCodeAt(index))) {
  362. while (index < max && isDigit(state.path.charCodeAt(index))) index++;
  363. } else {
  364. state.err = `SvgPath: invalid float exponent (at pos ${ index })`;
  365. return;
  366. }
  367. }
  368. state.index = index;
  369. state.param = parseFloat(state.path.slice(start, index)) + 0.0;
  370. };
  371. const finalizeSegment = state => {
  372. let cmd = state.path[state.segmentStart];
  373. let cmdLC = cmd.toLowerCase();
  374. let params = state.data;
  375. if (cmdLC === 'm' && params.length > 2) {
  376. state.result.push([ cmd, params[0], params[1] ]);
  377. params = params.slice(2);
  378. cmdLC = 'l';
  379. cmd = (cmd === 'm') ? 'l' : 'L';
  380. }
  381. if (cmdLC === 'r') state.result.push([ cmd ].concat(params));
  382. else {
  383. while (params.length >= paramCounts[cmdLC]) {
  384. state.result.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC])));
  385. if (!paramCounts[cmdLC]) break;
  386. }
  387. }
  388. };
  389. const scanSegment = state => {
  390. const max = state.max;
  391. state.segmentStart = state.index;
  392. const cmdCode = state.path.charCodeAt(state.index);
  393. const is_arc = isArc(cmdCode);
  394. if (!isCommand(cmdCode)) {
  395. state.err = `SvgPath: bad command ${ state.path[state.index] } (at pos ${ state.index })`;
  396. return;
  397. }
  398. const need_params = paramCounts[state.path[state.index].toLowerCase()];
  399. state.index++;
  400. skipSpaces(state);
  401. state.data = [];
  402. if (!need_params) {
  403. finalizeSegment(state);
  404. return;
  405. }
  406. let comma_found = false;
  407. for (;;) {
  408. for (let i = need_params; i > 0; i--) {
  409. if (is_arc && (i === 3 || i === 4)) scanFlag(state);
  410. else scanParam(state);
  411. if (state.err.length) {
  412. finalizeSegment(state);
  413. return;
  414. }
  415. state.data.push(state.param);
  416. skipSpaces(state);
  417. comma_found = false;
  418. if (state.index < max && state.path.charCodeAt(state.index) === 0x2C) {
  419. state.index++;
  420. skipSpaces(state);
  421. comma_found = true;
  422. }
  423. }
  424. if (comma_found) continue;
  425. if (state.index >= state.max) break;
  426. if (!isDigitStart(state.path.charCodeAt(state.index))) break;
  427. }
  428. finalizeSegment(state);
  429. };
  430. const pathParse = svgPath => {
  431. const state = new State(svgPath);
  432. const max = state.max;
  433. skipSpaces(state);
  434. while (state.index < max && !state.err.length) scanSegment(state);
  435. if (state.result.length) {
  436. if ('mM'.indexOf(state.result[0][0]) < 0) {
  437. state.err = 'SvgPath: string should start with `M` or `m`';
  438. state.result = [];
  439. } else {
  440. state.result[0][0] = 'M';
  441. }
  442. }
  443. return {
  444. err: state.err,
  445. segments: state.result
  446. };
  447. };
  448. const operations = {
  449. matrix: true,
  450. scale: true,
  451. rotate: true,
  452. translate: true,
  453. skewX: true,
  454. skewY: true
  455. };
  456. const CMD_SPLIT_RE = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/;
  457. const PARAMS_SPLIT_RE = /[\s,]+/;
  458. const transformParse = transformString => {
  459. const matrix = new Matrix();
  460. let cmd;
  461. let params;
  462. transformString.split(CMD_SPLIT_RE).forEach(item => {
  463. if (!item.length) return;
  464. if (typeof operations[item] !== 'undefined') {
  465. cmd = item;
  466. return;
  467. }
  468. params = item.split(PARAMS_SPLIT_RE).map(i => +i || 0);
  469. switch (cmd) {
  470. case 'matrix':
  471. if (params.length === 6) matrix.matrix(params);
  472. return;
  473. case 'scale':
  474. if (params.length === 1) matrix.scale(params[0], params[0]);
  475. else if (params.length === 2) matrix.scale(params[0], params[1]);
  476. return;
  477. case 'rotate':
  478. if (params.length === 1) matrix.rotate(params[0], 0, 0);
  479. else if (params.length === 3) matrix.rotate(params[0], params[1], params[2]);
  480. return;
  481. case 'translate':
  482. if (params.length === 1) matrix.translate(params[0], 0);
  483. else if (params.length === 2) matrix.translate(params[0], params[1]);
  484. return;
  485. case 'skewX':
  486. if (params.length === 1) matrix.skewX(params[0]);
  487. return;
  488. case 'skewY':
  489. if (params.length === 1) matrix.skewY(params[0]);
  490. return;
  491. }
  492. });
  493. return matrix;
  494. };
  495. class SvgPath {
  496. constructor(path) {
  497. if (!(this instanceof SvgPath)) return new SvgPath(path);
  498. const pstate = pathParse(path);
  499. this.segments = pstate.segments;
  500. this.err = pstate.err;
  501. this.__stack = [];
  502. }
  503. static from(src) {
  504. if (typeof src === 'string') return new SvgPath(src);
  505. if (src instanceof SvgPath) {
  506. const s = new SvgPath('');
  507. s.err = src.err;
  508. s.segments = src.segments.map(sgm => sgm.slice());
  509. s.__stack = src.__stack.map(m => new Matrix().matrix(m.toArray()));
  510. return s;
  511. }
  512. throw new Error(`SvgPath.from: invalid param type ${ src}`);
  513. }
  514. __matrix(m) {
  515. const self = this;
  516. if (!m.queue.length) return;
  517. this.iterate((s, index, x, y) => {
  518. let p;
  519. let result;
  520. let name;
  521. let isRelative;
  522. switch (s[0]) {
  523. case 'v':
  524. p = m.calc(0, s[1], true);
  525. result = (p[0] === 0) ? ['v', p[1]] : ['l', p[0], p[1]];
  526. break;
  527. case 'V':
  528. p = m.calc(x, s[1], false);
  529. result = (p[0] === m.calc(x, y, false)[0]) ? ['V', p[1]] : ['L', p[0], p[1]];
  530. break;
  531. case 'h':
  532. p = m.calc(s[1], 0, true);
  533. result = (p[1] === 0) ? ['h', p[0]] : ['l', p[0], p[1]];
  534. break;
  535. case 'H':
  536. p = m.calc(s[1], y, false);
  537. result = (p[1] === m.calc(x, y, false)[1]) ? ['H', p[0]] : ['L', p[0], p[1]];
  538. break;
  539. case 'a':
  540. case 'A': {
  541. /*if ((s[0] === 'A' && s[6] === x && s[7] === y) ||
  542. (s[0] === 'a' && s[6] === 0 && s[7] === 0)) {
  543. return [];
  544. }*/
  545. const ma = m.toArray();
  546. const e = new Ellipse(s[1], s[2], s[3]).transform(ma);
  547. if (ma[0] * ma[3] - ma[1] * ma[2] < 0) {
  548. s[5] = s[5] ? '0' : '1';
  549. }
  550. p = m.calc(s[6], s[7], s[0] === 'a');
  551. if (
  552. (s[0] === 'A' && s[6] === x && s[7] === y) ||
  553. (s[0] === 'a' && s[6] === 0 && s[7] === 0)
  554. ) {
  555. result = [s[0] === 'a' ? 'l' : 'L', p[0], p[1]];
  556. break;
  557. }
  558. result = e.isDegenerate() ? [s[0] === 'a' ? 'l' : 'L', p[0], p[1]] : [s[0], e.rx, e.ry, e.ax, s[4], s[5], p[0], p[1]];
  559. break;
  560. }
  561. case 'm':
  562. isRelative = index > 0;
  563. p = m.calc(s[1], s[2], isRelative);
  564. result = ['m', p[0], p[1]];
  565. break;
  566. default:
  567. name = s[0];
  568. result = [name];
  569. isRelative = (name.toLowerCase() === name);
  570. for (let i = 1; i < s.length; i += 2) {
  571. p = m.calc(s[i], s[i + 1], isRelative);
  572. result.push(p[0], p[1]);
  573. }
  574. }
  575. self.segments[index] = result;
  576. }, true);
  577. }
  578. __evaluateStack() {
  579. if (!this.__stack.length) return;
  580. if (this.__stack.length === 1) {
  581. this.__matrix(this.__stack[0]);
  582. this.__stack = [];
  583. return;
  584. }
  585. const m = new Matrix();
  586. let i = this.__stack.length;
  587. while (--i >= 0) m.matrix(this.__stack[i].toArray());
  588. this.__matrix(m);
  589. this.__stack = [];
  590. }
  591. toString() {
  592. let result = '';
  593. let prevCmd = '';
  594. let cmdSkipped = false;
  595. this.__evaluateStack();
  596. for (let i = 0, len = this.segments.length; i < len; i++) {
  597. const segment = this.segments[i];
  598. const cmd = segment[0];
  599. if (cmd !== prevCmd || cmd === 'm' || cmd === 'M') {
  600. if (cmd === 'm' && prevCmd === 'z') result += ' ';
  601. result += cmd;
  602. cmdSkipped = false;
  603. } else {
  604. cmdSkipped = true;
  605. }
  606. for (let pos = 1; pos < segment.length; pos++) {
  607. const val = segment[pos];
  608. if (pos === 1) {
  609. if (cmdSkipped && val >= 0) result += ' ';
  610. } else if (val >= 0) result += ' ';
  611. result += val;
  612. }
  613. prevCmd = cmd;
  614. }
  615. return result;
  616. }
  617. translate(x, y) {
  618. this.__stack.push(new Matrix().translate(x, y || 0));
  619. return this;
  620. }
  621. scale(sx, sy) {
  622. this.__stack.push(new Matrix().scale(sx, (!sy && (sy !== 0)) ? sx : sy));
  623. return this;
  624. }
  625. rotate(angle, rx, ry) {
  626. this.__stack.push(new Matrix().rotate(angle, rx || 0, ry || 0));
  627. return this;
  628. }
  629. skewX(degrees) {
  630. this.__stack.push(new Matrix().skewX(degrees));
  631. return this;
  632. }
  633. skewY(degrees) {
  634. this.__stack.push(new Matrix().skewY(degrees));
  635. return this;
  636. }
  637. matrix(m) {
  638. this.__stack.push(new Matrix().matrix(m));
  639. return this;
  640. }
  641. transform(transformString) {
  642. if (!transformString.trim()) return this;
  643. this.__stack.push(transformParse(transformString));
  644. return this;
  645. }
  646. round(d) {
  647. let contourStartDeltaX = 0;
  648. let contourStartDeltaY = 0;
  649. let deltaX = 0;
  650. let deltaY = 0;
  651. let l;
  652. d = d || 0;
  653. this.__evaluateStack();
  654. this.segments.forEach(s => {
  655. const isRelative = (s[0].toLowerCase() === s[0]);
  656. switch (s[0]) {
  657. case 'H':
  658. case 'h':
  659. if (isRelative) { s[1] += deltaX; }
  660. deltaX = s[1] - s[1].toFixed(d);
  661. s[1] = +s[1].toFixed(d);
  662. return;
  663. case 'V':
  664. case 'v':
  665. if (isRelative) { s[1] += deltaY; }
  666. deltaY = s[1] - s[1].toFixed(d);
  667. s[1] = +s[1].toFixed(d);
  668. return;
  669. case 'Z':
  670. case 'z':
  671. deltaX = contourStartDeltaX;
  672. deltaY = contourStartDeltaY;
  673. return;
  674. case 'M':
  675. case 'm':
  676. if (isRelative) {
  677. s[1] += deltaX;
  678. s[2] += deltaY;
  679. }
  680. deltaX = s[1] - s[1].toFixed(d);
  681. deltaY = s[2] - s[2].toFixed(d);
  682. contourStartDeltaX = deltaX;
  683. contourStartDeltaY = deltaY;
  684. s[1] = +s[1].toFixed(d);
  685. s[2] = +s[2].toFixed(d);
  686. return;
  687. case 'A':
  688. case 'a':
  689. if (isRelative) {
  690. s[6] += deltaX;
  691. s[7] += deltaY;
  692. }
  693. deltaX = s[6] - s[6].toFixed(d);
  694. deltaY = s[7] - s[7].toFixed(d);
  695. s[1] = +s[1].toFixed(d);
  696. s[2] = +s[2].toFixed(d);
  697. s[3] = +s[3].toFixed(d + 2);
  698. s[6] = +s[6].toFixed(d);
  699. s[7] = +s[7].toFixed(d);
  700. return;
  701. default:
  702. l = s.length;
  703. if (isRelative) {
  704. s[l - 2] += deltaX;
  705. s[l - 1] += deltaY;
  706. }
  707. deltaX = s[l - 2] - s[l - 2].toFixed(d);
  708. deltaY = s[l - 1] - s[l - 1].toFixed(d);
  709. s.forEach((val, i) => {
  710. if (!i) return;
  711. s[i] = +s[i].toFixed(d);
  712. });
  713. return;
  714. }
  715. });
  716. return this;
  717. }
  718. iterate(iterator, keepLazyStack) {
  719. const segments = this.segments;
  720. const replacements = {};
  721. let needReplace = false;
  722. let lastX = 0;
  723. let lastY = 0;
  724. let countourStartX = 0;
  725. let countourStartY = 0;
  726. if (!keepLazyStack) this.__evaluateStack();
  727. segments.forEach((s, index) => {
  728. const res = iterator(s, index, lastX, lastY);
  729. if (Array.isArray(res)) {
  730. replacements[index] = res;
  731. needReplace = true;
  732. }
  733. const isRelative = (s[0] === s[0].toLowerCase());
  734. switch (s[0]) {
  735. case 'm':
  736. case 'M':
  737. lastX = s[1] + (isRelative ? lastX : 0);
  738. lastY = s[2] + (isRelative ? lastY : 0);
  739. countourStartX = lastX;
  740. countourStartY = lastY;
  741. return;
  742. case 'h':
  743. case 'H':
  744. lastX = s[1] + (isRelative ? lastX : 0);
  745. return;
  746. case 'v':
  747. case 'V':
  748. lastY = s[1] + (isRelative ? lastY : 0);
  749. return;
  750. case 'z':
  751. case 'Z':
  752. lastX = countourStartX;
  753. lastY = countourStartY;
  754. return;
  755. default:
  756. lastX = s[s.length - 2] + (isRelative ? lastX : 0);
  757. lastY = s[s.length - 1] + (isRelative ? lastY : 0);
  758. }
  759. });
  760. if (!needReplace) return this;
  761. const newSegments = [];
  762. for (let i = 0; i < segments.length; i++) {
  763. if (typeof replacements[i] !== 'undefined') {
  764. for (let j = 0; j < replacements[i].length; j++) {
  765. newSegments.push(replacements[i][j]);
  766. }
  767. } else {
  768. newSegments.push(segments[i]);
  769. }
  770. }
  771. this.segments = newSegments;
  772. return this;
  773. }
  774. abs() {
  775. this.iterate((s, index, x, y) => {
  776. const name = s[0];
  777. const nameUC = name.toUpperCase();
  778. if (name === nameUC) return;
  779. s[0] = nameUC;
  780. switch (name) {
  781. case 'v':
  782. s[1] += y;
  783. return;
  784. case 'a':
  785. s[6] += x;
  786. s[7] += y;
  787. return;
  788. default:
  789. for (let i = 1; i < s.length; i++) {
  790. s[i] += i % 2 ? x : y;
  791. }
  792. }
  793. }, true);
  794. return this;
  795. }
  796. rel() {
  797. this.iterate((s, index, x, y) => {
  798. const name = s[0];
  799. const nameLC = name.toLowerCase();
  800. if (name === nameLC) return;
  801. if (index === 0 && name === 'M') return;
  802. s[0] = nameLC;
  803. switch (name) {
  804. case 'V':
  805. s[1] -= y;
  806. return;
  807. case 'A':
  808. s[6] -= x;
  809. s[7] -= y;
  810. return;
  811. default:
  812. for (let i = 1; i < s.length; i++) {
  813. s[i] -= i % 2 ? x : y;
  814. }
  815. }
  816. }, true);
  817. return this;
  818. }
  819. unarc() {
  820. this.iterate((s, index, x, y) => {
  821. let nextX;
  822. let nextY;
  823. const result = [];
  824. const name = s[0];
  825. if (name !== 'A' && name !== 'a') return null;
  826. if (name === 'a') {
  827. nextX = x + s[6];
  828. nextY = y + s[7];
  829. } else {
  830. nextX = s[6];
  831. nextY = s[7];
  832. }
  833. const new_segments = a2c(x, y, nextX, nextY, s[4], s[5], s[1], s[2], s[3]);
  834. if (new_segments.length === 0) return [[s[0] === 'a' ? 'l' : 'L', s[6], s[7]]];
  835. new_segments.forEach(s => result.push(['C', s[2], s[3], s[4], s[5], s[6], s[7]]));
  836. return result;
  837. });
  838. return this;
  839. }
  840. unshort() {
  841. const segments = this.segments;
  842. let prevControlX;
  843. let prevControlY;
  844. let prevSegment;
  845. let curControlX;
  846. let curControlY;
  847. this.iterate((s, idx, x, y) => {
  848. const name = s[0];
  849. const nameUC = name.toUpperCase();
  850. let isRelative;
  851. if (!idx) return;
  852. if (nameUC === 'T') {
  853. isRelative = (name === 't');
  854. prevSegment = segments[idx - 1];
  855. if (prevSegment[0] === 'Q') {
  856. prevControlX = prevSegment[1] - x;
  857. prevControlY = prevSegment[2] - y;
  858. } else if (prevSegment[0] === 'q') {
  859. prevControlX = prevSegment[1] - prevSegment[3];
  860. prevControlY = prevSegment[2] - prevSegment[4];
  861. } else {
  862. prevControlX = 0;
  863. prevControlY = 0;
  864. }
  865. curControlX = -prevControlX;
  866. curControlY = -prevControlY;
  867. if (!isRelative) {
  868. curControlX += x;
  869. curControlY += y;
  870. }
  871. segments[idx] = [
  872. isRelative ? 'q' : 'Q',
  873. curControlX, curControlY,
  874. s[1], s[2]
  875. ];
  876. } else if (nameUC === 'S') {
  877. isRelative = (name === 's');
  878. prevSegment = segments[idx - 1];
  879. if (prevSegment[0] === 'C') {
  880. prevControlX = prevSegment[3] - x;
  881. prevControlY = prevSegment[4] - y;
  882. } else if (prevSegment[0] === 'c') {
  883. prevControlX = prevSegment[3] - prevSegment[5];
  884. prevControlY = prevSegment[4] - prevSegment[6];
  885. } else {
  886. prevControlX = 0;
  887. prevControlY = 0;
  888. }
  889. curControlX = -prevControlX;
  890. curControlY = -prevControlY;
  891. if (!isRelative) {
  892. curControlX += x;
  893. curControlY += y;
  894. }
  895. segments[idx] = [
  896. isRelative ? 'c' : 'C',
  897. curControlX, curControlY,
  898. s[1], s[2], s[3], s[4]
  899. ];
  900. }
  901. });
  902. return this;
  903. }
  904. }
  905. return SvgPath;
  906. }));

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址