156 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| var Definable = require("./Definable");
 | |
| 
 | |
| var zrUtil = require("../../core/util");
 | |
| 
 | |
| var matrix = require("../../core/matrix");
 | |
| 
 | |
| /**
 | |
|  * @file Manages SVG clipPath elements.
 | |
|  * @author Zhang Wenli
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * 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);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| };
 | |
| 
 | |
| var _default = ClippathManager;
 | |
| module.exports = _default; |