You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1009 lines
						
					
					
						
							26 KiB
						
					
					
				
			
		
		
	
	
							1009 lines
						
					
					
						
							26 KiB
						
					
					
				var _config = require("./config"); | 
						|
 | 
						|
var devicePixelRatio = _config.devicePixelRatio; | 
						|
 | 
						|
var util = require("./core/util"); | 
						|
 | 
						|
var log = require("./core/log"); | 
						|
 | 
						|
var BoundingRect = require("./core/BoundingRect"); | 
						|
 | 
						|
var timsort = require("./core/timsort"); | 
						|
 | 
						|
var Layer = require("./Layer"); | 
						|
 | 
						|
var requestAnimationFrame = require("./animation/requestAnimationFrame"); | 
						|
 | 
						|
var Image = require("./graphic/Image"); | 
						|
 | 
						|
var env = require("./core/env"); | 
						|
 | 
						|
var HOVER_LAYER_ZLEVEL = 1e5; | 
						|
var CANVAS_ZLEVEL = 314159; | 
						|
var EL_AFTER_INCREMENTAL_INC = 0.01; | 
						|
var INCREMENTAL_INC = 0.001; | 
						|
 | 
						|
function parseInt10(val) { | 
						|
  return parseInt(val, 10); | 
						|
} | 
						|
 | 
						|
function isLayerValid(layer) { | 
						|
  if (!layer) { | 
						|
    return false; | 
						|
  } | 
						|
 | 
						|
  if (layer.__builtin__) { | 
						|
    return true; | 
						|
  } | 
						|
 | 
						|
  if (typeof layer.resize !== 'function' || typeof layer.refresh !== 'function') { | 
						|
    return false; | 
						|
  } | 
						|
 | 
						|
  return true; | 
						|
} | 
						|
 | 
						|
var tmpRect = new BoundingRect(0, 0, 0, 0); | 
						|
var viewRect = new BoundingRect(0, 0, 0, 0); | 
						|
 | 
						|
function isDisplayableCulled(el, width, height) { | 
						|
  tmpRect.copy(el.getBoundingRect()); | 
						|
 | 
						|
  if (el.transform) { | 
						|
    tmpRect.applyTransform(el.transform); | 
						|
  } | 
						|
 | 
						|
  viewRect.width = width; | 
						|
  viewRect.height = height; | 
						|
  return !tmpRect.intersect(viewRect); | 
						|
} | 
						|
 | 
						|
function isClipPathChanged(clipPaths, prevClipPaths) { | 
						|
  if (clipPaths === prevClipPaths) { | 
						|
    // Can both be null or undefined | 
						|
    return false; | 
						|
  } | 
						|
 | 
						|
  if (!clipPaths || !prevClipPaths || clipPaths.length !== prevClipPaths.length) { | 
						|
    return true; | 
						|
  } | 
						|
 | 
						|
  for (var i = 0; i < clipPaths.length; i++) { | 
						|
    if (clipPaths[i] !== prevClipPaths[i]) { | 
						|
      return true; | 
						|
    } | 
						|
  } | 
						|
} | 
						|
 | 
						|
function doClip(clipPaths, ctx) { | 
						|
  for (var i = 0; i < clipPaths.length; i++) { | 
						|
    var clipPath = clipPaths[i]; | 
						|
    clipPath.setTransform(ctx); | 
						|
    ctx.beginPath(); | 
						|
    clipPath.buildPath(ctx, clipPath.shape); | 
						|
    ctx.clip(); // Transform back | 
						|
 | 
						|
    clipPath.restoreTransform(ctx); | 
						|
  } | 
						|
} | 
						|
 | 
						|
function createRoot(width, height) { | 
						|
  var domRoot = document.createElement('div'); // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬 | 
						|
 | 
						|
  domRoot.style.cssText = ['position:relative', 'overflow:hidden', 'width:' + width + 'px', 'height:' + height + 'px', 'padding:0', 'margin:0', 'border-width:0'].join(';') + ';'; | 
						|
  return domRoot; | 
						|
} | 
						|
/** | 
						|
 * @alias module:zrender/Painter | 
						|
 * @constructor | 
						|
 * @param {HTMLElement} root 绘图容器 | 
						|
 * @param {module:zrender/Storage} storage | 
						|
 * @param {Object} opts | 
						|
 */ | 
						|
 | 
						|
 | 
						|
