mask.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import { getLocationHref } from './main';
  2. import {
  3. createElementID,
  4. } from './utils/common';
  5. import {
  6. createSizedArray,
  7. } from './utils/helpers/arrays';
  8. import PropertyFactory from './utils/PropertyFactory';
  9. import ShapePropertyFactory from './utils/shapes/ShapeProperty';
  10. import createNS from './utils/helpers/svg_elements';
  11. function MaskElement(data, element, globalData) {
  12. this.data = data;
  13. this.element = element;
  14. this.globalData = globalData;
  15. this.storedData = [];
  16. this.masksProperties = this.data.masksProperties || [];
  17. this.maskElement = null;
  18. var defs = this.globalData.defs;
  19. var i;
  20. var len = this.masksProperties ? this.masksProperties.length : 0;
  21. this.viewData = createSizedArray(len);
  22. this.solidPath = '';
  23. var path;
  24. var properties = this.masksProperties;
  25. var count = 0;
  26. var currentMasks = [];
  27. var j;
  28. var jLen;
  29. var layerId = createElementID();
  30. var rect;
  31. var expansor;
  32. var feMorph;
  33. var x;
  34. var maskType = 'clipPath';
  35. var maskRef = 'clip-path';
  36. for (i = 0; i < len; i += 1) {
  37. if ((properties[i].mode !== 'a' && properties[i].mode !== 'n') || properties[i].inv || properties[i].o.k !== 100 || properties[i].o.x) {
  38. maskType = 'mask';
  39. maskRef = 'mask';
  40. }
  41. if ((properties[i].mode === 's' || properties[i].mode === 'i') && count === 0) {
  42. rect = createNS('rect');
  43. rect.setAttribute('fill', '#ffffff');
  44. rect.setAttribute('width', this.element.comp.data.w || 0);
  45. rect.setAttribute('height', this.element.comp.data.h || 0);
  46. currentMasks.push(rect);
  47. } else {
  48. rect = null;
  49. }
  50. path = createNS('path');
  51. if (properties[i].mode === 'n') {
  52. // TODO move this to a factory or to a constructor
  53. this.viewData[i] = {
  54. op: PropertyFactory.getProp(this.element, properties[i].o, 0, 0.01, this.element),
  55. prop: ShapePropertyFactory.getShapeProp(this.element, properties[i], 3),
  56. elem: path,
  57. lastPath: '',
  58. };
  59. defs.appendChild(path);
  60. } else {
  61. count += 1;
  62. path.setAttribute('fill', properties[i].mode === 's' ? '#000000' : '#ffffff');
  63. path.setAttribute('clip-rule', 'nonzero');
  64. var filterID;
  65. if (properties[i].x.k !== 0) {
  66. maskType = 'mask';
  67. maskRef = 'mask';
  68. x = PropertyFactory.getProp(this.element, properties[i].x, 0, null, this.element);
  69. filterID = createElementID();
  70. expansor = createNS('filter');
  71. expansor.setAttribute('id', filterID);
  72. feMorph = createNS('feMorphology');
  73. feMorph.setAttribute('operator', 'erode');
  74. feMorph.setAttribute('in', 'SourceGraphic');
  75. feMorph.setAttribute('radius', '0');
  76. expansor.appendChild(feMorph);
  77. defs.appendChild(expansor);
  78. path.setAttribute('stroke', properties[i].mode === 's' ? '#000000' : '#ffffff');
  79. } else {
  80. feMorph = null;
  81. x = null;
  82. }
  83. // TODO move this to a factory or to a constructor
  84. this.storedData[i] = {
  85. elem: path,
  86. x: x,
  87. expan: feMorph,
  88. lastPath: '',
  89. lastOperator: '',
  90. filterId: filterID,
  91. lastRadius: 0,
  92. };
  93. if (properties[i].mode === 'i') {
  94. jLen = currentMasks.length;
  95. var g = createNS('g');
  96. for (j = 0; j < jLen; j += 1) {
  97. g.appendChild(currentMasks[j]);
  98. }
  99. var mask = createNS('mask');
  100. mask.setAttribute('mask-type', 'alpha');
  101. mask.setAttribute('id', layerId + '_' + count);
  102. mask.appendChild(path);
  103. defs.appendChild(mask);
  104. g.setAttribute('mask', 'url(' + getLocationHref() + '#' + layerId + '_' + count + ')');
  105. currentMasks.length = 0;
  106. currentMasks.push(g);
  107. } else {
  108. currentMasks.push(path);
  109. }
  110. if (properties[i].inv && !this.solidPath) {
  111. this.solidPath = this.createLayerSolidPath();
  112. }
  113. // TODO move this to a factory or to a constructor
  114. this.viewData[i] = {
  115. elem: path,
  116. lastPath: '',
  117. op: PropertyFactory.getProp(this.element, properties[i].o, 0, 0.01, this.element),
  118. prop: ShapePropertyFactory.getShapeProp(this.element, properties[i], 3),
  119. invRect: rect,
  120. };
  121. if (!this.viewData[i].prop.k) {
  122. this.drawPath(properties[i], this.viewData[i].prop.v, this.viewData[i]);
  123. }
  124. }
  125. }
  126. this.maskElement = createNS(maskType);
  127. len = currentMasks.length;
  128. for (i = 0; i < len; i += 1) {
  129. this.maskElement.appendChild(currentMasks[i]);
  130. }
  131. if (count > 0) {
  132. this.maskElement.setAttribute('id', layerId);
  133. this.element.maskedElement.setAttribute(maskRef, 'url(' + getLocationHref() + '#' + layerId + ')');
  134. defs.appendChild(this.maskElement);
  135. }
  136. if (this.viewData.length) {
  137. this.element.addRenderableComponent(this);
  138. }
  139. }
  140. MaskElement.prototype.getMaskProperty = function (pos) {
  141. return this.viewData[pos].prop;
  142. };
  143. MaskElement.prototype.renderFrame = function (isFirstFrame) {
  144. var finalMat = this.element.finalTransform.mat;
  145. var i;
  146. var len = this.masksProperties.length;
  147. for (i = 0; i < len; i += 1) {
  148. if (this.viewData[i].prop._mdf || isFirstFrame) {
  149. this.drawPath(this.masksProperties[i], this.viewData[i].prop.v, this.viewData[i]);
  150. }
  151. if (this.viewData[i].op._mdf || isFirstFrame) {
  152. this.viewData[i].elem.setAttribute('fill-opacity', this.viewData[i].op.v);
  153. }
  154. if (this.masksProperties[i].mode !== 'n') {
  155. if (this.viewData[i].invRect && (this.element.finalTransform.mProp._mdf || isFirstFrame)) {
  156. this.viewData[i].invRect.setAttribute('transform', finalMat.getInverseMatrix().to2dCSS());
  157. }
  158. if (this.storedData[i].x && (this.storedData[i].x._mdf || isFirstFrame)) {
  159. var feMorph = this.storedData[i].expan;
  160. if (this.storedData[i].x.v < 0) {
  161. if (this.storedData[i].lastOperator !== 'erode') {
  162. this.storedData[i].lastOperator = 'erode';
  163. this.storedData[i].elem.setAttribute('filter', 'url(' + getLocationHref() + '#' + this.storedData[i].filterId + ')');
  164. }
  165. feMorph.setAttribute('radius', -this.storedData[i].x.v);
  166. } else {
  167. if (this.storedData[i].lastOperator !== 'dilate') {
  168. this.storedData[i].lastOperator = 'dilate';
  169. this.storedData[i].elem.setAttribute('filter', null);
  170. }
  171. this.storedData[i].elem.setAttribute('stroke-width', this.storedData[i].x.v * 2);
  172. }
  173. }
  174. }
  175. }
  176. };
  177. MaskElement.prototype.getMaskelement = function () {
  178. return this.maskElement;
  179. };
  180. MaskElement.prototype.createLayerSolidPath = function () {
  181. var path = 'M0,0 ';
  182. path += ' h' + this.globalData.compSize.w;
  183. path += ' v' + this.globalData.compSize.h;
  184. path += ' h-' + this.globalData.compSize.w;
  185. path += ' v-' + this.globalData.compSize.h + ' ';
  186. return path;
  187. };
  188. MaskElement.prototype.drawPath = function (pathData, pathNodes, viewData) {
  189. var pathString = ' M' + pathNodes.v[0][0] + ',' + pathNodes.v[0][1];
  190. var i;
  191. var len;
  192. len = pathNodes._length;
  193. for (i = 1; i < len; i += 1) {
  194. // pathString += " C"+pathNodes.o[i-1][0]+','+pathNodes.o[i-1][1] + " "+pathNodes.i[i][0]+','+pathNodes.i[i][1] + " "+pathNodes.v[i][0]+','+pathNodes.v[i][1];
  195. pathString += ' C' + pathNodes.o[i - 1][0] + ',' + pathNodes.o[i - 1][1] + ' ' + pathNodes.i[i][0] + ',' + pathNodes.i[i][1] + ' ' + pathNodes.v[i][0] + ',' + pathNodes.v[i][1];
  196. }
  197. // pathString += " C"+pathNodes.o[i-1][0]+','+pathNodes.o[i-1][1] + " "+pathNodes.i[0][0]+','+pathNodes.i[0][1] + " "+pathNodes.v[0][0]+','+pathNodes.v[0][1];
  198. if (pathNodes.c && len > 1) {
  199. pathString += ' C' + pathNodes.o[i - 1][0] + ',' + pathNodes.o[i - 1][1] + ' ' + pathNodes.i[0][0] + ',' + pathNodes.i[0][1] + ' ' + pathNodes.v[0][0] + ',' + pathNodes.v[0][1];
  200. }
  201. // pathNodes.__renderedString = pathString;
  202. if (viewData.lastPath !== pathString) {
  203. var pathShapeValue = '';
  204. if (viewData.elem) {
  205. if (pathNodes.c) {
  206. pathShapeValue = pathData.inv ? this.solidPath + pathString : pathString;
  207. }
  208. viewData.elem.setAttribute('d', pathShapeValue);
  209. }
  210. viewData.lastPath = pathString;
  211. }
  212. };
  213. MaskElement.prototype.destroy = function () {
  214. this.element = null;
  215. this.globalData = null;
  216. this.maskElement = null;
  217. this.data = null;
  218. this.masksProperties = null;
  219. };
  220. export default MaskElement;