Painter.js 31 KB

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