266 lines
5.9 KiB
JavaScript
266 lines
5.9 KiB
JavaScript
|
var _core = require("../core");
|
||
|
|
||
|
var createElement = _core.createElement;
|
||
|
|
||
|
var zrUtil = require("../../core/util");
|
||
|
|
||
|
var Path = require("../../graphic/Path");
|
||
|
|
||
|
var ZImage = require("../../graphic/Image");
|
||
|
|
||
|
var ZText = require("../../graphic/Text");
|
||
|
|
||
|
var _graphic = require("../graphic");
|
||
|
|
||
|
var svgPath = _graphic.path;
|
||
|
var svgImage = _graphic.image;
|
||
|
var svgText = _graphic.text;
|
||
|
|
||
|
/**
|
||
|
* @file Manages elements that can be defined in <defs> in SVG,
|
||
|
* e.g., gradients, clip path, etc.
|
||
|
* @author Zhang Wenli
|
||
|
*/
|
||
|
var MARK_UNUSED = '0';
|
||
|
var MARK_USED = '1';
|
||
|
/**
|
||
|
* Manages elements that can be defined in <defs> in SVG,
|
||
|
* e.g., gradients, clip path, etc.
|
||
|
*
|
||
|
* @class
|
||
|
* @param {number} zrId zrender instance id
|
||
|
* @param {SVGElement} svgRoot root of SVG document
|
||
|
* @param {string|string[]} tagNames possible tag names
|
||
|
* @param {string} markLabel label name to make if the element
|
||
|
* is used
|
||
|
*/
|
||
|
|
||
|
function Definable(zrId, svgRoot, tagNames, markLabel, domName) {
|
||
|
this._zrId = zrId;
|
||
|
this._svgRoot = svgRoot;
|
||
|
this._tagNames = typeof tagNames === 'string' ? [tagNames] : tagNames;
|
||
|
this._markLabel = markLabel;
|
||
|
this._domName = domName || '_dom';
|
||
|
this.nextId = 0;
|
||
|
}
|
||
|
|
||
|
Definable.prototype.createElement = createElement;
|
||
|
/**
|
||
|
* Get the <defs> tag for svgRoot; optionally creates one if not exists.
|
||
|
*
|
||
|
* @param {boolean} isForceCreating if need to create when not exists
|
||
|
* @return {SVGDefsElement} SVG <defs> element, null if it doesn't
|
||
|
* exist and isForceCreating is false
|
||
|
*/
|
||
|
|
||
|
Definable.prototype.getDefs = function (isForceCreating) {
|
||
|
var svgRoot = this._svgRoot;
|
||
|
|
||
|
var defs = this._svgRoot.getElementsByTagName('defs');
|
||
|
|
||
|
if (defs.length === 0) {
|
||
|
// Not exist
|
||
|
if (isForceCreating) {
|
||
|
defs = svgRoot.insertBefore(this.createElement('defs'), // Create new tag
|
||
|
svgRoot.firstChild // Insert in the front of svg
|
||
|
);
|
||
|
|
||
|
if (!defs.contains) {
|
||
|
// IE doesn't support contains method
|
||
|
defs.contains = function (el) {
|
||
|
var children = defs.children;
|
||
|
|
||
|
if (!children) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (var i = children.length - 1; i >= 0; --i) {
|
||
|
if (children[i] === el) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
return defs;
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
} else {
|
||
|
return defs[0];
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Update DOM element if necessary.
|
||
|
*
|
||
|
* @param {Object|string} element style element. e.g., for gradient,
|
||
|
* it may be '#ccc' or {type: 'linear', ...}
|
||
|
* @param {Function|undefined} onUpdate update callback
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.update = function (element, onUpdate) {
|
||
|
if (!element) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var defs = this.getDefs(false);
|
||
|
|
||
|
if (element[this._domName] && defs.contains(element[this._domName])) {
|
||
|
// Update DOM
|
||
|
if (typeof onUpdate === 'function') {
|
||
|
onUpdate(element);
|
||
|
}
|
||
|
} else {
|
||
|
// No previous dom, create new
|
||
|
var dom = this.add(element);
|
||
|
|
||
|
if (dom) {
|
||
|
element[this._domName] = dom;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Add gradient dom to defs
|
||
|
*
|
||
|
* @param {SVGElement} dom DOM to be added to <defs>
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.addDom = function (dom) {
|
||
|
var defs = this.getDefs(true);
|
||
|
defs.appendChild(dom);
|
||
|
};
|
||
|
/**
|
||
|
* Remove DOM of a given element.
|
||
|
*
|
||
|
* @param {SVGElement} element element to remove dom
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.removeDom = function (element) {
|
||
|
var defs = this.getDefs(false);
|
||
|
|
||
|
if (defs && element[this._domName]) {
|
||
|
defs.removeChild(element[this._domName]);
|
||
|
element[this._domName] = null;
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Get DOMs of this element.
|
||
|
*
|
||
|
* @return {HTMLDomElement} doms of this defineable elements in <defs>
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.getDoms = function () {
|
||
|
var defs = this.getDefs(false);
|
||
|
|
||
|
if (!defs) {
|
||
|
// No dom when defs is not defined
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
var doms = [];
|
||
|
zrUtil.each(this._tagNames, function (tagName) {
|
||
|
var tags = defs.getElementsByTagName(tagName); // Note that tags is HTMLCollection, which is array-like
|
||
|
// rather than real array.
|
||
|
// So `doms.concat(tags)` add tags as one object.
|
||
|
|
||
|
doms = doms.concat([].slice.call(tags));
|
||
|
});
|
||
|
return doms;
|
||
|
};
|
||
|
/**
|
||
|
* Mark DOMs to be unused before painting, and clear unused ones at the end
|
||
|
* of the painting.
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.markAllUnused = function () {
|
||
|
var doms = this.getDoms();
|
||
|
var that = this;
|
||
|
zrUtil.each(doms, function (dom) {
|
||
|
dom[that._markLabel] = MARK_UNUSED;
|
||
|
});
|
||
|
};
|
||
|
/**
|
||
|
* Mark a single DOM to be used.
|
||
|
*
|
||
|
* @param {SVGElement} dom DOM to mark
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.markUsed = function (dom) {
|
||
|
if (dom) {
|
||
|
dom[this._markLabel] = MARK_USED;
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Remove unused DOMs defined in <defs>
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.removeUnused = function () {
|
||
|
var defs = this.getDefs(false);
|
||
|
|
||
|
if (!defs) {
|
||
|
// Nothing to remove
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var doms = this.getDoms();
|
||
|
var that = this;
|
||
|
zrUtil.each(doms, function (dom) {
|
||
|
if (dom[that._markLabel] !== MARK_USED) {
|
||
|
// Remove gradient
|
||
|
defs.removeChild(dom);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
/**
|
||
|
* Get SVG proxy.
|
||
|
*
|
||
|
* @param {Displayable} displayable displayable element
|
||
|
* @return {Path|Image|Text} svg proxy of given element
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.getSvgProxy = function (displayable) {
|
||
|
if (displayable instanceof Path) {
|
||
|
return svgPath;
|
||
|
} else if (displayable instanceof ZImage) {
|
||
|
return svgImage;
|
||
|
} else if (displayable instanceof ZText) {
|
||
|
return svgText;
|
||
|
} else {
|
||
|
return svgPath;
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Get text SVG element.
|
||
|
*
|
||
|
* @param {Displayable} displayable displayable element
|
||
|
* @return {SVGElement} SVG element of text
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.getTextSvgElement = function (displayable) {
|
||
|
return displayable.__textSvgEl;
|
||
|
};
|
||
|
/**
|
||
|
* Get SVG element.
|
||
|
*
|
||
|
* @param {Displayable} displayable displayable element
|
||
|
* @return {SVGElement} SVG element
|
||
|
*/
|
||
|
|
||
|
|
||
|
Definable.prototype.getSvgElement = function (displayable) {
|
||
|
return displayable.__svgEl;
|
||
|
};
|
||
|
|
||
|
var _default = Definable;
|
||
|
module.exports = _default;
|