CVShapeElement.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. import {
  2. degToRads,
  3. bmFloor,
  4. } from '../../utils/common';
  5. import {
  6. extendPrototype,
  7. } from '../../utils/functionExtensions';
  8. import PropertyFactory from '../../utils/PropertyFactory';
  9. import RenderableElement from '../helpers/RenderableElement';
  10. import BaseElement from '../BaseElement';
  11. import TransformElement from '../helpers/TransformElement';
  12. import HierarchyElement from '../helpers/HierarchyElement';
  13. import FrameElement from '../helpers/FrameElement';
  14. import RenderableDOMElement from '../helpers/RenderableDOMElement';
  15. import ShapeTransformManager from '../helpers/shapes/ShapeTransformManager';
  16. import CVBaseElement from './CVBaseElement';
  17. import IShapeElement from '../ShapeElement';
  18. import GradientProperty from '../../utils/shapes/GradientProperty';
  19. import DashProperty from '../../utils/shapes/DashProperty';
  20. import TransformPropertyFactory from '../../utils/TransformProperty';
  21. import CVShapeData from '../helpers/shapes/CVShapeData';
  22. import { ShapeModifiers } from '../../utils/shapes/ShapeModifiers';
  23. import {
  24. lineCapEnum,
  25. lineJoinEnum,
  26. } from '../../utils/helpers/shapeEnums';
  27. function CVShapeElement(data, globalData, comp) {
  28. this.shapes = [];
  29. this.shapesData = data.shapes;
  30. this.stylesList = [];
  31. this.itemsData = [];
  32. this.prevViewData = [];
  33. this.shapeModifiers = [];
  34. this.processedElements = [];
  35. this.transformsManager = new ShapeTransformManager();
  36. this.initElement(data, globalData, comp);
  37. }
  38. extendPrototype([BaseElement, TransformElement, CVBaseElement, IShapeElement, HierarchyElement, FrameElement, RenderableElement], CVShapeElement);
  39. CVShapeElement.prototype.initElement = RenderableDOMElement.prototype.initElement;
  40. CVShapeElement.prototype.transformHelper = { opacity: 1, _opMdf: false };
  41. CVShapeElement.prototype.dashResetter = [];
  42. CVShapeElement.prototype.createContent = function () {
  43. this.searchShapes(this.shapesData, this.itemsData, this.prevViewData, true, []);
  44. };
  45. CVShapeElement.prototype.createStyleElement = function (data, transforms) {
  46. var styleElem = {
  47. data: data,
  48. type: data.ty,
  49. preTransforms: this.transformsManager.addTransformSequence(transforms),
  50. transforms: [],
  51. elements: [],
  52. closed: data.hd === true,
  53. };
  54. var elementData = {};
  55. if (data.ty === 'fl' || data.ty === 'st') {
  56. elementData.c = PropertyFactory.getProp(this, data.c, 1, 255, this);
  57. if (!elementData.c.k) {
  58. styleElem.co = 'rgb(' + bmFloor(elementData.c.v[0]) + ',' + bmFloor(elementData.c.v[1]) + ',' + bmFloor(elementData.c.v[2]) + ')';
  59. }
  60. } else if (data.ty === 'gf' || data.ty === 'gs') {
  61. elementData.s = PropertyFactory.getProp(this, data.s, 1, null, this);
  62. elementData.e = PropertyFactory.getProp(this, data.e, 1, null, this);
  63. elementData.h = PropertyFactory.getProp(this, data.h || { k: 0 }, 0, 0.01, this);
  64. elementData.a = PropertyFactory.getProp(this, data.a || { k: 0 }, 0, degToRads, this);
  65. elementData.g = new GradientProperty(this, data.g, this);
  66. }
  67. elementData.o = PropertyFactory.getProp(this, data.o, 0, 0.01, this);
  68. if (data.ty === 'st' || data.ty === 'gs') {
  69. styleElem.lc = lineCapEnum[data.lc || 2];
  70. styleElem.lj = lineJoinEnum[data.lj || 2];
  71. if (data.lj == 1) { // eslint-disable-line eqeqeq
  72. styleElem.ml = data.ml;
  73. }
  74. elementData.w = PropertyFactory.getProp(this, data.w, 0, null, this);
  75. if (!elementData.w.k) {
  76. styleElem.wi = elementData.w.v;
  77. }
  78. if (data.d) {
  79. var d = new DashProperty(this, data.d, 'canvas', this);
  80. elementData.d = d;
  81. if (!elementData.d.k) {
  82. styleElem.da = elementData.d.dashArray;
  83. styleElem.do = elementData.d.dashoffset[0];
  84. }
  85. }
  86. } else {
  87. styleElem.r = data.r === 2 ? 'evenodd' : 'nonzero';
  88. }
  89. this.stylesList.push(styleElem);
  90. elementData.style = styleElem;
  91. return elementData;
  92. };
  93. CVShapeElement.prototype.createGroupElement = function () {
  94. var elementData = {
  95. it: [],
  96. prevViewData: [],
  97. };
  98. return elementData;
  99. };
  100. CVShapeElement.prototype.createTransformElement = function (data) {
  101. var elementData = {
  102. transform: {
  103. opacity: 1,
  104. _opMdf: false,
  105. key: this.transformsManager.getNewKey(),
  106. op: PropertyFactory.getProp(this, data.o, 0, 0.01, this),
  107. mProps: TransformPropertyFactory.getTransformProperty(this, data, this),
  108. },
  109. };
  110. return elementData;
  111. };
  112. CVShapeElement.prototype.createShapeElement = function (data) {
  113. var elementData = new CVShapeData(this, data, this.stylesList, this.transformsManager);
  114. this.shapes.push(elementData);
  115. this.addShapeToModifiers(elementData);
  116. return elementData;
  117. };
  118. CVShapeElement.prototype.reloadShapes = function () {
  119. this._isFirstFrame = true;
  120. var i;
  121. var len = this.itemsData.length;
  122. for (i = 0; i < len; i += 1) {
  123. this.prevViewData[i] = this.itemsData[i];
  124. }
  125. this.searchShapes(this.shapesData, this.itemsData, this.prevViewData, true, []);
  126. len = this.dynamicProperties.length;
  127. for (i = 0; i < len; i += 1) {
  128. this.dynamicProperties[i].getValue();
  129. }
  130. this.renderModifiers();
  131. this.transformsManager.processSequences(this._isFirstFrame);
  132. };
  133. CVShapeElement.prototype.addTransformToStyleList = function (transform) {
  134. var i;
  135. var len = this.stylesList.length;
  136. for (i = 0; i < len; i += 1) {
  137. if (!this.stylesList[i].closed) {
  138. this.stylesList[i].transforms.push(transform);
  139. }
  140. }
  141. };
  142. CVShapeElement.prototype.removeTransformFromStyleList = function () {
  143. var i;
  144. var len = this.stylesList.length;
  145. for (i = 0; i < len; i += 1) {
  146. if (!this.stylesList[i].closed) {
  147. this.stylesList[i].transforms.pop();
  148. }
  149. }
  150. };
  151. CVShapeElement.prototype.closeStyles = function (styles) {
  152. var i;
  153. var len = styles.length;
  154. for (i = 0; i < len; i += 1) {
  155. styles[i].closed = true;
  156. }
  157. };
  158. CVShapeElement.prototype.searchShapes = function (arr, itemsData, prevViewData, shouldRender, transforms) {
  159. var i;
  160. var len = arr.length - 1;
  161. var j;
  162. var jLen;
  163. var ownStyles = [];
  164. var ownModifiers = [];
  165. var processedPos;
  166. var modifier;
  167. var currentTransform;
  168. var ownTransforms = [].concat(transforms);
  169. for (i = len; i >= 0; i -= 1) {
  170. processedPos = this.searchProcessedElement(arr[i]);
  171. if (!processedPos) {
  172. arr[i]._shouldRender = shouldRender;
  173. } else {
  174. itemsData[i] = prevViewData[processedPos - 1];
  175. }
  176. if (arr[i].ty === 'fl' || arr[i].ty === 'st' || arr[i].ty === 'gf' || arr[i].ty === 'gs') {
  177. if (!processedPos) {
  178. itemsData[i] = this.createStyleElement(arr[i], ownTransforms);
  179. } else {
  180. itemsData[i].style.closed = false;
  181. }
  182. ownStyles.push(itemsData[i].style);
  183. } else if (arr[i].ty === 'gr') {
  184. if (!processedPos) {
  185. itemsData[i] = this.createGroupElement(arr[i]);
  186. } else {
  187. jLen = itemsData[i].it.length;
  188. for (j = 0; j < jLen; j += 1) {
  189. itemsData[i].prevViewData[j] = itemsData[i].it[j];
  190. }
  191. }
  192. this.searchShapes(arr[i].it, itemsData[i].it, itemsData[i].prevViewData, shouldRender, ownTransforms);
  193. } else if (arr[i].ty === 'tr') {
  194. if (!processedPos) {
  195. currentTransform = this.createTransformElement(arr[i]);
  196. itemsData[i] = currentTransform;
  197. }
  198. ownTransforms.push(itemsData[i]);
  199. this.addTransformToStyleList(itemsData[i]);
  200. } else if (arr[i].ty === 'sh' || arr[i].ty === 'rc' || arr[i].ty === 'el' || arr[i].ty === 'sr') {
  201. if (!processedPos) {
  202. itemsData[i] = this.createShapeElement(arr[i]);
  203. }
  204. } else if (arr[i].ty === 'tm' || arr[i].ty === 'rd' || arr[i].ty === 'pb' || arr[i].ty === 'zz' || arr[i].ty === 'op') {
  205. if (!processedPos) {
  206. modifier = ShapeModifiers.getModifier(arr[i].ty);
  207. modifier.init(this, arr[i]);
  208. itemsData[i] = modifier;
  209. this.shapeModifiers.push(modifier);
  210. } else {
  211. modifier = itemsData[i];
  212. modifier.closed = false;
  213. }
  214. ownModifiers.push(modifier);
  215. } else if (arr[i].ty === 'rp') {
  216. if (!processedPos) {
  217. modifier = ShapeModifiers.getModifier(arr[i].ty);
  218. itemsData[i] = modifier;
  219. modifier.init(this, arr, i, itemsData);
  220. this.shapeModifiers.push(modifier);
  221. shouldRender = false;
  222. } else {
  223. modifier = itemsData[i];
  224. modifier.closed = true;
  225. }
  226. ownModifiers.push(modifier);
  227. }
  228. this.addProcessedElement(arr[i], i + 1);
  229. }
  230. this.removeTransformFromStyleList();
  231. this.closeStyles(ownStyles);
  232. len = ownModifiers.length;
  233. for (i = 0; i < len; i += 1) {
  234. ownModifiers[i].closed = true;
  235. }
  236. };
  237. CVShapeElement.prototype.renderInnerContent = function () {
  238. this.transformHelper.opacity = 1;
  239. this.transformHelper._opMdf = false;
  240. this.renderModifiers();
  241. this.transformsManager.processSequences(this._isFirstFrame);
  242. this.renderShape(this.transformHelper, this.shapesData, this.itemsData, true);
  243. };
  244. CVShapeElement.prototype.renderShapeTransform = function (parentTransform, groupTransform) {
  245. if (parentTransform._opMdf || groupTransform.op._mdf || this._isFirstFrame) {
  246. groupTransform.opacity = parentTransform.opacity;
  247. groupTransform.opacity *= groupTransform.op.v;
  248. groupTransform._opMdf = true;
  249. }
  250. };
  251. CVShapeElement.prototype.drawLayer = function () {
  252. var i;
  253. var len = this.stylesList.length;
  254. var j;
  255. var jLen;
  256. var k;
  257. var kLen;
  258. var elems;
  259. var nodes;
  260. var renderer = this.globalData.renderer;
  261. var ctx = this.globalData.canvasContext;
  262. var type;
  263. var currentStyle;
  264. for (i = 0; i < len; i += 1) {
  265. currentStyle = this.stylesList[i];
  266. type = currentStyle.type;
  267. // Skipping style when
  268. // Stroke width equals 0
  269. // style should not be rendered (extra unused repeaters)
  270. // current opacity equals 0
  271. // global opacity equals 0
  272. if (!(((type === 'st' || type === 'gs') && currentStyle.wi === 0) || !currentStyle.data._shouldRender || currentStyle.coOp === 0 || this.globalData.currentGlobalAlpha === 0)) {
  273. renderer.save();
  274. elems = currentStyle.elements;
  275. if (type === 'st' || type === 'gs') {
  276. renderer.ctxStrokeStyle(type === 'st' ? currentStyle.co : currentStyle.grd);
  277. // ctx.strokeStyle = type === 'st' ? currentStyle.co : currentStyle.grd;
  278. renderer.ctxLineWidth(currentStyle.wi);
  279. // ctx.lineWidth = currentStyle.wi;
  280. renderer.ctxLineCap(currentStyle.lc);
  281. // ctx.lineCap = currentStyle.lc;
  282. renderer.ctxLineJoin(currentStyle.lj);
  283. // ctx.lineJoin = currentStyle.lj;
  284. renderer.ctxMiterLimit(currentStyle.ml || 0);
  285. // ctx.miterLimit = currentStyle.ml || 0;
  286. } else {
  287. renderer.ctxFillStyle(type === 'fl' ? currentStyle.co : currentStyle.grd);
  288. // ctx.fillStyle = type === 'fl' ? currentStyle.co : currentStyle.grd;
  289. }
  290. renderer.ctxOpacity(currentStyle.coOp);
  291. if (type !== 'st' && type !== 'gs') {
  292. ctx.beginPath();
  293. }
  294. renderer.ctxTransform(currentStyle.preTransforms.finalTransform.props);
  295. jLen = elems.length;
  296. for (j = 0; j < jLen; j += 1) {
  297. if (type === 'st' || type === 'gs') {
  298. ctx.beginPath();
  299. if (currentStyle.da) {
  300. ctx.setLineDash(currentStyle.da);
  301. ctx.lineDashOffset = currentStyle.do;
  302. }
  303. }
  304. nodes = elems[j].trNodes;
  305. kLen = nodes.length;
  306. for (k = 0; k < kLen; k += 1) {
  307. if (nodes[k].t === 'm') {
  308. ctx.moveTo(nodes[k].p[0], nodes[k].p[1]);
  309. } else if (nodes[k].t === 'c') {
  310. ctx.bezierCurveTo(nodes[k].pts[0], nodes[k].pts[1], nodes[k].pts[2], nodes[k].pts[3], nodes[k].pts[4], nodes[k].pts[5]);
  311. } else {
  312. ctx.closePath();
  313. }
  314. }
  315. if (type === 'st' || type === 'gs') {
  316. // ctx.stroke();
  317. renderer.ctxStroke();
  318. if (currentStyle.da) {
  319. ctx.setLineDash(this.dashResetter);
  320. }
  321. }
  322. }
  323. if (type !== 'st' && type !== 'gs') {
  324. // ctx.fill(currentStyle.r);
  325. this.globalData.renderer.ctxFill(currentStyle.r);
  326. }
  327. renderer.restore();
  328. }
  329. }
  330. };
  331. CVShapeElement.prototype.renderShape = function (parentTransform, items, data, isMain) {
  332. var i;
  333. var len = items.length - 1;
  334. var groupTransform;
  335. groupTransform = parentTransform;
  336. for (i = len; i >= 0; i -= 1) {
  337. if (items[i].ty === 'tr') {
  338. groupTransform = data[i].transform;
  339. this.renderShapeTransform(parentTransform, groupTransform);
  340. } else if (items[i].ty === 'sh' || items[i].ty === 'el' || items[i].ty === 'rc' || items[i].ty === 'sr') {
  341. this.renderPath(items[i], data[i]);
  342. } else if (items[i].ty === 'fl') {
  343. this.renderFill(items[i], data[i], groupTransform);
  344. } else if (items[i].ty === 'st') {
  345. this.renderStroke(items[i], data[i], groupTransform);
  346. } else if (items[i].ty === 'gf' || items[i].ty === 'gs') {
  347. this.renderGradientFill(items[i], data[i], groupTransform);
  348. } else if (items[i].ty === 'gr') {
  349. this.renderShape(groupTransform, items[i].it, data[i].it);
  350. } else if (items[i].ty === 'tm') {
  351. //
  352. }
  353. }
  354. if (isMain) {
  355. this.drawLayer();
  356. }
  357. };
  358. CVShapeElement.prototype.renderStyledShape = function (styledShape, shape) {
  359. if (this._isFirstFrame || shape._mdf || styledShape.transforms._mdf) {
  360. var shapeNodes = styledShape.trNodes;
  361. var paths = shape.paths;
  362. var i;
  363. var len;
  364. var j;
  365. var jLen = paths._length;
  366. shapeNodes.length = 0;
  367. var groupTransformMat = styledShape.transforms.finalTransform;
  368. for (j = 0; j < jLen; j += 1) {
  369. var pathNodes = paths.shapes[j];
  370. if (pathNodes && pathNodes.v) {
  371. len = pathNodes._length;
  372. for (i = 1; i < len; i += 1) {
  373. if (i === 1) {
  374. shapeNodes.push({
  375. t: 'm',
  376. p: groupTransformMat.applyToPointArray(pathNodes.v[0][0], pathNodes.v[0][1], 0),
  377. });
  378. }
  379. shapeNodes.push({
  380. t: 'c',
  381. pts: groupTransformMat.applyToTriplePoints(pathNodes.o[i - 1], pathNodes.i[i], pathNodes.v[i]),
  382. });
  383. }
  384. if (len === 1) {
  385. shapeNodes.push({
  386. t: 'm',
  387. p: groupTransformMat.applyToPointArray(pathNodes.v[0][0], pathNodes.v[0][1], 0),
  388. });
  389. }
  390. if (pathNodes.c && len) {
  391. shapeNodes.push({
  392. t: 'c',
  393. pts: groupTransformMat.applyToTriplePoints(pathNodes.o[i - 1], pathNodes.i[0], pathNodes.v[0]),
  394. });
  395. shapeNodes.push({
  396. t: 'z',
  397. });
  398. }
  399. }
  400. }
  401. styledShape.trNodes = shapeNodes;
  402. }
  403. };
  404. CVShapeElement.prototype.renderPath = function (pathData, itemData) {
  405. if (pathData.hd !== true && pathData._shouldRender) {
  406. var i;
  407. var len = itemData.styledShapes.length;
  408. for (i = 0; i < len; i += 1) {
  409. this.renderStyledShape(itemData.styledShapes[i], itemData.sh);
  410. }
  411. }
  412. };
  413. CVShapeElement.prototype.renderFill = function (styleData, itemData, groupTransform) {
  414. var styleElem = itemData.style;
  415. if (itemData.c._mdf || this._isFirstFrame) {
  416. styleElem.co = 'rgb('
  417. + bmFloor(itemData.c.v[0]) + ','
  418. + bmFloor(itemData.c.v[1]) + ','
  419. + bmFloor(itemData.c.v[2]) + ')';
  420. }
  421. if (itemData.o._mdf || groupTransform._opMdf || this._isFirstFrame) {
  422. styleElem.coOp = itemData.o.v * groupTransform.opacity;
  423. }
  424. };
  425. CVShapeElement.prototype.renderGradientFill = function (styleData, itemData, groupTransform) {
  426. var styleElem = itemData.style;
  427. var grd;
  428. if (!styleElem.grd || itemData.g._mdf || itemData.s._mdf || itemData.e._mdf || (styleData.t !== 1 && (itemData.h._mdf || itemData.a._mdf))) {
  429. var ctx = this.globalData.canvasContext;
  430. var pt1 = itemData.s.v;
  431. var pt2 = itemData.e.v;
  432. if (styleData.t === 1) {
  433. grd = ctx.createLinearGradient(pt1[0], pt1[1], pt2[0], pt2[1]);
  434. } else {
  435. var rad = Math.sqrt(Math.pow(pt1[0] - pt2[0], 2) + Math.pow(pt1[1] - pt2[1], 2));
  436. var ang = Math.atan2(pt2[1] - pt1[1], pt2[0] - pt1[0]);
  437. var percent = itemData.h.v;
  438. if (percent >= 1) {
  439. percent = 0.99;
  440. } else if (percent <= -1) {
  441. percent = -0.99;
  442. }
  443. var dist = rad * percent;
  444. var x = Math.cos(ang + itemData.a.v) * dist + pt1[0];
  445. var y = Math.sin(ang + itemData.a.v) * dist + pt1[1];
  446. grd = ctx.createRadialGradient(x, y, 0, pt1[0], pt1[1], rad);
  447. }
  448. var i;
  449. var len = styleData.g.p;
  450. var cValues = itemData.g.c;
  451. var opacity = 1;
  452. for (i = 0; i < len; i += 1) {
  453. if (itemData.g._hasOpacity && itemData.g._collapsable) {
  454. opacity = itemData.g.o[i * 2 + 1];
  455. }
  456. grd.addColorStop(cValues[i * 4] / 100, 'rgba(' + cValues[i * 4 + 1] + ',' + cValues[i * 4 + 2] + ',' + cValues[i * 4 + 3] + ',' + opacity + ')');
  457. }
  458. styleElem.grd = grd;
  459. }
  460. styleElem.coOp = itemData.o.v * groupTransform.opacity;
  461. };
  462. CVShapeElement.prototype.renderStroke = function (styleData, itemData, groupTransform) {
  463. var styleElem = itemData.style;
  464. var d = itemData.d;
  465. if (d && (d._mdf || this._isFirstFrame)) {
  466. styleElem.da = d.dashArray;
  467. styleElem.do = d.dashoffset[0];
  468. }
  469. if (itemData.c._mdf || this._isFirstFrame) {
  470. styleElem.co = 'rgb(' + bmFloor(itemData.c.v[0]) + ',' + bmFloor(itemData.c.v[1]) + ',' + bmFloor(itemData.c.v[2]) + ')';
  471. }
  472. if (itemData.o._mdf || groupTransform._opMdf || this._isFirstFrame) {
  473. styleElem.coOp = itemData.o.v * groupTransform.opacity;
  474. }
  475. if (itemData.w._mdf || this._isFirstFrame) {
  476. styleElem.wi = itemData.w.v;
  477. }
  478. };
  479. CVShapeElement.prototype.destroy = function () {
  480. this.shapesData = null;
  481. this.globalData = null;
  482. this.canvasContext = null;
  483. this.stylesList.length = 0;
  484. this.itemsData.length = 0;
  485. };
  486. export default CVShapeElement;