123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- /**
- * @file Manages SVG pattern elements.
- * @author Zhang Wenli
- */
- import Definable from './Definable';
- import * as zrUtil from '../../core/util';
- import Displayable from '../../graphic/Displayable';
- import {PatternObject} from '../../graphic/Pattern';
- import {createOrUpdateImage} from '../../graphic/helper/image';
- import WeakMap from '../../core/WeakMap';
- import { getIdURL, isPattern, isSVGPattern } from '../../svg/helper';
- import { createElement } from '../../svg/core';
- const patternDomMap = new WeakMap<PatternObject, SVGElement>();
- /**
- * Manages SVG pattern elements.
- *
- * @param zrId zrender instance id
- * @param svgRoot root of SVG document
- */
- export default class PatternManager extends Definable {
- constructor(zrId: number, svgRoot: SVGElement) {
- super(zrId, svgRoot, ['pattern'], '__pattern_in_use__');
- }
- /**
- * Create new pattern DOM for fill or stroke if not exist,
- * but will not update pattern if exists.
- *
- * @param svgElement SVG element to paint
- * @param displayable zrender displayable element
- */
- addWithoutUpdate(
- svgElement: SVGElement,
- displayable: Displayable
- ) {
- if (displayable && displayable.style) {
- const that = this;
- zrUtil.each(['fill', 'stroke'], function (fillOrStroke: 'fill' | 'stroke') {
- const pattern = displayable.style[fillOrStroke] as PatternObject;
- if (isPattern(pattern)) {
- const defs = that.getDefs(true);
- // Create dom in <defs> if not exists
- let dom = patternDomMap.get(pattern);
- if (dom) {
- // Pattern exists
- if (!defs.contains(dom)) {
- // __dom is no longer in defs, recreate
- that.addDom(dom);
- }
- }
- else {
- // New dom
- dom = that.add(pattern);
- }
- that.markUsed(displayable);
- svgElement.setAttribute(fillOrStroke, getIdURL(dom.getAttribute('id')));
- }
- });
- }
- }
- /**
- * Add a new pattern tag in <defs>
- *
- * @param pattern zr pattern instance
- */
- add(pattern: PatternObject): SVGElement {
- if (!isPattern(pattern)) {
- return;
- }
- let dom = createElement('pattern');
- pattern.id = pattern.id == null ? this.nextId++ : pattern.id;
- dom.setAttribute('id', 'zr' + this._zrId
- + '-pattern-' + pattern.id);
- dom.setAttribute('patternUnits', 'userSpaceOnUse');
- this.updateDom(pattern, dom);
- this.addDom(dom);
- return dom;
- }
- /**
- * Update pattern.
- *
- * @param pattern zr pattern instance or color string
- */
- update(pattern: PatternObject | string) {
- if (!isPattern(pattern)) {
- return;
- }
- const that = this;
- this.doUpdate(pattern, function () {
- const dom = patternDomMap.get(pattern);
- that.updateDom(pattern, dom);
- });
- }
- /**
- * Update pattern dom
- *
- * @param pattern zr pattern instance
- * @param patternDom DOM to update
- */
- updateDom(pattern: PatternObject, patternDom: SVGElement) {
- if (isSVGPattern(pattern)) {
- // New SVGPattern will not been supported in the legacy SVG renderer.
- // svg-legacy will been removed soon.
- // const svgElement = pattern.svgElement;
- // const isStringSVG = typeof svgElement === 'string';
- // if (isStringSVG || svgElement.parentNode !== patternDom) {
- // if (isStringSVG) {
- // patternDom.innerHTML = svgElement;
- // }
- // else {
- // patternDom.innerHTML = '';
- // patternDom.appendChild(svgElement);
- // }
- // patternDom.setAttribute('width', pattern.svgWidth as any);
- // patternDom.setAttribute('height', pattern.svgHeight as any);
- // }
- }
- else {
- let img: SVGElement;
- const prevImage = patternDom.getElementsByTagName('image');
- if (prevImage.length) {
- if (pattern.image) {
- // Update
- img = prevImage[0];
- }
- else {
- // Remove
- patternDom.removeChild(prevImage[0]);
- return;
- }
- }
- else if (pattern.image) {
- // Create
- img = createElement('image');
- }
- if (img) {
- let imageSrc;
- const patternImage = pattern.image;
- if (typeof patternImage === 'string') {
- imageSrc = patternImage;
- }
- else if (patternImage instanceof HTMLImageElement) {
- imageSrc = patternImage.src;
- }
- else if (patternImage instanceof HTMLCanvasElement) {
- imageSrc = patternImage.toDataURL();
- }
- if (imageSrc) {
- img.setAttribute('href', imageSrc);
- // No need to re-render so dirty is empty
- const hostEl = {
- dirty: () => {}
- };
- const updateSize = (img: HTMLImageElement) => {
- patternDom.setAttribute('width', img.width as any);
- patternDom.setAttribute('height', img.height as any);
- };
- const createdImage = createOrUpdateImage(imageSrc, img as any, hostEl, updateSize);
- if (createdImage && createdImage.width && createdImage.height) {
- // Loaded before
- updateSize(createdImage as HTMLImageElement);
- }
- patternDom.appendChild(img);
- }
- }
- }
- const x = pattern.x || 0;
- const y = pattern.y || 0;
- const rotation = (pattern.rotation || 0) / Math.PI * 180;
- const scaleX = pattern.scaleX || 1;
- const scaleY = pattern.scaleY || 1;
- const transform = `translate(${x}, ${y}) rotate(${rotation}) scale(${scaleX}, ${scaleY})`;
- patternDom.setAttribute('patternTransform', transform);
- patternDomMap.set(pattern, patternDom);
- }
- /**
- * Mark a single pattern to be used
- *
- * @param displayable displayable element
- */
- markUsed(displayable: Displayable) {
- if (displayable.style) {
- if (isPattern(displayable.style.fill)) {
- super.markDomUsed(patternDomMap.get(displayable.style.fill));
- }
- if (isPattern(displayable.style.stroke)) {
- super.markDomUsed(patternDomMap.get(displayable.style.stroke));
- }
- }
- }
- }
|