ClippathManager.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. var Definable = require("./Definable");
  2. var zrUtil = require("../../core/util");
  3. var matrix = require("../../core/matrix");
  4. /**
  5. * @file Manages SVG clipPath elements.
  6. * @author Zhang Wenli
  7. */
  8. /**
  9. * Manages SVG clipPath elements.
  10. *
  11. * @class
  12. * @extends Definable
  13. * @param {SVGElement} svgRoot root of SVG document
  14. */
  15. function ClippathManager(svgRoot) {
  16. Definable.call(this, svgRoot, 'clipPath', '__clippath_in_use__');
  17. }
  18. zrUtil.inherits(ClippathManager, Definable);
  19. /**
  20. * Update clipPath.
  21. *
  22. * @param {Displayable} displayable displayable element
  23. */
  24. ClippathManager.prototype.update = function (displayable) {
  25. var svgEl = this.getSvgElement(displayable);
  26. if (svgEl) {
  27. this.updateDom(svgEl, displayable.__clipPaths, false);
  28. }
  29. var textEl = this.getTextSvgElement(displayable);
  30. if (textEl) {
  31. // Make another clipPath for text, since it's transform
  32. // matrix is not the same with svgElement
  33. this.updateDom(textEl, displayable.__clipPaths, true);
  34. }
  35. this.markUsed(displayable);
  36. };
  37. /**
  38. * Create an SVGElement of displayable and create a <clipPath> of its
  39. * clipPath
  40. *
  41. * @param {Displayable} parentEl parent element
  42. * @param {ClipPath[]} clipPaths clipPaths of parent element
  43. * @param {boolean} isText if parent element is Text
  44. */
  45. ClippathManager.prototype.updateDom = function (parentEl, clipPaths, isText) {
  46. if (clipPaths && clipPaths.length > 0) {
  47. // Has clipPath, create <clipPath> with the first clipPath
  48. var defs = this.getDefs(true);
  49. var clipPath = clipPaths[0];
  50. var clipPathEl;
  51. var id;
  52. var dom = isText ? '_textDom' : '_dom';
  53. if (clipPath[dom]) {
  54. // Use a dom that is already in <defs>
  55. id = clipPath[dom].getAttribute('id');
  56. clipPathEl = clipPath[dom]; // Use a dom that is already in <defs>
  57. if (!defs.contains(clipPathEl)) {
  58. // This happens when set old clipPath that has
  59. // been previously removed
  60. defs.appendChild(clipPathEl);
  61. }
  62. } else {
  63. // New <clipPath>
  64. id = 'zr-clip-' + this.nextId;
  65. ++this.nextId;
  66. clipPathEl = this.createElement('clipPath');
  67. clipPathEl.setAttribute('id', id);
  68. defs.appendChild(clipPathEl);
  69. clipPath[dom] = clipPathEl;
  70. } // Build path and add to <clipPath>
  71. var svgProxy = this.getSvgProxy(clipPath);
  72. if (clipPath.transform && clipPath.parent.invTransform && !isText) {
  73. /**
  74. * If a clipPath has a parent with transform, the transform
  75. * of parent should not be considered when setting transform
  76. * of clipPath. So we need to transform back from parent's
  77. * transform, which is done by multiplying parent's inverse
  78. * transform.
  79. */
  80. // Store old transform
  81. var transform = Array.prototype.slice.call(clipPath.transform); // Transform back from parent, and brush path
  82. matrix.mul(clipPath.transform, clipPath.parent.invTransform, clipPath.transform);
  83. svgProxy.brush(clipPath); // Set back transform of clipPath
  84. clipPath.transform = transform;
  85. } else {
  86. svgProxy.brush(clipPath);
  87. }
  88. var pathEl = this.getSvgElement(clipPath);
  89. /**
  90. * Use `cloneNode()` here to appendChild to multiple parents,
  91. * which may happend when Text and other shapes are using the same
  92. * clipPath. Since Text will create an extra clipPath DOM due to
  93. * different transform rules.
  94. */
  95. clipPathEl.appendChild(pathEl.cloneNode());
  96. parentEl.setAttribute('clip-path', 'url(#' + id + ')');
  97. if (clipPaths.length > 1) {
  98. // Make the other clipPaths recursively
  99. this.updateDom(clipPathEl, clipPaths.slice(1), isText);
  100. }
  101. } else {
  102. // No clipPath
  103. if (parentEl) {
  104. parentEl.setAttribute('clip-path', 'none');
  105. }
  106. }
  107. };
  108. /**
  109. * Mark a single clipPath to be used
  110. *
  111. * @param {Displayable} displayable displayable element
  112. */
  113. ClippathManager.prototype.markUsed = function (displayable) {
  114. var that = this;
  115. if (displayable.__clipPaths && displayable.__clipPaths.length > 0) {
  116. zrUtil.each(displayable.__clipPaths, function (clipPath) {
  117. if (clipPath._dom) {
  118. Definable.prototype.markUsed.call(that, clipPath._dom);
  119. }
  120. if (clipPath._textDom) {
  121. Definable.prototype.markUsed.call(that, clipPath._textDom);
  122. }
  123. });
  124. }
  125. };
  126. var _default = ClippathManager;
  127. module.exports = _default;