var Painter = function (root, storage, opts) { | 
						|
  this.type = 'canvas'; // In node environment using node-canvas | 
						|
 | 
						|
  var singleCanvas = !root.nodeName // In node ? | 
						|
  || root.nodeName.toUpperCase() === 'CANVAS'; | 
						|
  this._opts = opts = util.extend({}, opts || {}); | 
						|
  /** | 
						|
   * @type {number} | 
						|
   */ | 
						|
 | 
						|
  this.dpr = opts.devicePixelRatio || devicePixelRatio; | 
						|
  /** | 
						|
   * @type {boolean} | 
						|
   * @private | 
						|
   */ | 
						|
 | 
						|
  this._singleCanvas = singleCanvas; | 
						|
  /** | 
						|
   * 绘图容器 | 
						|
   * @type {HTMLElement} | 
						|
   */ | 
						|
 | 
						|
  this.root = root; | 
						|
  var rootStyle = root.style; | 
						|
 | 
						|
  if (rootStyle) { | 
						|
    rootStyle['-webkit-tap-highlight-color'] = 'transparent'; | 
						|
    rootStyle['-webkit-user-select'] = rootStyle['user-select'] = rootStyle['-webkit-touch-callout'] = 'none'; | 
						|
    root.innerHTML = ''; | 
						|
  } | 
						|
  /** | 
						|
   * @type {module:zrender/Storage} | 
						|
   */ | 
						|
 | 
						|
 | 
						|
  this.storage = storage; | 
						|
  /** | 
						|
   * @type {Array.<number>} | 
						|
   * @private | 
						|
   */ | 
						|
 | 
						|
  var zlevelList = this._zlevelList = []; | 
						|
  /** | 
						|
   * @type {Object.<string, module:zrender/Layer>} | 
						|
   * @private | 
						|
   */ | 
						|
 | 
						|
  var layers = this._layers = {}; | 
						|
  /** | 
						|
   * @type {Object.<string, Object>} | 
						|
   * @private | 
						|
   */ | 
						|
 | 
						|
  this._layerConfig = {}; | 
						|
  /** | 
						|
   * zrender will do compositing when root is a canvas and have multiple zlevels. | 
						|
   */ | 
						|
 | 
						|
  this._needsManuallyCompositing = false; | 
						|
 | 
						|
  if (!singleCanvas) { | 
						|
    this._width = this._getSize(0); | 
						|
    this._height = this._getSize(1); | 
						|
    var domRoot = this._domRoot = createRoot(this._width, this._height); | 
						|
    root.appendChild(domRoot); | 
						|
  } else { | 
						|
    var width = root.width; | 
						|
    var height = root.height; | 
						|
 | 
						|
    if (opts.width != null) { | 
						|
      width = opts.width; | 
						|
    } | 
						|
 | 
						|
    if (opts.height != null) { | 
						|
      height = opts.height; | 
						|
    } | 
						|
 | 
						|
    this.dpr = opts.devicePixelRatio || 1; // Use canvas width and height directly | 
						|
 | 
						|
    root.width = width * this.dpr; | 
						|
    root.height = height * this.dpr; | 
						|
    this._width = width; | 
						|
    this._height = height; // Create layer if only one given canvas | 
						|
    // Device can be specified to create a high dpi image. | 
						|
 | 
						|
    var mainLayer = new Layer(root, this, this.dpr); | 
						|
    mainLayer.__builtin__ = true; | 
						|
    mainLayer.initContext(); // FIXME Use canvas width and height | 
						|
    // mainLayer.resize(width, height); | 
						|
 | 
						|
    layers[CANVAS_ZLEVEL] = mainLayer; | 
						|
    mainLayer.zlevel = CANVAS_ZLEVEL; // Not use common zlevel. | 
						|
 | 
						|
    zlevelList.push(CANVAS_ZLEVEL); | 
						|
    this._domRoot = root; | 
						|
  } | 
						|
  /** | 
						|
   * @type {module:zrender/Layer} | 
						|
   * @private | 
						|
   */ | 
						|
 | 
						|
 | 
						|
  this._hoverlayer = null; | 
						|
  this._hoverElements = []; | 
						|
}; | 
						|
 | 
						|
