| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- /**
- * @file Manages elements that can be defined in <defs> in SVG,
- * e.g., gradients, clip path, etc.
- * @author Zhang Wenli
- */
- import {createElement} from '../../svg/core';
- import * as zrUtil from '../../core/util';
- import Displayable from '../../graphic/Displayable';
- const MARK_UNUSED = '0';
- const MARK_USED = '1';
- /**
- * Manages elements that can be defined in <defs> in SVG,
- * e.g., gradients, clip path, etc.
- */
- export default class Definable {
- nextId = 0
- protected _zrId: number
- protected _svgRoot: SVGElement
- protected _tagNames: string[]
- protected _markLabel: string
- protected _domName: string = '_dom'
- constructor(
- zrId: number, // zrender instance id
- svgRoot: SVGElement, // root of SVG document
- tagNames: string | string[], // possible tag names
- markLabel: string, // label name to make if the element
- domName?: string
- ) {
- this._zrId = zrId;
- this._svgRoot = svgRoot;
- this._tagNames = typeof tagNames === 'string' ? [tagNames] : tagNames;
- this._markLabel = markLabel;
- if (domName) {
- this._domName = domName;
- }
- }
- /**
- * Get the <defs> tag for svgRoot; optionally creates one if not exists.
- *
- * @param isForceCreating if need to create when not exists
- * @return SVG <defs> element, null if it doesn't
- * exist and isForceCreating is false
- */
- getDefs(isForceCreating?: boolean): SVGDefsElement {
- let svgRoot = this._svgRoot;
- let defs = this._svgRoot.getElementsByTagName('defs');
- if (defs.length === 0) {
- // Not exist
- if (isForceCreating) {
- let defs = svgRoot.insertBefore(
- createElement('defs'), // Create new tag
- svgRoot.firstChild // Insert in the front of svg
- ) as SVGDefsElement;
- if (!defs.contains) {
- // IE doesn't support contains method
- defs.contains = function (el) {
- const children = defs.children;
- if (!children) {
- return false;
- }
- for (let 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 element style element. e.g., for gradient,
- * it may be '#ccc' or {type: 'linear', ...}
- * @param onUpdate update callback
- */
- doUpdate<T>(target: T, onUpdate?: (target: T) => void) {
- if (!target) {
- return;
- }
- const defs = this.getDefs(false);
- if ((target as any)[this._domName] && defs.contains((target as any)[this._domName])) {
- // Update DOM
- if (typeof onUpdate === 'function') {
- onUpdate(target);
- }
- }
- else {
- // No previous dom, create new
- const dom = this.add(target);
- if (dom) {
- (target as any)[this._domName] = dom;
- }
- }
- }
- add(target: any): SVGElement {
- return null;
- }
- /**
- * Add gradient dom to defs
- *
- * @param dom DOM to be added to <defs>
- */
- addDom(dom: SVGElement) {
- const defs = this.getDefs(true);
- if (dom.parentNode !== defs) {
- defs.appendChild(dom);
- }
- }
- /**
- * Remove DOM of a given element.
- *
- * @param target Target where to attach the dom
- */
- removeDom<T>(target: T) {
- const defs = this.getDefs(false);
- if (defs && (target as any)[this._domName]) {
- defs.removeChild((target as any)[this._domName]);
- (target as any)[this._domName] = null;
- }
- }
- /**
- * Get DOMs of this element.
- *
- * @return doms of this defineable elements in <defs>
- */
- getDoms() {
- const defs = this.getDefs(false);
- if (!defs) {
- // No dom when defs is not defined
- return [];
- }
- let doms: SVGElement[] = [];
- zrUtil.each(this._tagNames, function (tagName) {
- const tags = defs.getElementsByTagName(tagName) as HTMLCollectionOf<SVGElement>;
- // Note that tags is HTMLCollection, which is array-like
- // rather than real array.
- // So `doms.concat(tags)` add tags as one object.
- for (let i = 0; i < tags.length; i++) {
- doms.push(tags[i]);
- }
- });
- return doms;
- }
- /**
- * Mark DOMs to be unused before painting, and clear unused ones at the end
- * of the painting.
- */
- markAllUnused() {
- const doms = this.getDoms();
- const that = this;
- zrUtil.each(doms, function (dom) {
- (dom as any)[that._markLabel] = MARK_UNUSED;
- });
- }
- /**
- * Mark a single DOM to be used.
- *
- * @param dom DOM to mark
- */
- markDomUsed(dom: SVGElement) {
- dom && ((dom as any)[this._markLabel] = MARK_USED);
- };
- markDomUnused(dom: SVGElement) {
- dom && ((dom as any)[this._markLabel] = MARK_UNUSED);
- };
- isDomUnused(dom: SVGElement) {
- return dom && (dom as any)[this._markLabel] !== MARK_USED;
- }
- /**
- * Remove unused DOMs defined in <defs>
- */
- removeUnused() {
- const defs = this.getDefs(false);
- if (!defs) {
- // Nothing to remove
- return;
- }
- const doms = this.getDoms();
- zrUtil.each(doms, (dom) => {
- if (this.isDomUnused(dom)) {
- // Remove gradient
- defs.removeChild(dom);
- }
- });
- }
- /**
- * Get SVG element.
- *
- * @param displayable displayable element
- * @return SVG element
- */
- getSvgElement(displayable: Displayable): SVGElement {
- return displayable.__svgEl;
- }
- }
|