HShapeElement.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. import {
  2. bmPow,
  3. bmMax,
  4. bmMin,
  5. bmSqrt,
  6. } from '../../utils/common';
  7. import {
  8. extendPrototype,
  9. } from '../../utils/functionExtensions';
  10. import createNS from '../../utils/helpers/svg_elements';
  11. import RenderableElement from '../helpers/RenderableElement';
  12. import BaseElement from '../BaseElement';
  13. import TransformElement from '../helpers/TransformElement';
  14. import HierarchyElement from '../helpers/HierarchyElement';
  15. import FrameElement from '../helpers/FrameElement';
  16. import HBaseElement from './HBaseElement';
  17. import HSolidElement from './HSolidElement';
  18. import SVGShapeElement from '../svgElements/SVGShapeElement';
  19. function HShapeElement(data, globalData, comp) {
  20. // List of drawable elements
  21. this.shapes = [];
  22. // Full shape data
  23. this.shapesData = data.shapes;
  24. // List of styles that will be applied to shapes
  25. this.stylesList = [];
  26. // List of modifiers that will be applied to shapes
  27. this.shapeModifiers = [];
  28. // List of items in shape tree
  29. this.itemsData = [];
  30. // List of items in previous shape tree
  31. this.processedElements = [];
  32. // List of animated components
  33. this.animatedContents = [];
  34. this.shapesContainer = createNS('g');
  35. this.initElement(data, globalData, comp);
  36. // Moving any property that doesn't get too much access after initialization because of v8 way of handling more than 10 properties.
  37. // List of elements that have been created
  38. this.prevViewData = [];
  39. this.currentBBox = {
  40. x: 999999,
  41. y: -999999,
  42. h: 0,
  43. w: 0,
  44. };
  45. }
  46. extendPrototype([BaseElement, TransformElement, HSolidElement, SVGShapeElement, HBaseElement, HierarchyElement, FrameElement, RenderableElement], HShapeElement);
  47. HShapeElement.prototype._renderShapeFrame = HShapeElement.prototype.renderInnerContent;
  48. HShapeElement.prototype.createContent = function () {
  49. var cont;
  50. this.baseElement.style.fontSize = 0;
  51. if (this.data.hasMask) {
  52. this.layerElement.appendChild(this.shapesContainer);
  53. cont = this.svgElement;
  54. } else {
  55. cont = createNS('svg');
  56. var size = this.comp.data ? this.comp.data : this.globalData.compSize;
  57. cont.setAttribute('width', size.w);
  58. cont.setAttribute('height', size.h);
  59. cont.appendChild(this.shapesContainer);
  60. this.layerElement.appendChild(cont);
  61. }
  62. this.searchShapes(this.shapesData, this.itemsData, this.prevViewData, this.shapesContainer, 0, [], true);
  63. this.filterUniqueShapes();
  64. this.shapeCont = cont;
  65. };
  66. HShapeElement.prototype.getTransformedPoint = function (transformers, point) {
  67. var i;
  68. var len = transformers.length;
  69. for (i = 0; i < len; i += 1) {
  70. point = transformers[i].mProps.v.applyToPointArray(point[0], point[1], 0);
  71. }
  72. return point;
  73. };
  74. HShapeElement.prototype.calculateShapeBoundingBox = function (item, boundingBox) {
  75. var shape = item.sh.v;
  76. var transformers = item.transformers;
  77. var i;
  78. var len = shape._length;
  79. var vPoint;
  80. var oPoint;
  81. var nextIPoint;
  82. var nextVPoint;
  83. if (len <= 1) {
  84. return;
  85. }
  86. for (i = 0; i < len - 1; i += 1) {
  87. vPoint = this.getTransformedPoint(transformers, shape.v[i]);
  88. oPoint = this.getTransformedPoint(transformers, shape.o[i]);
  89. nextIPoint = this.getTransformedPoint(transformers, shape.i[i + 1]);
  90. nextVPoint = this.getTransformedPoint(transformers, shape.v[i + 1]);
  91. this.checkBounds(vPoint, oPoint, nextIPoint, nextVPoint, boundingBox);
  92. }
  93. if (shape.c) {
  94. vPoint = this.getTransformedPoint(transformers, shape.v[i]);
  95. oPoint = this.getTransformedPoint(transformers, shape.o[i]);
  96. nextIPoint = this.getTransformedPoint(transformers, shape.i[0]);
  97. nextVPoint = this.getTransformedPoint(transformers, shape.v[0]);
  98. this.checkBounds(vPoint, oPoint, nextIPoint, nextVPoint, boundingBox);
  99. }
  100. };
  101. HShapeElement.prototype.checkBounds = function (vPoint, oPoint, nextIPoint, nextVPoint, boundingBox) {
  102. this.getBoundsOfCurve(vPoint, oPoint, nextIPoint, nextVPoint);
  103. var bounds = this.shapeBoundingBox;
  104. boundingBox.x = bmMin(bounds.left, boundingBox.x);
  105. boundingBox.xMax = bmMax(bounds.right, boundingBox.xMax);
  106. boundingBox.y = bmMin(bounds.top, boundingBox.y);
  107. boundingBox.yMax = bmMax(bounds.bottom, boundingBox.yMax);
  108. };
  109. HShapeElement.prototype.shapeBoundingBox = {
  110. left: 0,
  111. right: 0,
  112. top: 0,
  113. bottom: 0,
  114. };
  115. HShapeElement.prototype.tempBoundingBox = {
  116. x: 0,
  117. xMax: 0,
  118. y: 0,
  119. yMax: 0,
  120. width: 0,
  121. height: 0,
  122. };
  123. HShapeElement.prototype.getBoundsOfCurve = function (p0, p1, p2, p3) {
  124. var bounds = [[p0[0], p3[0]], [p0[1], p3[1]]];
  125. for (var a, b, c, t, b2ac, t1, t2, i = 0; i < 2; ++i) { // eslint-disable-line no-plusplus
  126. b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
  127. a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
  128. c = 3 * p1[i] - 3 * p0[i];
  129. b |= 0; // eslint-disable-line no-bitwise
  130. a |= 0; // eslint-disable-line no-bitwise
  131. c |= 0; // eslint-disable-line no-bitwise
  132. if (a === 0 && b === 0) {
  133. //
  134. } else if (a === 0) {
  135. t = -c / b;
  136. if (t > 0 && t < 1) {
  137. bounds[i].push(this.calculateF(t, p0, p1, p2, p3, i));
  138. }
  139. } else {
  140. b2ac = b * b - 4 * c * a;
  141. if (b2ac >= 0) {
  142. t1 = (-b + bmSqrt(b2ac)) / (2 * a);
  143. if (t1 > 0 && t1 < 1) bounds[i].push(this.calculateF(t1, p0, p1, p2, p3, i));
  144. t2 = (-b - bmSqrt(b2ac)) / (2 * a);
  145. if (t2 > 0 && t2 < 1) bounds[i].push(this.calculateF(t2, p0, p1, p2, p3, i));
  146. }
  147. }
  148. }
  149. this.shapeBoundingBox.left = bmMin.apply(null, bounds[0]);
  150. this.shapeBoundingBox.top = bmMin.apply(null, bounds[1]);
  151. this.shapeBoundingBox.right = bmMax.apply(null, bounds[0]);
  152. this.shapeBoundingBox.bottom = bmMax.apply(null, bounds[1]);
  153. };
  154. HShapeElement.prototype.calculateF = function (t, p0, p1, p2, p3, i) {
  155. return bmPow(1 - t, 3) * p0[i]
  156. + 3 * bmPow(1 - t, 2) * t * p1[i]
  157. + 3 * (1 - t) * bmPow(t, 2) * p2[i]
  158. + bmPow(t, 3) * p3[i];
  159. };
  160. HShapeElement.prototype.calculateBoundingBox = function (itemsData, boundingBox) {
  161. var i;
  162. var len = itemsData.length;
  163. for (i = 0; i < len; i += 1) {
  164. if (itemsData[i] && itemsData[i].sh) {
  165. this.calculateShapeBoundingBox(itemsData[i], boundingBox);
  166. } else if (itemsData[i] && itemsData[i].it) {
  167. this.calculateBoundingBox(itemsData[i].it, boundingBox);
  168. } else if (itemsData[i] && itemsData[i].style && itemsData[i].w) {
  169. this.expandStrokeBoundingBox(itemsData[i].w, boundingBox);
  170. }
  171. }
  172. };
  173. HShapeElement.prototype.expandStrokeBoundingBox = function (widthProperty, boundingBox) {
  174. var width = 0;
  175. if (widthProperty.keyframes) {
  176. for (var i = 0; i < widthProperty.keyframes.length; i += 1) {
  177. var kfw = widthProperty.keyframes[i].s;
  178. if (kfw > width) {
  179. width = kfw;
  180. }
  181. }
  182. width *= widthProperty.mult;
  183. } else {
  184. width = widthProperty.v * widthProperty.mult;
  185. }
  186. boundingBox.x -= width;
  187. boundingBox.xMax += width;
  188. boundingBox.y -= width;
  189. boundingBox.yMax += width;
  190. };
  191. HShapeElement.prototype.currentBoxContains = function (box) {
  192. return this.currentBBox.x <= box.x
  193. && this.currentBBox.y <= box.y
  194. && this.currentBBox.width + this.currentBBox.x >= box.x + box.width
  195. && this.currentBBox.height + this.currentBBox.y >= box.y + box.height;
  196. };
  197. HShapeElement.prototype.renderInnerContent = function () {
  198. this._renderShapeFrame();
  199. if (!this.hidden && (this._isFirstFrame || this._mdf)) {
  200. var tempBoundingBox = this.tempBoundingBox;
  201. var max = 999999;
  202. tempBoundingBox.x = max;
  203. tempBoundingBox.xMax = -max;
  204. tempBoundingBox.y = max;
  205. tempBoundingBox.yMax = -max;
  206. this.calculateBoundingBox(this.itemsData, tempBoundingBox);
  207. tempBoundingBox.width = tempBoundingBox.xMax < tempBoundingBox.x ? 0 : tempBoundingBox.xMax - tempBoundingBox.x;
  208. tempBoundingBox.height = tempBoundingBox.yMax < tempBoundingBox.y ? 0 : tempBoundingBox.yMax - tempBoundingBox.y;
  209. // var tempBoundingBox = this.shapeCont.getBBox();
  210. if (this.currentBoxContains(tempBoundingBox)) {
  211. return;
  212. }
  213. var changed = false;
  214. if (this.currentBBox.w !== tempBoundingBox.width) {
  215. this.currentBBox.w = tempBoundingBox.width;
  216. this.shapeCont.setAttribute('width', tempBoundingBox.width);
  217. changed = true;
  218. }
  219. if (this.currentBBox.h !== tempBoundingBox.height) {
  220. this.currentBBox.h = tempBoundingBox.height;
  221. this.shapeCont.setAttribute('height', tempBoundingBox.height);
  222. changed = true;
  223. }
  224. if (changed || this.currentBBox.x !== tempBoundingBox.x || this.currentBBox.y !== tempBoundingBox.y) {
  225. this.currentBBox.w = tempBoundingBox.width;
  226. this.currentBBox.h = tempBoundingBox.height;
  227. this.currentBBox.x = tempBoundingBox.x;
  228. this.currentBBox.y = tempBoundingBox.y;
  229. this.shapeCont.setAttribute('viewBox', this.currentBBox.x + ' ' + this.currentBBox.y + ' ' + this.currentBBox.w + ' ' + this.currentBBox.h);
  230. var shapeStyle = this.shapeCont.style;
  231. var shapeTransform = 'translate(' + this.currentBBox.x + 'px,' + this.currentBBox.y + 'px)';
  232. shapeStyle.transform = shapeTransform;
  233. shapeStyle.webkitTransform = shapeTransform;
  234. }
  235. }
  236. };
  237. export default HShapeElement;