blob: 379df0930aa6030f63a6a8a5e1dd60acab5a1789 [file] [log] [blame]
/**
* @file Manages SVG clipPath elements.
* @author Zhang Wenli
*/
import Definable from './Definable';
import * as zrUtil from '../../core/util';
import * as matrix from '../../core/matrix';
/**
* Manages SVG clipPath elements.
*
* @class
* @extends Definable
* @param {number} zrId zrender instance id
* @param {SVGElement} svgRoot root of SVG document
*/
function ClippathManager(zrId, svgRoot) {
Definable.call(this, zrId, svgRoot, 'clipPath', '__clippath_in_use__');
}
zrUtil.inherits(ClippathManager, Definable);
/**
* Update clipPath.
*
* @param {Displayable} displayable displayable element
*/
ClippathManager.prototype.update = function (displayable) {
var svgEl = this.getSvgElement(displayable);
if (svgEl) {
this.updateDom(svgEl, displayable.__clipPaths, false);
}
var textEl = this.getTextSvgElement(displayable);
if (textEl) {
// Make another clipPath for text, since it's transform
// matrix is not the same with svgElement
this.updateDom(textEl, displayable.__clipPaths, true);
}
this.markUsed(displayable);
};
/**
* Create an SVGElement of displayable and create a <clipPath> of its
* clipPath
*
* @param {Displayable} parentEl parent element
* @param {ClipPath[]} clipPaths clipPaths of parent element
* @param {boolean} isText if parent element is Text
*/
ClippathManager.prototype.updateDom = function (parentEl, clipPaths, isText) {
if (clipPaths && clipPaths.length > 0) {
// Has clipPath, create <clipPath> with the first clipPath
var defs = this.getDefs(true);
var clipPath = clipPaths[0];
var clipPathEl;
var id;
var dom = isText ? '_textDom' : '_dom';
if (clipPath[dom]) {
// Use a dom that is already in <defs>
id = clipPath[dom].getAttribute('id');
clipPathEl = clipPath[dom]; // Use a dom that is already in <defs>
if (!defs.contains(clipPathEl)) {
// This happens when set old clipPath that has
// been previously removed
defs.appendChild(clipPathEl);
}
} else {
// New <clipPath>
id = 'zr' + this._zrId + '-clip-' + this.nextId;
++this.nextId;
clipPathEl = this.createElement('clipPath');
clipPathEl.setAttribute('id', id);
defs.appendChild(clipPathEl);
clipPath[dom] = clipPathEl;
} // Build path and add to <clipPath>
var svgProxy = this.getSvgProxy(clipPath);
if (clipPath.transform && clipPath.parent.invTransform && !isText) {
/**
* If a clipPath has a parent with transform, the transform
* of parent should not be considered when setting transform
* of clipPath. So we need to transform back from parent's
* transform, which is done by multiplying parent's inverse
* transform.
*/
// Store old transform
var transform = Array.prototype.slice.call(clipPath.transform); // Transform back from parent, and brush path
matrix.mul(clipPath.transform, clipPath.parent.invTransform, clipPath.transform);
svgProxy.brush(clipPath); // Set back transform of clipPath
clipPath.transform = transform;
} else {
svgProxy.brush(clipPath);
}
var pathEl = this.getSvgElement(clipPath);
clipPathEl.innerHTML = '';
/**
* Use `cloneNode()` here to appendChild to multiple parents,
* which may happend when Text and other shapes are using the same
* clipPath. Since Text will create an extra clipPath DOM due to
* different transform rules.
*/
clipPathEl.appendChild(pathEl.cloneNode());
parentEl.setAttribute('clip-path', 'url(#' + id + ')');
if (clipPaths.length > 1) {
// Make the other clipPaths recursively
this.updateDom(clipPathEl, clipPaths.slice(1), isText);
}
} else {
// No clipPath
if (parentEl) {
parentEl.setAttribute('clip-path', 'none');
}
}
};
/**
* Mark a single clipPath to be used
*
* @param {Displayable} displayable displayable element
*/
ClippathManager.prototype.markUsed = function (displayable) {
var that = this; // displayable.__clipPaths can only be `null`/`undefined` or an non-empty array.
if (displayable.__clipPaths) {
zrUtil.each(displayable.__clipPaths, function (clipPath) {
if (clipPath._dom) {
Definable.prototype.markUsed.call(that, clipPath._dom);
}
if (clipPath._textDom) {
Definable.prototype.markUsed.call(that, clipPath._textDom);
}
});
}
};
export default ClippathManager;