Painter.prototype = { | 
						|
  constructor: Painter, | 
						|
  getType: function () { | 
						|
    return 'canvas'; | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * If painter use a single canvas | 
						|
   * @return {boolean} | 
						|
   */ | 
						|
  isSingleCanvas: function () { | 
						|
    return this._singleCanvas; | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * @return {HTMLDivElement} | 
						|
   */ | 
						|
  getViewportRoot: function () { | 
						|
    return this._domRoot; | 
						|
  }, | 
						|
  getViewportRootOffset: function () { | 
						|
    var viewportRoot = this.getViewportRoot(); | 
						|
 | 
						|
    if (viewportRoot) { | 
						|
      return { | 
						|
        offsetLeft: viewportRoot.offsetLeft || 0, | 
						|
        offsetTop: viewportRoot.offsetTop || 0 | 
						|
      }; | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 刷新 | 
						|
   * @param {boolean} [paintAll=false] 强制绘制所有displayable | 
						|
   */ | 
						|
  refresh: function (paintAll) { | 
						|
    var list = this.storage.getDisplayList(true); | 
						|
    var zlevelList = this._zlevelList; | 
						|
    this._redrawId = Math.random(); | 
						|
 | 
						|
    this._paintList(list, paintAll, this._redrawId); // Paint custum layers | 
						|
 | 
						|
 | 
						|
    for (var i = 0; i < zlevelList.length; i++) { | 
						|
      var z = zlevelList[i]; | 
						|
      var layer = this._layers[z]; | 
						|
 | 
						|
      if (!layer.__builtin__ && layer.refresh) { | 
						|
        var clearColor = i === 0 ? this._backgroundColor : null; | 
						|
        layer.refresh(clearColor); | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    this.refreshHover(); | 
						|
    return this; | 
						|
  }, | 
						|
  addHover: function (el, hoverStyle) { | 
						|
    if (el.__hoverMir) { | 
						|
      return; | 
						|
    } | 
						|
 | 
						|
    var elMirror = new el.constructor({ | 
						|
      style: el.style, | 
						|
      shape: el.shape, | 
						|
      z: el.z, | 
						|
      z2: el.z2, | 
						|
      silent: el.silent | 
						|
    }); | 
						|
    elMirror.__from = el; | 
						|
    el.__hoverMir = elMirror; | 
						|
    hoverStyle && elMirror.setStyle(hoverStyle); | 
						|
 | 
						|
    this._hoverElements.push(elMirror); | 
						|
 | 
						|
    return elMirror; | 
						|
  }, | 
						|
  removeHover: function (el) { | 
						|
    var elMirror = el.__hoverMir; | 
						|
    var hoverElements = this._hoverElements; | 
						|
    var idx = util.indexOf(hoverElements, elMirror); | 
						|
 | 
						|
    if (idx >= 0) { | 
						|
      hoverElements.splice(idx, 1); | 
						|
    } | 
						|
 | 
						|
    el.__hoverMir = null; | 
						|
  }, | 
						|
  clearHover: function (el) { | 
						|
    var hoverElements = this._hoverElements; | 
						|
 | 
						|
    for (var i = 0; i < hoverElements.length; i++) { | 
						|
      var from = hoverElements[i].__from; | 
						|
 | 
						|
      if (from) { | 
						|
        from.__hoverMir = null; | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    hoverElements.length = 0; | 
						|
  }, | 
						|
  refreshHover: function () { | 
						|
    var hoverElements = this._hoverElements; | 
						|
    var len = hoverElements.length; | 
						|
    var hoverLayer = this._hoverlayer; | 
						|
    hoverLayer && hoverLayer.clear(); | 
						|
 | 
						|
    if (!len) { | 
						|
      return; | 
						|
    } | 
						|
 | 
						|
    timsort(hoverElements, this.storage.displayableSortFunc); // Use a extream large zlevel | 
						|
    // FIXME? | 
						|
 | 
						|
    if (!hoverLayer) { | 
						|
      hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL); | 
						|
    } | 
						|
 | 
						|
    var scope = {}; | 
						|
    hoverLayer.ctx.save(); | 
						|
 | 
						|
    for (var i = 0; i < len;) { | 
						|
      var el = hoverElements[i]; | 
						|
      var originalEl = el.__from; // Original el is removed | 
						|
      // PENDING | 
						|
 | 
						|
      if (!(originalEl && originalEl.__zr)) { | 
						|
        hoverElements.splice(i, 1); | 
						|
        originalEl.__hoverMir = null; | 
						|
        len--; | 
						|
        continue; | 
						|
      } | 
						|
 | 
						|
      i++; // Use transform | 
						|
      // FIXME style and shape ? | 
						|
 | 
						|
      if (!originalEl.invisible) { | 
						|
        el.transform = originalEl.transform; | 
						|
        el.invTransform = originalEl.invTransform; | 
						|
        el.__clipPaths = originalEl.__clipPaths; // el. | 
						|
 | 
						|
        this._doPaintEl(el, hoverLayer, true, scope); | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    hoverLayer.ctx.restore(); | 
						|
  }, | 
						|
  getHoverLayer: function () { | 
						|
    return this.getLayer(HOVER_LAYER_ZLEVEL); | 
						|
  }, | 
						|
  _paintList: function (list, paintAll, redrawId) { | 
						|
    if (this._redrawId !== redrawId) { | 
						|
      return; | 
						|
    } | 
						|
 | 
						|
    paintAll = paintAll || false; | 
						|
 | 
						|
    this._updateLayerStatus(list); | 
						|
 | 
						|
    var finished = this._doPaintList(list, paintAll); | 
						|
 | 
						|
    if (this._needsManuallyCompositing) { | 
						|
      this._compositeManually(); | 
						|
    } | 
						|
 | 
						|
    if (!finished) { | 
						|
      var self = this; | 
						|
      requestAnimationFrame(function () { | 
						|
        self._paintList(list, paintAll, redrawId); | 
						|
      }); | 
						|
    } | 
						|
  }, | 
						|
  _compositeManually: function () { | 
						|
    var ctx = this.getLayer(CANVAS_ZLEVEL).ctx; | 
						|
    var width = this._domRoot.width; | 
						|
    var height = this._domRoot.height; | 
						|
    ctx.clearRect(0, 0, width, height); // PENDING, If only builtin layer? | 
						|
 | 
						|
    this.eachBuiltinLayer(function (layer) { | 
						|
      if (layer.virtual) { | 
						|
        ctx.drawImage(layer.dom, 0, 0, width, height); | 
						|
      } | 
						|
    }); | 
						|
  }, | 
						|
  _doPaintList: function (list, paintAll) { | 
						|
    var layerList = []; | 
						|
 | 
						|
    for (var zi = 0; zi < this._zlevelList.length; zi++) { | 
						|
      var zlevel = this._zlevelList[zi]; | 
						|
      var layer = this._layers[zlevel]; | 
						|
 | 
						|
      if (layer.__builtin__ && layer !== this._hoverlayer && (layer.__dirty || paintAll)) { | 
						|
        layerList.push(layer); | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    var finished = true; | 
						|
 | 
						|
    for (var k = 0; k < layerList.length; k++) { | 
						|
      var layer = layerList[k]; | 
						|
      var ctx = layer.ctx; | 
						|
      var scope = {}; | 
						|
      ctx.save(); | 
						|
      var start = paintAll ? layer.__startIndex : layer.__drawIndex; | 
						|
      var useTimer = !paintAll && layer.incremental && Date.now; | 
						|
      var startTime = useTimer && Date.now(); | 
						|
      var clearColor = layer.zlevel === this._zlevelList[0] ? this._backgroundColor : null; // All elements in this layer are cleared. | 
						|
 | 
						|
      if (layer.__startIndex === layer.__endIndex) { | 
						|
        layer.clear(false, clearColor); | 
						|
      } else if (start === layer.__startIndex) { | 
						|
        var firstEl = list[start]; | 
						|
 | 
						|
        if (!firstEl.incremental || !firstEl.notClear || paintAll) { | 
						|
          layer.clear(false, clearColor); | 
						|
        } | 
						|
      } | 
						|
 | 
						|
      if (start === -1) { | 
						|
        console.error('For some unknown reason. drawIndex is -1'); | 
						|
        start = layer.__startIndex; | 
						|
      } | 
						|
 | 
						|
      for (var i = start; i < layer.__endIndex; i++) { | 
						|
        var el = list[i]; | 
						|
 | 
						|
        this._doPaintEl(el, layer, paintAll, scope); | 
						|
 | 
						|
        el.__dirty = el.__dirtyText = false; | 
						|
 | 
						|
        if (useTimer) { | 
						|
          // Date.now can be executed in 13,025,305 ops/second. | 
						|
          var dTime = Date.now() - startTime; // Give 15 millisecond to draw. | 
						|
          // The rest elements will be drawn in the next frame. | 
						|
 | 
						|
          if (dTime > 15) { | 
						|
            break; | 
						|
          } | 
						|
        } | 
						|
      } | 
						|
 | 
						|
      layer.__drawIndex = i; | 
						|
 | 
						|
      if (layer.__drawIndex < layer.__endIndex) { | 
						|
        finished = false; | 
						|
      } | 
						|
 | 
						|
      if (scope.prevElClipPaths) { | 
						|
        // Needs restore the state. If last drawn element is in the clipping area. | 
						|
        ctx.restore(); | 
						|
      } | 
						|
 | 
						|
      ctx.restore(); | 
						|
    } | 
						|
 | 
						|
    if (env.wxa) { | 
						|
      // Flush for weixin application | 
						|
      util.each(this._layers, function (layer) { | 
						|
        if (layer && layer.ctx && layer.ctx.draw) { | 
						|
          layer.ctx.draw(); | 
						|
        } | 
						|
      }); | 
						|
    } | 
						|
 | 
						|
    return finished; | 
						|
  }, | 
						|
  _doPaintEl: function (el, currentLayer, forcePaint, scope) { | 
						|
    var ctx = currentLayer.ctx; | 
						|
    var m = el.transform; | 
						|
 | 
						|
    if ((currentLayer.__dirty || forcePaint) && // Ignore invisible element | 
						|
    !el.invisible // Ignore transparent element | 
						|
    && el.style.opacity !== 0 // Ignore scale 0 element, in some environment like node-canvas | 
						|
    // Draw a scale 0 element can cause all following draw wrong | 
						|
    // And setTransform with scale 0 will cause set back transform failed. | 
						|
    && !(m && !m[0] && !m[3]) // Ignore culled element | 
						|
    && !(el.culling && isDisplayableCulled(el, this._width, this._height))) { | 
						|
      var clipPaths = el.__clipPaths; // Optimize when clipping on group with several elements | 
						|
 | 
						|
      if (!scope.prevElClipPaths || isClipPathChanged(clipPaths, scope.prevElClipPaths)) { | 
						|
        // If has previous clipping state, restore from it | 
						|
        if (scope.prevElClipPaths) { | 
						|
          currentLayer.ctx.restore(); | 
						|
          scope.prevElClipPaths = null; // Reset prevEl since context has been restored | 
						|
 | 
						|
          scope.prevEl = null; | 
						|
        } // New clipping state | 
						|
 | 
						|
 | 
						|
        if (clipPaths) { | 
						|
          ctx.save(); | 
						|
          doClip(clipPaths, ctx); | 
						|
          scope.prevElClipPaths = clipPaths; | 
						|
        } | 
						|
      } | 
						|
 | 
						|
      el.beforeBrush && el.beforeBrush(ctx); | 
						|
      el.brush(ctx, scope.prevEl || null); | 
						|
      scope.prevEl = el; | 
						|
      el.afterBrush && el.afterBrush(ctx); | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 获取 zlevel 所在层,如果不存在则会创建一个新的层 | 
						|
   * @param {number} zlevel | 
						|
   * @param {boolean} virtual Virtual layer will not be inserted into dom. | 
						|
   * @return {module:zrender/Layer} | 
						|
   */ | 
						|
  getLayer: function (zlevel, virtual) { | 
						|
    if (this._singleCanvas && !this._needsManuallyCompositing) { | 
						|
      zlevel = CANVAS_ZLEVEL; | 
						|
    } | 
						|
 | 
						|
    var layer = this._layers[zlevel]; | 
						|
 | 
						|
    if (!layer) { | 
						|
      // Create a new layer | 
						|
      layer = new Layer('zr_' + zlevel, this, this.dpr); | 
						|
      layer.zlevel = zlevel; | 
						|
      layer.__builtin__ = true; | 
						|
 | 
						|
      if (this._layerConfig[zlevel]) { | 
						|
        util.merge(layer, this._layerConfig[zlevel], true); | 
						|
      } | 
						|
 | 
						|
      if (virtual) { | 
						|
        layer.virtual = virtual; | 
						|
      } | 
						|
 | 
						|
      this.insertLayer(zlevel, layer); // Context is created after dom inserted to document | 
						|
      // Or excanvas will get 0px clientWidth and clientHeight | 
						|
 | 
						|
      layer.initContext(); | 
						|
    } | 
						|
 | 
						|
    return layer; | 
						|
  }, | 
						|
  insertLayer: function (zlevel, layer) { | 
						|
    var layersMap = this._layers; | 
						|
    var zlevelList = this._zlevelList; | 
						|
    var len = zlevelList.length; | 
						|
    var prevLayer = null; | 
						|
    var i = -1; | 
						|
    var domRoot = this._domRoot; | 
						|
 | 
						|
    if (layersMap[zlevel]) { | 
						|
      log('ZLevel ' + zlevel + ' has been used already'); | 
						|
      return; | 
						|
    } // Check if is a valid layer | 
						|
 | 
						|
 | 
						|
    if (!isLayerValid(layer)) { | 
						|
      log('Layer of zlevel ' + zlevel + ' is not valid'); | 
						|
      return; | 
						|
    } | 
						|
 | 
						|
    if (len > 0 && zlevel > zlevelList[0]) { | 
						|
      for (i = 0; i < len - 1; i++) { | 
						|
        if (zlevelList[i] < zlevel && zlevelList[i + 1] > zlevel) { | 
						|
          break; | 
						|
        } | 
						|
      } | 
						|
 | 
						|
      prevLayer = layersMap[zlevelList[i]]; | 
						|
    } | 
						|
 | 
						|
    zlevelList.splice(i + 1, 0, zlevel); | 
						|
    layersMap[zlevel] = layer; // Vitual layer will not directly show on the screen. | 
						|
    // (It can be a WebGL layer and assigned to a ZImage element) | 
						|
    // But it still under management of zrender. | 
						|
 | 
						|
    if (!layer.virtual) { | 
						|
      if (prevLayer) { | 
						|
        var prevDom = prevLayer.dom; | 
						|
 | 
						|
        if (prevDom.nextSibling) { | 
						|
          domRoot.insertBefore(layer.dom, prevDom.nextSibling); | 
						|
        } else { | 
						|
          domRoot.appendChild(layer.dom); | 
						|
        } | 
						|
      } else { | 
						|
        if (domRoot.firstChild) { | 
						|
          domRoot.insertBefore(layer.dom, domRoot.firstChild); | 
						|
        } else { | 
						|
          domRoot.appendChild(layer.dom); | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
  }, | 
						|
  // Iterate each layer | 
						|
  eachLayer: function (cb, context) { | 
						|
    var zlevelList = this._zlevelList; | 
						|
    var z; | 
						|
    var i; | 
						|
 | 
						|
    for (i = 0; i < zlevelList.length; i++) { | 
						|
      z = zlevelList[i]; | 
						|
      cb.call(context, this._layers[z], z); | 
						|
    } | 
						|
  }, | 
						|
  // Iterate each buildin layer | 
						|
  eachBuiltinLayer: function (cb, context) { | 
						|
    var zlevelList = this._zlevelList; | 
						|
    var layer; | 
						|
    var z; | 
						|
    var i; | 
						|
 | 
						|
    for (i = 0; i < zlevelList.length; i++) { | 
						|
      z = zlevelList[i]; | 
						|
      layer = this._layers[z]; | 
						|
 | 
						|
      if (layer.__builtin__) { | 
						|
        cb.call(context, layer, z); | 
						|
      } | 
						|
    } | 
						|
  }, | 
						|
  // Iterate each other layer except buildin layer | 
						|
  eachOtherLayer: function (cb, context) { | 
						|
    var zlevelList = this._zlevelList; | 
						|
    var layer; | 
						|
    var z; | 
						|
    var i; | 
						|
 | 
						|
    for (i = 0; i < zlevelList.length; i++) { | 
						|
      z = zlevelList[i]; | 
						|
      layer = this._layers[z]; | 
						|
 | 
						|
      if (!layer.__builtin__) { | 
						|
        cb.call(context, layer, z); | 
						|
      } | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 获取所有已创建的层 | 
						|
   * @param {Array.<module:zrender/Layer>} [prevLayer] | 
						|
   */ | 
						|
  getLayers: function () { | 
						|
    return this._layers; | 
						|
  }, | 
						|
  _updateLayerStatus: function (list) { | 
						|
    this.eachBuiltinLayer(function (layer, z) { | 
						|
      layer.__dirty = layer.__used = false; | 
						|
    }); | 
						|
 | 
						|
    function updatePrevLayer(idx) { | 
						|
      if (prevLayer) { | 
						|
        if (prevLayer.__endIndex !== idx) { | 
						|
          prevLayer.__dirty = true; | 
						|
        } | 
						|
 | 
						|
        prevLayer.__endIndex = idx; | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    if (this._singleCanvas) { | 
						|
      for (var i = 1; i < list.length; i++) { | 
						|
        var el = list[i]; | 
						|
 | 
						|
        if (el.zlevel !== list[i - 1].zlevel || el.incremental) { | 
						|
          this._needsManuallyCompositing = true; | 
						|
          break; | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    var prevLayer = null; | 
						|
    var incrementalLayerCount = 0; | 
						|
 | 
						|
    for (var i = 0; i < list.length; i++) { | 
						|
      var el = list[i]; | 
						|
      var zlevel = el.zlevel; | 
						|
      var layer; // PENDING If change one incremental element style ? | 
						|
      // TODO Where there are non-incremental elements between incremental elements. | 
						|
 | 
						|
      if (el.incremental) { | 
						|
        layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing); | 
						|
        layer.incremental = true; | 
						|
        incrementalLayerCount = 1; | 
						|
      } else { | 
						|
        layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing); | 
						|
      } | 
						|
 | 
						|
      if (!layer.__builtin__) { | 
						|
        log('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id); | 
						|
      } | 
						|
 | 
						|
      if (layer !== prevLayer) { | 
						|
        layer.__used = true; | 
						|
 | 
						|
        if (layer.__startIndex !== i) { | 
						|
          layer.__dirty = true; | 
						|
        } | 
						|
 | 
						|
        layer.__startIndex = i; | 
						|
 | 
						|
        if (!layer.incremental) { | 
						|
          layer.__drawIndex = i; | 
						|
        } else { | 
						|
          // Mark layer draw index needs to update. | 
						|
          layer.__drawIndex = -1; | 
						|
        } | 
						|
 | 
						|
        updatePrevLayer(i); | 
						|
        prevLayer = layer; | 
						|
      } | 
						|
 | 
						|
      if (el.__dirty) { | 
						|
        layer.__dirty = true; | 
						|
 | 
						|
        if (layer.incremental && layer.__drawIndex < 0) { | 
						|
          // Start draw from the first dirty element. | 
						|
          layer.__drawIndex = i; | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    updatePrevLayer(i); | 
						|
    this.eachBuiltinLayer(function (layer, z) { | 
						|
      // Used in last frame but not in this frame. Needs clear | 
						|
      if (!layer.__used && layer.getElementCount() > 0) { | 
						|
        layer.__dirty = true; | 
						|
        layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0; | 
						|
      } // For incremental layer. In case start index changed and no elements are dirty. | 
						|
 | 
						|
 | 
						|
      if (layer.__dirty && layer.__drawIndex < 0) { | 
						|
        layer.__drawIndex = layer.__startIndex; | 
						|
      } | 
						|
    }); | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 清除hover层外所有内容 | 
						|
   */ | 
						|
  clear: function () { | 
						|
    this.eachBuiltinLayer(this._clearLayer); | 
						|
    return this; | 
						|
  }, | 
						|
  _clearLayer: function (layer) { | 
						|
    layer.clear(); | 
						|
  }, | 
						|
  setBackgroundColor: function (backgroundColor) { | 
						|
    this._backgroundColor = backgroundColor; | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 修改指定zlevel的绘制参数 | 
						|
   * | 
						|
   * @param {string} zlevel | 
						|
   * @param {Object} config 配置对象 | 
						|
   * @param {string} [config.clearColor=0] 每次清空画布的颜色 | 
						|
   * @param {string} [config.motionBlur=false] 是否开启动态模糊 | 
						|
   * @param {number} [config.lastFrameAlpha=0.7] | 
						|
   *                 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显 | 
						|
   */ | 
						|
  configLayer: function (zlevel, config) { | 
						|
    if (config) { | 
						|
      var layerConfig = this._layerConfig; | 
						|
 | 
						|
      if (!layerConfig[zlevel]) { | 
						|
        layerConfig[zlevel] = config; | 
						|
      } else { | 
						|
        util.merge(layerConfig[zlevel], config, true); | 
						|
      } | 
						|
 | 
						|
      for (var i = 0; i < this._zlevelList.length; i++) { | 
						|
        var _zlevel = this._zlevelList[i]; | 
						|
 | 
						|
        if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) { | 
						|
          var layer = this._layers[_zlevel]; | 
						|
          util.merge(layer, layerConfig[zlevel], true); | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 删除指定层 | 
						|
   * @param {number} zlevel 层所在的zlevel | 
						|
   */ | 
						|
  delLayer: function (zlevel) { | 
						|
    var layers = this._layers; | 
						|
    var zlevelList = this._zlevelList; | 
						|
    var layer = layers[zlevel]; | 
						|
 | 
						|
    if (!layer) { | 
						|
      return; | 
						|
    } | 
						|
 | 
						|
    layer.dom.parentNode.removeChild(layer.dom); | 
						|
    delete layers[zlevel]; | 
						|
    zlevelList.splice(util.indexOf(zlevelList, zlevel), 1); | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 区域大小变化后重绘 | 
						|
   */ | 
						|
  resize: function (width, height) { | 
						|
    if (!this._domRoot.style) { | 
						|
      // Maybe in node or worker | 
						|
      if (width == null || height == null) { | 
						|
        return; | 
						|
      } | 
						|
 | 
						|
      this._width = width; | 
						|
      this._height = height; | 
						|
      this.getLayer(CANVAS_ZLEVEL).resize(width, height); | 
						|
    } else { | 
						|
      var domRoot = this._domRoot; // FIXME Why ? | 
						|
 | 
						|
      domRoot.style.display = 'none'; // Save input w/h | 
						|
 | 
						|
      var opts = this._opts; | 
						|
      width != null && (opts.width = width); | 
						|
      height != null && (opts.height = height); | 
						|
      width = this._getSize(0); | 
						|
      height = this._getSize(1); | 
						|
      domRoot.style.display = ''; // 优化没有实际改变的resize | 
						|
 | 
						|
      if (this._width !== width || height !== this._height) { | 
						|
        domRoot.style.width = width + 'px'; | 
						|
        domRoot.style.height = height + 'px'; | 
						|
 | 
						|
        for (var id in this._layers) { | 
						|
          if (this._layers.hasOwnProperty(id)) { | 
						|
            this._layers[id].resize(width, height); | 
						|
          } | 
						|
        } | 
						|
 | 
						|
        util.each(this._progressiveLayers, function (layer) { | 
						|
          layer.resize(width, height); | 
						|
        }); | 
						|
        this.refresh(true); | 
						|
      } | 
						|
 | 
						|
      this._width = width; | 
						|
      this._height = height; | 
						|
    } | 
						|
 | 
						|
    return this; | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 清除单独的一个层 | 
						|
   * @param {number} zlevel | 
						|
   */ | 
						|
  clearLayer: function (zlevel) { | 
						|
    var layer = this._layers[zlevel]; | 
						|
 | 
						|
    if (layer) { | 
						|
      layer.clear(); | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 释放 | 
						|
   */ | 
						|
  dispose: function () { | 
						|
    this.root.innerHTML = ''; | 
						|
    this.root = this.storage = this._domRoot = this._layers = null; | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * Get canvas which has all thing rendered | 
						|
   * @param {Object} opts | 
						|
   * @param {string} [opts.backgroundColor] | 
						|
   * @param {number} [opts.pixelRatio] | 
						|
   */ | 
						|
  getRenderedCanvas: function (opts) { | 
						|
    opts = opts || {}; | 
						|
 | 
						|
    if (this._singleCanvas && !this._compositeManually) { | 
						|
      return this._layers[CANVAS_ZLEVEL].dom; | 
						|
    } | 
						|
 | 
						|
    var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr); | 
						|
    imageLayer.initContext(); | 
						|
    imageLayer.clear(false, opts.backgroundColor || this._backgroundColor); | 
						|
 | 
						|
    if (opts.pixelRatio <= this.dpr) { | 
						|
      this.refresh(); | 
						|
      var width = imageLayer.dom.width; | 
						|
      var height = imageLayer.dom.height; | 
						|
      var ctx = imageLayer.ctx; | 
						|
      this.eachLayer(function (layer) { | 
						|
        if (layer.__builtin__) { | 
						|
          ctx.drawImage(layer.dom, 0, 0, width, height); | 
						|
        } else if (layer.renderToCanvas) { | 
						|
          imageLayer.ctx.save(); | 
						|
          layer.renderToCanvas(imageLayer.ctx); | 
						|
          imageLayer.ctx.restore(); | 
						|
        } | 
						|
      }); | 
						|
    } else { | 
						|
      // PENDING, echarts-gl and incremental rendering. | 
						|
      var scope = {}; | 
						|
      var displayList = this.storage.getDisplayList(true); | 
						|
 | 
						|
      for (var i = 0; i < displayList.length; i++) { | 
						|
        var el = displayList[i]; | 
						|
 | 
						|
        this._doPaintEl(el, imageLayer, true, scope); | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    return imageLayer.dom; | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 获取绘图区域宽度 | 
						|
   */ | 
						|
  getWidth: function () { | 
						|
    return this._width; | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * 获取绘图区域高度 | 
						|
   */ | 
						|
  getHeight: function () { | 
						|
    return this._height; | 
						|
  }, | 
						|
  _getSize: function (whIdx) { | 
						|
    var opts = this._opts; | 
						|
    var wh = ['width', 'height'][whIdx]; | 
						|
    var cwh = ['clientWidth', 'clientHeight'][whIdx]; | 
						|
    var plt = ['paddingLeft', 'paddingTop'][whIdx]; | 
						|
    var prb = ['paddingRight', 'paddingBottom'][whIdx]; | 
						|
 | 
						|
    if (opts[wh] != null && opts[wh] !== 'auto') { | 
						|
      return parseFloat(opts[wh]); | 
						|
    } | 
						|
 | 
						|
    var root = this.root; // IE8 does not support getComputedStyle, but it use VML. | 
						|
 | 
						|
    var stl = document.defaultView.getComputedStyle(root); | 
						|
    return (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) - (parseInt10(stl[plt]) || 0) - (parseInt10(stl[prb]) || 0) | 0; | 
						|
  }, | 
						|
  pathToImage: function (path, dpr) { | 
						|
    dpr = dpr || this.dpr; | 
						|
    var canvas = document.createElement('canvas'); | 
						|
    var ctx = canvas.getContext('2d'); | 
						|
    var rect = path.getBoundingRect(); | 
						|
    var style = path.style; | 
						|
    var shadowBlurSize = style.shadowBlur * dpr; | 
						|
    var shadowOffsetX = style.shadowOffsetX * dpr; | 
						|
    var shadowOffsetY = style.shadowOffsetY * dpr; | 
						|
    var lineWidth = style.hasStroke() ? style.lineWidth : 0; | 
						|
    var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize); | 
						|
    var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize); | 
						|
    var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize); | 
						|
    var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize); | 
						|
    var width = rect.width + leftMargin + rightMargin; | 
						|
    var height = rect.height + topMargin + bottomMargin; | 
						|
    canvas.width = width * dpr; | 
						|
    canvas.height = height * dpr; | 
						|
    ctx.scale(dpr, dpr); | 
						|
    ctx.clearRect(0, 0, width, height); | 
						|
    ctx.dpr = dpr; | 
						|
    var pathTransform = { | 
						|
      position: path.position, | 
						|
      rotation: path.rotation, | 
						|
      scale: path.scale | 
						|
    }; | 
						|
    path.position = [leftMargin - rect.x, topMargin - rect.y]; | 
						|
    path.rotation = 0; | 
						|
    path.scale = [1, 1]; | 
						|
    path.updateTransform(); | 
						|
 | 
						|
    if (path) { | 
						|
      path.brush(ctx); | 
						|
    } | 
						|
 | 
						|
    var ImageShape = Image; | 
						|
    var imgShape = new ImageShape({ | 
						|
      style: { | 
						|
        x: 0, | 
						|
        y: 0, | 
						|
        image: canvas | 
						|
      } | 
						|
    }); | 
						|
 | 
						|
    if (pathTransform.position != null) { | 
						|
      imgShape.position = path.position = pathTransform.position; | 
						|
    } | 
						|
 | 
						|
    if (pathTransform.rotation != null) { | 
						|
      imgShape.rotation = path.rotation = pathTransform.rotation; | 
						|
    } | 
						|
 | 
						|
    if (pathTransform.scale != null) { | 
						|
      imgShape.scale = path.scale = pathTransform.scale; | 
						|
    } | 
						|
 | 
						|
    return imgShape; | 
						|
  } | 
						|
}; | 
						|
var _default = Painter; | 
						|
module.exports = _default; |