123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- var Definable = require("./Definable");
- var zrUtil = require("../../core/util");
- /**
- * @file Manages SVG shadow elements.
- * @author Zhang Wenli
- */
- /**
- * Manages SVG shadow elements.
- *
- * @class
- * @extends Definable
- * @param {number} zrId zrender instance id
- * @param {SVGElement} svgRoot root of SVG document
- */
- function ShadowManager(zrId, svgRoot) {
- Definable.call(this, zrId, svgRoot, ['filter'], '__filter_in_use__', '_shadowDom');
- }
- zrUtil.inherits(ShadowManager, Definable);
- /**
- * Create new shadow DOM for fill or stroke if not exist,
- * but will not update shadow if exists.
- *
- * @param {SvgElement} svgElement SVG element to paint
- * @param {Displayable} displayable zrender displayable element
- */
- ShadowManager.prototype.addWithoutUpdate = function (svgElement, displayable) {
- if (displayable && hasShadow(displayable.style)) {
- // Create dom in <defs> if not exists
- var dom;
- if (displayable._shadowDom) {
- // Gradient exists
- dom = displayable._shadowDom;
- var defs = this.getDefs(true);
- if (!defs.contains(displayable._shadowDom)) {
- // _shadowDom is no longer in defs, recreate
- this.addDom(dom);
- }
- } else {
- // New dom
- dom = this.add(displayable);
- }
- this.markUsed(displayable);
- var id = dom.getAttribute('id');
- svgElement.style.filter = 'url(#' + id + ')';
- }
- };
- /**
- * Add a new shadow tag in <defs>
- *
- * @param {Displayable} displayable zrender displayable element
- * @return {SVGFilterElement} created DOM
- */
- ShadowManager.prototype.add = function (displayable) {
- var dom = this.createElement('filter'); // Set dom id with shadow id, since each shadow instance
- // will have no more than one dom element.
- // id may exists before for those dirty elements, in which case
- // id should remain the same, and other attributes should be
- // updated.
- displayable._shadowDomId = displayable._shadowDomId || this.nextId++;
- dom.setAttribute('id', 'zr' + this._zrId + '-shadow-' + displayable._shadowDomId);
- this.updateDom(displayable, dom);
- this.addDom(dom);
- return dom;
- };
- /**
- * Update shadow.
- *
- * @param {Displayable} displayable zrender displayable element
- */
- ShadowManager.prototype.update = function (svgElement, displayable) {
- var style = displayable.style;
- if (hasShadow(style)) {
- var that = this;
- Definable.prototype.update.call(this, displayable, function () {
- that.updateDom(displayable, displayable._shadowDom);
- });
- } else {
- // Remove shadow
- this.remove(svgElement, displayable);
- }
- };
- /**
- * Remove DOM and clear parent filter
- */
- ShadowManager.prototype.remove = function (svgElement, displayable) {
- if (displayable._shadowDomId != null) {
- this.removeDom(svgElement);
- svgElement.style.filter = '';
- }
- };
- /**
- * Update shadow dom
- *
- * @param {Displayable} displayable zrender displayable element
- * @param {SVGFilterElement} dom DOM to update
- */
- ShadowManager.prototype.updateDom = function (displayable, dom) {
- var domChild = dom.getElementsByTagName('feDropShadow');
- if (domChild.length === 0) {
- domChild = this.createElement('feDropShadow');
- } else {
- domChild = domChild[0];
- }
- var style = displayable.style;
- var scaleX = displayable.scale ? displayable.scale[0] || 1 : 1;
- var scaleY = displayable.scale ? displayable.scale[1] || 1 : 1; // TODO: textBoxShadowBlur is not supported yet
- var offsetX;
- var offsetY;
- var blur;
- var color;
- if (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY) {
- offsetX = style.shadowOffsetX || 0;
- offsetY = style.shadowOffsetY || 0;
- blur = style.shadowBlur;
- color = style.shadowColor;
- } else if (style.textShadowBlur) {
- offsetX = style.textShadowOffsetX || 0;
- offsetY = style.textShadowOffsetY || 0;
- blur = style.textShadowBlur;
- color = style.textShadowColor;
- } else {
- // Remove shadow
- this.removeDom(dom, style);
- return;
- }
- domChild.setAttribute('dx', offsetX / scaleX);
- domChild.setAttribute('dy', offsetY / scaleY);
- domChild.setAttribute('flood-color', color); // Divide by two here so that it looks the same as in canvas
- // See: https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur
- var stdDx = blur / 2 / scaleX;
- var stdDy = blur / 2 / scaleY;
- var stdDeviation = stdDx + ' ' + stdDy;
- domChild.setAttribute('stdDeviation', stdDeviation); // Fix filter clipping problem
- dom.setAttribute('x', '-100%');
- dom.setAttribute('y', '-100%');
- dom.setAttribute('width', Math.ceil(blur / 2 * 200) + '%');
- dom.setAttribute('height', Math.ceil(blur / 2 * 200) + '%');
- dom.appendChild(domChild); // Store dom element in shadow, to avoid creating multiple
- // dom instances for the same shadow element
- displayable._shadowDom = dom;
- };
- /**
- * Mark a single shadow to be used
- *
- * @param {Displayable} displayable displayable element
- */
- ShadowManager.prototype.markUsed = function (displayable) {
- if (displayable._shadowDom) {
- Definable.prototype.markUsed.call(this, displayable._shadowDom);
- }
- };
- function hasShadow(style) {
- // TODO: textBoxShadowBlur is not supported yet
- return style && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY || style.textShadowBlur || style.textShadowOffsetX || style.textShadowOffsetY);
- }
- var _default = ShadowManager;
- module.exports = _default;
|