Painter.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035
  1. var _config = require("./config");
  2. var devicePixelRatio = _config.devicePixelRatio;
  3. var util = require("./core/util");
  4. var logError = require("./core/log");
  5. var BoundingRect = require("./core/BoundingRect");
  6. var timsort = require("./core/timsort");
  7. var Layer = require("./Layer");
  8. var requestAnimationFrame = require("./animation/requestAnimationFrame");
  9. var Image = require("./graphic/Image");
  10. var env = require("./core/env");
  11. var HOVER_LAYER_ZLEVEL = 1e5;
  12. var CANVAS_ZLEVEL = 314159;
  13. var EL_AFTER_INCREMENTAL_INC = 0.01;
  14. var INCREMENTAL_INC = 0.001;
  15. function parseInt10(val) {
  16. return parseInt(val, 10);
  17. }
  18. function isLayerValid(layer) {
  19. if (!layer) {
  20. return false;
  21. }
  22. if (layer.__builtin__) {
  23. return true;
  24. }
  25. if (typeof layer.resize !== 'function' || typeof layer.refresh !== 'function') {
  26. return false;
  27. }
  28. return true;
  29. }
  30. var tmpRect = new BoundingRect(0, 0, 0, 0);
  31. var viewRect = new BoundingRect(0, 0, 0, 0);
  32. function isDisplayableCulled(el, width, height) {
  33. tmpRect.copy(el.getBoundingRect());
  34. if (el.transform) {
  35. tmpRect.applyTransform(el.transform);
  36. }
  37. viewRect.width = width;
  38. viewRect.height = height;
  39. return !tmpRect.intersect(viewRect);
  40. }
  41. function isClipPathChanged(clipPaths, prevClipPaths) {
  42. // displayable.__clipPaths can only be `null`/`undefined` or an non-empty array.
  43. if (clipPaths === prevClipPaths) {
  44. return false;
  45. }
  46. if (!clipPaths || !prevClipPaths || clipPaths.length !== prevClipPaths.length) {
  47. return true;
  48. }
  49. for (var i = 0; i < clipPaths.length; i++) {
  50. if (clipPaths[i] !== prevClipPaths[i]) {
  51. return true;
  52. }
  53. }
  54. return false;
  55. }
  56. function doClip(clipPaths, ctx) {
  57. for (var i = 0; i < clipPaths.length; i++) {
  58. var clipPath = clipPaths[i];
  59. clipPath.setTransform(ctx);
  60. ctx.beginPath();
  61. clipPath.buildPath(ctx, clipPath.shape);
  62. ctx.clip(); // Transform back
  63. clipPath.restoreTransform(ctx);
  64. }
  65. }
  66. function createRoot(width, height) {
  67. var domRoot = document.createElement('div'); // domRoot.onselectstart = returnFalse; // Avoid page selected
  68. domRoot.style.cssText = ['position:relative', // IOS13 safari probably has a compositing bug (z order of the canvas and the consequent
  69. // dom does not act as expected) when some of the parent dom has
  70. // `-webkit-overflow-scrolling: touch;` and the webpage is longer than one screen and
  71. // the canvas is not at the top part of the page.
  72. // Check `https://bugs.webkit.org/show_bug.cgi?id=203681` for more details. We remove
  73. // this `overflow:hidden` to avoid the bug.
  74. // 'overflow:hidden',
  75. 'width:' + width + 'px', 'height:' + height + 'px', 'padding:0', 'margin:0', 'border-width:0'].join(';') + ';';
  76. return domRoot;
  77. }
  78. /**
  79. * @alias module:zrender/Painter
  80. * @constructor
  81. * @param {HTMLElement} root 绘图容器
  82. * @param {module:zrender/Storage} storage
  83. * @param {Object} opts
  84. */
  85. var Painter = function (root, storage, opts) {
  86. this.type = 'canvas'; // In node environment using node-canvas
  87. var singleCanvas = !root.nodeName // In node ?
  88. || root.nodeName.toUpperCase() === 'CANVAS';
  89. this._opts = opts = util.extend({}, opts || {});
  90. /**
  91. * @type {number}
  92. */
  93. this.dpr = opts.devicePixelRatio || devicePixelRatio;
  94. /**
  95. * @type {boolean}
  96. * @private
  97. */
  98. this._singleCanvas = singleCanvas;
  99. /**
  100. * 绘图容器
  101. * @type {HTMLElement}
  102. */
  103. this.root = root;
  104. var rootStyle = root.style;
  105. if (rootStyle) {
  106. rootStyle['-webkit-tap-highlight-color'] = 'transparent';
  107. rootStyle['-webkit-user-select'] = rootStyle['user-select'] = rootStyle['-webkit-touch-callout'] = 'none';
  108. root.innerHTML = '';
  109. }
  110. /**
  111. * @type {module:zrender/Storage}
  112. */
  113. this.storage = storage;
  114. /**
  115. * @type {Array.<number>}
  116. * @private
  117. */
  118. var zlevelList = this._zlevelList = [];
  119. /**
  120. * @type {Object.<string, module:zrender/Layer>}
  121. * @private
  122. */
  123. var layers = this._layers = {};
  124. /**
  125. * @type {Object.<string, Object>}
  126. * @private
  127. */
  128. this._layerConfig = {};
  129. /**
  130. * zrender will do compositing when root is a canvas and have multiple zlevels.
  131. */
  132. this._needsManuallyCompositing = false;
  133. if (!singleCanvas) {
  134. this._width = this._getSize(0);
  135. this._height = this._getSize(1);
  136. var domRoot = this._domRoot = createRoot(this._width, this._height);
  137. root.appendChild(domRoot);
  138. } else {
  139. var width = root.width;
  140. var height = root.height;
  141. if (opts.width != null) {
  142. width = opts.width;
  143. }
  144. if (opts.height != null) {
  145. height = opts.height;
  146. }
  147. this.dpr = opts.devicePixelRatio || 1; // Use canvas width and height directly
  148. root.width = width * this.dpr;
  149. root.height = height * this.dpr;
  150. this._width = width;
  151. this._height = height; // Create layer if only one given canvas
  152. // Device can be specified to create a high dpi image.
  153. var mainLayer = new Layer(root, this, this.dpr);
  154. mainLayer.__builtin__ = true;
  155. mainLayer.initContext(); // FIXME Use canvas width and height
  156. // mainLayer.resize(width, height);
  157. layers[CANVAS_ZLEVEL] = mainLayer;
  158. mainLayer.zlevel = CANVAS_ZLEVEL; // Not use common zlevel.
  159. zlevelList.push(CANVAS_ZLEVEL);
  160. this._domRoot = root;
  161. }
  162. /**
  163. * @type {module:zrender/Layer}
  164. * @private
  165. */
  166. this._hoverlayer = null;
  167. this._hoverElements = [];
  168. };
  169. Painter.prototype = {
  170. constructor: Painter,
  171. getType: function () {
  172. return 'canvas';
  173. },
  174. /**
  175. * If painter use a single canvas
  176. * @return {boolean}
  177. */
  178. isSingleCanvas: function () {
  179. return this._singleCanvas;
  180. },
  181. /**
  182. * @return {HTMLDivElement}
  183. */
  184. getViewportRoot: function () {
  185. return this._domRoot;
  186. },
  187. getViewportRootOffset: function () {
  188. var viewportRoot = this.getViewportRoot();
  189. if (viewportRoot) {
  190. return {
  191. offsetLeft: viewportRoot.offsetLeft || 0,
  192. offsetTop: viewportRoot.offsetTop || 0
  193. };
  194. }
  195. },
  196. /**
  197. * 刷新
  198. * @param {boolean} [paintAll=false] 强制绘制所有displayable
  199. */
  200. refresh: function (paintAll) {
  201. var list = this.storage.getDisplayList(true);
  202. var zlevelList = this._zlevelList;
  203. this._redrawId = Math.random();
  204. this._paintList(list, paintAll, this._redrawId); // Paint custum layers
  205. for (var i = 0; i < zlevelList.length; i++) {
  206. var z = zlevelList[i];
  207. var layer = this._layers[z];
  208. if (!layer.__builtin__ && layer.refresh) {
  209. var clearColor = i === 0 ? this._backgroundColor : null;
  210. layer.refresh(clearColor);
  211. }
  212. }
  213. this.refreshHover();
  214. return this;
  215. },
  216. addHover: function (el, hoverStyle) {
  217. if (el.__hoverMir) {
  218. return;
  219. }
  220. var elMirror = new el.constructor({
  221. style: el.style,
  222. shape: el.shape,
  223. z: el.z,
  224. z2: el.z2,
  225. silent: el.silent
  226. });
  227. elMirror.__from = el;
  228. el.__hoverMir = elMirror;
  229. hoverStyle && elMirror.setStyle(hoverStyle);
  230. this._hoverElements.push(elMirror);
  231. return elMirror;
  232. },
  233. removeHover: function (el) {
  234. var elMirror = el.__hoverMir;
  235. var hoverElements = this._hoverElements;
  236. var idx = util.indexOf(hoverElements, elMirror);
  237. if (idx >= 0) {
  238. hoverElements.splice(idx, 1);
  239. }
  240. el.__hoverMir = null;
  241. },
  242. clearHover: function (el) {
  243. var hoverElements = this._hoverElements;
  244. for (var i = 0; i < hoverElements.length; i++) {
  245. var from = hoverElements[i].__from;
  246. if (from) {
  247. from.__hoverMir = null;
  248. }
  249. }
  250. hoverElements.length = 0;
  251. },
  252. refreshHover: function () {
  253. var hoverElements = this._hoverElements;
  254. var len = hoverElements.length;
  255. var hoverLayer = this._hoverlayer;
  256. hoverLayer && hoverLayer.clear();
  257. if (!len) {
  258. return;
  259. }
  260. timsort(hoverElements, this.storage.displayableSortFunc); // Use a extream large zlevel
  261. // FIXME?
  262. if (!hoverLayer) {
  263. hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL);
  264. }
  265. var scope = {};
  266. hoverLayer.ctx.save();
  267. for (var i = 0; i < len;) {
  268. var el = hoverElements[i];
  269. var originalEl = el.__from; // Original el is removed
  270. // PENDING
  271. if (!(originalEl && originalEl.__zr)) {
  272. hoverElements.splice(i, 1);
  273. originalEl.__hoverMir = null;
  274. len--;
  275. continue;
  276. }
  277. i++; // Use transform
  278. // FIXME style and shape ?
  279. if (!originalEl.invisible) {
  280. el.transform = originalEl.transform;
  281. el.invTransform = originalEl.invTransform;
  282. el.__clipPaths = originalEl.__clipPaths; // el.
  283. this._doPaintEl(el, hoverLayer, true, scope);
  284. }
  285. }
  286. hoverLayer.ctx.restore();
  287. },
  288. getHoverLayer: function () {
  289. return this.getLayer(HOVER_LAYER_ZLEVEL);
  290. },
  291. _paintList: function (list, paintAll, redrawId) {
  292. if (this._redrawId !== redrawId) {
  293. return;
  294. }
  295. paintAll = paintAll || false;
  296. this._updateLayerStatus(list);
  297. var finished = this._doPaintList(list, paintAll);
  298. if (this._needsManuallyCompositing) {
  299. this._compositeManually();
  300. }
  301. if (!finished) {
  302. var self = this;
  303. requestAnimationFrame(function () {
  304. self._paintList(list, paintAll, redrawId);
  305. });
  306. }
  307. },
  308. _compositeManually: function () {
  309. var ctx = this.getLayer(CANVAS_ZLEVEL).ctx;
  310. var width = this._domRoot.width;
  311. var height = this._domRoot.height;
  312. ctx.clearRect(0, 0, width, height); // PENDING, If only builtin layer?
  313. this.eachBuiltinLayer(function (layer) {
  314. if (layer.virtual) {
  315. ctx.drawImage(layer.dom, 0, 0, width, height);
  316. }
  317. });
  318. },
  319. _doPaintList: function (list, paintAll) {
  320. var layerList = [];
  321. for (var zi = 0; zi < this._zlevelList.length; zi++) {
  322. var zlevel = this._zlevelList[zi];
  323. var layer = this._layers[zlevel];
  324. if (layer.__builtin__ && layer !== this._hoverlayer && (layer.__dirty || paintAll)) {
  325. layerList.push(layer);
  326. }
  327. }
  328. var finished = true;
  329. for (var k = 0; k < layerList.length; k++) {
  330. var layer = layerList[k];
  331. var ctx = layer.ctx;
  332. var scope = {};
  333. ctx.save();
  334. var start = paintAll ? layer.__startIndex : layer.__drawIndex;
  335. var useTimer = !paintAll && layer.incremental && Date.now;
  336. var startTime = useTimer && Date.now();
  337. var clearColor = layer.zlevel === this._zlevelList[0] ? this._backgroundColor : null; // All elements in this layer are cleared.
  338. if (layer.__startIndex === layer.__endIndex) {
  339. layer.clear(false, clearColor);
  340. } else if (start === layer.__startIndex) {
  341. var firstEl = list[start];
  342. if (!firstEl.incremental || !firstEl.notClear || paintAll) {
  343. layer.clear(false, clearColor);
  344. }
  345. }
  346. if (start === -1) {
  347. console.error('For some unknown reason. drawIndex is -1');
  348. start = layer.__startIndex;
  349. }
  350. for (var i = start; i < layer.__endIndex; i++) {
  351. var el = list[i];
  352. this._doPaintEl(el, layer, paintAll, scope);
  353. el.__dirty = el.__dirtyText = false;
  354. if (useTimer) {
  355. // Date.now can be executed in 13,025,305 ops/second.
  356. var dTime = Date.now() - startTime; // Give 15 millisecond to draw.
  357. // The rest elements will be drawn in the next frame.
  358. if (dTime > 15) {
  359. break;
  360. }
  361. }
  362. }
  363. layer.__drawIndex = i;
  364. if (layer.__drawIndex < layer.__endIndex) {
  365. finished = false;
  366. }
  367. if (scope.prevElClipPaths) {
  368. // Needs restore the state. If last drawn element is in the clipping area.
  369. ctx.restore();
  370. }
  371. ctx.restore();
  372. }
  373. if (env.wxa) {
  374. // Flush for weixin application
  375. util.each(this._layers, function (layer) {
  376. if (layer && layer.ctx && layer.ctx.draw) {
  377. layer.ctx.draw();
  378. }
  379. });
  380. }
  381. return finished;
  382. },
  383. _doPaintEl: function (el, currentLayer, forcePaint, scope) {
  384. var ctx = currentLayer.ctx;
  385. var m = el.transform;
  386. if ((currentLayer.__dirty || forcePaint) && // Ignore invisible element
  387. !el.invisible // Ignore transparent element
  388. && el.style.opacity !== 0 // Ignore scale 0 element, in some environment like node-canvas
  389. // Draw a scale 0 element can cause all following draw wrong
  390. // And setTransform with scale 0 will cause set back transform failed.
  391. && !(m && !m[0] && !m[3]) // Ignore culled element
  392. && !(el.culling && isDisplayableCulled(el, this._width, this._height))) {
  393. var clipPaths = el.__clipPaths;
  394. var prevElClipPaths = scope.prevElClipPaths; // Optimize when clipping on group with several elements
  395. if (!prevElClipPaths || isClipPathChanged(clipPaths, prevElClipPaths)) {
  396. // If has previous clipping state, restore from it
  397. if (prevElClipPaths) {
  398. ctx.restore();
  399. scope.prevElClipPaths = null; // Reset prevEl since context has been restored
  400. scope.prevEl = null;
  401. } // New clipping state
  402. if (clipPaths) {
  403. ctx.save();
  404. doClip(clipPaths, ctx);
  405. scope.prevElClipPaths = clipPaths;
  406. }
  407. }
  408. el.beforeBrush && el.beforeBrush(ctx);
  409. el.brush(ctx, scope.prevEl || null);
  410. scope.prevEl = el;
  411. el.afterBrush && el.afterBrush(ctx);
  412. }
  413. },
  414. /**
  415. * 获取 zlevel 所在层,如果不存在则会创建一个新的层
  416. * @param {number} zlevel
  417. * @param {boolean} virtual Virtual layer will not be inserted into dom.
  418. * @return {module:zrender/Layer}
  419. */
  420. getLayer: function (zlevel, virtual) {
  421. if (this._singleCanvas && !this._needsManuallyCompositing) {
  422. zlevel = CANVAS_ZLEVEL;
  423. }
  424. var layer = this._layers[zlevel];
  425. if (!layer) {
  426. // Create a new layer
  427. layer = new Layer('zr_' + zlevel, this, this.dpr);
  428. layer.zlevel = zlevel;
  429. layer.__builtin__ = true;
  430. if (this._layerConfig[zlevel]) {
  431. util.merge(layer, this._layerConfig[zlevel], true);
  432. } // TODO Remove EL_AFTER_INCREMENTAL_INC magic number
  433. else if (this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC]) {
  434. util.merge(layer, this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC], true);
  435. }
  436. if (virtual) {
  437. layer.virtual = virtual;
  438. }
  439. this.insertLayer(zlevel, layer); // Context is created after dom inserted to document
  440. // Or excanvas will get 0px clientWidth and clientHeight
  441. layer.initContext();
  442. }
  443. return layer;
  444. },
  445. insertLayer: function (zlevel, layer) {
  446. var layersMap = this._layers;
  447. var zlevelList = this._zlevelList;
  448. var len = zlevelList.length;
  449. var prevLayer = null;
  450. var i = -1;
  451. var domRoot = this._domRoot;
  452. if (layersMap[zlevel]) {
  453. logError('ZLevel ' + zlevel + ' has been used already');
  454. return;
  455. } // Check if is a valid layer
  456. if (!isLayerValid(layer)) {
  457. logError('Layer of zlevel ' + zlevel + ' is not valid');
  458. return;
  459. }
  460. if (len > 0 && zlevel > zlevelList[0]) {
  461. for (i = 0; i < len - 1; i++) {
  462. if (zlevelList[i] < zlevel && zlevelList[i + 1] > zlevel) {
  463. break;
  464. }
  465. }
  466. prevLayer = layersMap[zlevelList[i]];
  467. }
  468. zlevelList.splice(i + 1, 0, zlevel);
  469. layersMap[zlevel] = layer; // Vitual layer will not directly show on the screen.
  470. // (It can be a WebGL layer and assigned to a ZImage element)
  471. // But it still under management of zrender.
  472. if (!layer.virtual) {
  473. if (prevLayer) {
  474. var prevDom = prevLayer.dom;
  475. if (prevDom.nextSibling) {
  476. domRoot.insertBefore(layer.dom, prevDom.nextSibling);
  477. } else {
  478. domRoot.appendChild(layer.dom);
  479. }
  480. } else {
  481. if (domRoot.firstChild) {
  482. domRoot.insertBefore(layer.dom, domRoot.firstChild);
  483. } else {
  484. domRoot.appendChild(layer.dom);
  485. }
  486. }
  487. }
  488. },
  489. // Iterate each layer
  490. eachLayer: function (cb, context) {
  491. var zlevelList = this._zlevelList;
  492. var z;
  493. var i;
  494. for (i = 0; i < zlevelList.length; i++) {
  495. z = zlevelList[i];
  496. cb.call(context, this._layers[z], z);
  497. }
  498. },
  499. // Iterate each buildin layer
  500. eachBuiltinLayer: function (cb, context) {
  501. var zlevelList = this._zlevelList;
  502. var layer;
  503. var z;
  504. var i;
  505. for (i = 0; i < zlevelList.length; i++) {
  506. z = zlevelList[i];
  507. layer = this._layers[z];
  508. if (layer.__builtin__) {
  509. cb.call(context, layer, z);
  510. }
  511. }
  512. },
  513. // Iterate each other layer except buildin layer
  514. eachOtherLayer: function (cb, context) {
  515. var zlevelList = this._zlevelList;
  516. var layer;
  517. var z;
  518. var i;
  519. for (i = 0; i < zlevelList.length; i++) {
  520. z = zlevelList[i];
  521. layer = this._layers[z];
  522. if (!layer.__builtin__) {
  523. cb.call(context, layer, z);
  524. }
  525. }
  526. },
  527. /**
  528. * 获取所有已创建的层
  529. * @param {Array.<module:zrender/Layer>} [prevLayer]
  530. */
  531. getLayers: function () {
  532. return this._layers;
  533. },
  534. _updateLayerStatus: function (list) {
  535. this.eachBuiltinLayer(function (layer, z) {
  536. layer.__dirty = layer.__used = false;
  537. });
  538. function updatePrevLayer(idx) {
  539. if (prevLayer) {
  540. if (prevLayer.__endIndex !== idx) {
  541. prevLayer.__dirty = true;
  542. }
  543. prevLayer.__endIndex = idx;
  544. }
  545. }
  546. if (this._singleCanvas) {
  547. for (var i = 1; i < list.length; i++) {
  548. var el = list[i];
  549. if (el.zlevel !== list[i - 1].zlevel || el.incremental) {
  550. this._needsManuallyCompositing = true;
  551. break;
  552. }
  553. }
  554. }
  555. var prevLayer = null;
  556. var incrementalLayerCount = 0;
  557. var prevZlevel;
  558. for (var i = 0; i < list.length; i++) {
  559. var el = list[i];
  560. var zlevel = el.zlevel;
  561. var layer;
  562. if (prevZlevel !== zlevel) {
  563. prevZlevel = zlevel;
  564. incrementalLayerCount = 0;
  565. } // TODO Not use magic number on zlevel.
  566. // Each layer with increment element can be separated to 3 layers.
  567. // (Other Element drawn after incremental element)
  568. // -----------------zlevel + EL_AFTER_INCREMENTAL_INC--------------------
  569. // (Incremental element)
  570. // ----------------------zlevel + INCREMENTAL_INC------------------------
  571. // (Element drawn before incremental element)
  572. // --------------------------------zlevel--------------------------------
  573. if (el.incremental) {
  574. layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing);
  575. layer.incremental = true;
  576. incrementalLayerCount = 1;
  577. } else {
  578. layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing);
  579. }
  580. if (!layer.__builtin__) {
  581. logError('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id);
  582. }
  583. if (layer !== prevLayer) {
  584. layer.__used = true;
  585. if (layer.__startIndex !== i) {
  586. layer.__dirty = true;
  587. }
  588. layer.__startIndex = i;
  589. if (!layer.incremental) {
  590. layer.__drawIndex = i;
  591. } else {
  592. // Mark layer draw index needs to update.
  593. layer.__drawIndex = -1;
  594. }
  595. updatePrevLayer(i);
  596. prevLayer = layer;
  597. }
  598. if (el.__dirty) {
  599. layer.__dirty = true;
  600. if (layer.incremental && layer.__drawIndex < 0) {
  601. // Start draw from the first dirty element.
  602. layer.__drawIndex = i;
  603. }
  604. }
  605. }
  606. updatePrevLayer(i);
  607. this.eachBuiltinLayer(function (layer, z) {
  608. // Used in last frame but not in this frame. Needs clear
  609. if (!layer.__used && layer.getElementCount() > 0) {
  610. layer.__dirty = true;
  611. layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0;
  612. } // For incremental layer. In case start index changed and no elements are dirty.
  613. if (layer.__dirty && layer.__drawIndex < 0) {
  614. layer.__drawIndex = layer.__startIndex;
  615. }
  616. });
  617. },
  618. /**
  619. * 清除hover层外所有内容
  620. */
  621. clear: function () {
  622. this.eachBuiltinLayer(this._clearLayer);
  623. return this;
  624. },
  625. _clearLayer: function (layer) {
  626. layer.clear();
  627. },
  628. setBackgroundColor: function (backgroundColor) {
  629. this._backgroundColor = backgroundColor;
  630. },
  631. /**
  632. * 修改指定zlevel的绘制参数
  633. *
  634. * @param {string} zlevel
  635. * @param {Object} config 配置对象
  636. * @param {string} [config.clearColor=0] 每次清空画布的颜色
  637. * @param {string} [config.motionBlur=false] 是否开启动态模糊
  638. * @param {number} [config.lastFrameAlpha=0.7]
  639. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  640. */
  641. configLayer: function (zlevel, config) {
  642. if (config) {
  643. var layerConfig = this._layerConfig;
  644. if (!layerConfig[zlevel]) {
  645. layerConfig[zlevel] = config;
  646. } else {
  647. util.merge(layerConfig[zlevel], config, true);
  648. }
  649. for (var i = 0; i < this._zlevelList.length; i++) {
  650. var _zlevel = this._zlevelList[i]; // TODO Remove EL_AFTER_INCREMENTAL_INC magic number
  651. if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) {
  652. var layer = this._layers[_zlevel];
  653. util.merge(layer, layerConfig[zlevel], true);
  654. }
  655. }
  656. }
  657. },
  658. /**
  659. * 删除指定层
  660. * @param {number} zlevel 层所在的zlevel
  661. */
  662. delLayer: function (zlevel) {
  663. var layers = this._layers;
  664. var zlevelList = this._zlevelList;
  665. var layer = layers[zlevel];
  666. if (!layer) {
  667. return;
  668. }
  669. layer.dom.parentNode.removeChild(layer.dom);
  670. delete layers[zlevel];
  671. zlevelList.splice(util.indexOf(zlevelList, zlevel), 1);
  672. },
  673. /**
  674. * 区域大小变化后重绘
  675. */
  676. resize: function (width, height) {
  677. if (!this._domRoot.style) {
  678. // Maybe in node or worker
  679. if (width == null || height == null) {
  680. return;
  681. }
  682. this._width = width;
  683. this._height = height;
  684. this.getLayer(CANVAS_ZLEVEL).resize(width, height);
  685. } else {
  686. var domRoot = this._domRoot; // FIXME Why ?
  687. domRoot.style.display = 'none'; // Save input w/h
  688. var opts = this._opts;
  689. width != null && (opts.width = width);
  690. height != null && (opts.height = height);
  691. width = this._getSize(0);
  692. height = this._getSize(1);
  693. domRoot.style.display = ''; // 优化没有实际改变的resize
  694. if (this._width !== width || height !== this._height) {
  695. domRoot.style.width = width + 'px';
  696. domRoot.style.height = height + 'px';
  697. for (var id in this._layers) {
  698. if (this._layers.hasOwnProperty(id)) {
  699. this._layers[id].resize(width, height);
  700. }
  701. }
  702. util.each(this._progressiveLayers, function (layer) {
  703. layer.resize(width, height);
  704. });
  705. this.refresh(true);
  706. }
  707. this._width = width;
  708. this._height = height;
  709. }
  710. return this;
  711. },
  712. /**
  713. * 清除单独的一个层
  714. * @param {number} zlevel
  715. */
  716. clearLayer: function (zlevel) {
  717. var layer = this._layers[zlevel];
  718. if (layer) {
  719. layer.clear();
  720. }
  721. },
  722. /**
  723. * 释放
  724. */
  725. dispose: function () {
  726. this.root.innerHTML = '';
  727. this.root = this.storage = this._domRoot = this._layers = null;
  728. },
  729. /**
  730. * Get canvas which has all thing rendered
  731. * @param {Object} opts
  732. * @param {string} [opts.backgroundColor]
  733. * @param {number} [opts.pixelRatio]
  734. */
  735. getRenderedCanvas: function (opts) {
  736. opts = opts || {};
  737. if (this._singleCanvas && !this._compositeManually) {
  738. return this._layers[CANVAS_ZLEVEL].dom;
  739. }
  740. var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);
  741. imageLayer.initContext();
  742. imageLayer.clear(false, opts.backgroundColor || this._backgroundColor);
  743. if (opts.pixelRatio <= this.dpr) {
  744. this.refresh();
  745. var width = imageLayer.dom.width;
  746. var height = imageLayer.dom.height;
  747. var ctx = imageLayer.ctx;
  748. this.eachLayer(function (layer) {
  749. if (layer.__builtin__) {
  750. ctx.drawImage(layer.dom, 0, 0, width, height);
  751. } else if (layer.renderToCanvas) {
  752. imageLayer.ctx.save();
  753. layer.renderToCanvas(imageLayer.ctx);
  754. imageLayer.ctx.restore();
  755. }
  756. });
  757. } else {
  758. // PENDING, echarts-gl and incremental rendering.
  759. var scope = {};
  760. var displayList = this.storage.getDisplayList(true);
  761. for (var i = 0; i < displayList.length; i++) {
  762. var el = displayList[i];
  763. this._doPaintEl(el, imageLayer, true, scope);
  764. }
  765. }
  766. return imageLayer.dom;
  767. },
  768. /**
  769. * 获取绘图区域宽度
  770. */
  771. getWidth: function () {
  772. return this._width;
  773. },
  774. /**
  775. * 获取绘图区域高度
  776. */
  777. getHeight: function () {
  778. return this._height;
  779. },
  780. _getSize: function (whIdx) {
  781. var opts = this._opts;
  782. var wh = ['width', 'height'][whIdx];
  783. var cwh = ['clientWidth', 'clientHeight'][whIdx];
  784. var plt = ['paddingLeft', 'paddingTop'][whIdx];
  785. var prb = ['paddingRight', 'paddingBottom'][whIdx];
  786. if (opts[wh] != null && opts[wh] !== 'auto') {
  787. return parseFloat(opts[wh]);
  788. }
  789. var root = this.root; // IE8 does not support getComputedStyle, but it use VML.
  790. var stl = document.defaultView.getComputedStyle(root);
  791. return (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) - (parseInt10(stl[plt]) || 0) - (parseInt10(stl[prb]) || 0) | 0;
  792. },
  793. pathToImage: function (path, dpr) {
  794. dpr = dpr || this.dpr;
  795. var canvas = document.createElement('canvas');
  796. var ctx = canvas.getContext('2d');
  797. var rect = path.getBoundingRect();
  798. var style = path.style;
  799. var shadowBlurSize = style.shadowBlur * dpr;
  800. var shadowOffsetX = style.shadowOffsetX * dpr;
  801. var shadowOffsetY = style.shadowOffsetY * dpr;
  802. var lineWidth = style.hasStroke() ? style.lineWidth : 0;
  803. var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize);
  804. var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize);
  805. var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize);
  806. var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize);
  807. var width = rect.width + leftMargin + rightMargin;
  808. var height = rect.height + topMargin + bottomMargin;
  809. canvas.width = width * dpr;
  810. canvas.height = height * dpr;
  811. ctx.scale(dpr, dpr);
  812. ctx.clearRect(0, 0, width, height);
  813. ctx.dpr = dpr;
  814. var pathTransform = {
  815. position: path.position,
  816. rotation: path.rotation,
  817. scale: path.scale
  818. };
  819. path.position = [leftMargin - rect.x, topMargin - rect.y];
  820. path.rotation = 0;
  821. path.scale = [1, 1];
  822. path.updateTransform();
  823. if (path) {
  824. path.brush(ctx);
  825. }
  826. var ImageShape = Image;
  827. var imgShape = new ImageShape({
  828. style: {
  829. x: 0,
  830. y: 0,
  831. image: canvas
  832. }
  833. });
  834. if (pathTransform.position != null) {
  835. imgShape.position = path.position = pathTransform.position;
  836. }
  837. if (pathTransform.rotation != null) {
  838. imgShape.rotation = path.rotation = pathTransform.rotation;
  839. }
  840. if (pathTransform.scale != null) {
  841. imgShape.scale = path.scale = pathTransform.scale;
  842. }
  843. return imgShape;
  844. }
  845. };
  846. var _default = Painter;
  847. module.exports = _default;