Painter.js 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  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. }
  433. if (virtual) {
  434. layer.virtual = virtual;
  435. }
  436. this.insertLayer(zlevel, layer); // Context is created after dom inserted to document
  437. // Or excanvas will get 0px clientWidth and clientHeight
  438. layer.initContext();
  439. }
  440. return layer;
  441. },
  442. insertLayer: function (zlevel, layer) {
  443. var layersMap = this._layers;
  444. var zlevelList = this._zlevelList;
  445. var len = zlevelList.length;
  446. var prevLayer = null;
  447. var i = -1;
  448. var domRoot = this._domRoot;
  449. if (layersMap[zlevel]) {
  450. logError('ZLevel ' + zlevel + ' has been used already');
  451. return;
  452. } // Check if is a valid layer
  453. if (!isLayerValid(layer)) {
  454. logError('Layer of zlevel ' + zlevel + ' is not valid');
  455. return;
  456. }
  457. if (len > 0 && zlevel > zlevelList[0]) {
  458. for (i = 0; i < len - 1; i++) {
  459. if (zlevelList[i] < zlevel && zlevelList[i + 1] > zlevel) {
  460. break;
  461. }
  462. }
  463. prevLayer = layersMap[zlevelList[i]];
  464. }
  465. zlevelList.splice(i + 1, 0, zlevel);
  466. layersMap[zlevel] = layer; // Vitual layer will not directly show on the screen.
  467. // (It can be a WebGL layer and assigned to a ZImage element)
  468. // But it still under management of zrender.
  469. if (!layer.virtual) {
  470. if (prevLayer) {
  471. var prevDom = prevLayer.dom;
  472. if (prevDom.nextSibling) {
  473. domRoot.insertBefore(layer.dom, prevDom.nextSibling);
  474. } else {
  475. domRoot.appendChild(layer.dom);
  476. }
  477. } else {
  478. if (domRoot.firstChild) {
  479. domRoot.insertBefore(layer.dom, domRoot.firstChild);
  480. } else {
  481. domRoot.appendChild(layer.dom);
  482. }
  483. }
  484. }
  485. },
  486. // Iterate each layer
  487. eachLayer: function (cb, context) {
  488. var zlevelList = this._zlevelList;
  489. var z;
  490. var i;
  491. for (i = 0; i < zlevelList.length; i++) {
  492. z = zlevelList[i];
  493. cb.call(context, this._layers[z], z);
  494. }
  495. },
  496. // Iterate each buildin layer
  497. eachBuiltinLayer: function (cb, context) {
  498. var zlevelList = this._zlevelList;
  499. var layer;
  500. var z;
  501. var i;
  502. for (i = 0; i < zlevelList.length; i++) {
  503. z = zlevelList[i];
  504. layer = this._layers[z];
  505. if (layer.__builtin__) {
  506. cb.call(context, layer, z);
  507. }
  508. }
  509. },
  510. // Iterate each other layer except buildin layer
  511. eachOtherLayer: function (cb, context) {
  512. var zlevelList = this._zlevelList;
  513. var layer;
  514. var z;
  515. var i;
  516. for (i = 0; i < zlevelList.length; i++) {
  517. z = zlevelList[i];
  518. layer = this._layers[z];
  519. if (!layer.__builtin__) {
  520. cb.call(context, layer, z);
  521. }
  522. }
  523. },
  524. /**
  525. * 获取所有已创建的层
  526. * @param {Array.<module:zrender/Layer>} [prevLayer]
  527. */
  528. getLayers: function () {
  529. return this._layers;
  530. },
  531. _updateLayerStatus: function (list) {
  532. this.eachBuiltinLayer(function (layer, z) {
  533. layer.__dirty = layer.__used = false;
  534. });
  535. function updatePrevLayer(idx) {
  536. if (prevLayer) {
  537. if (prevLayer.__endIndex !== idx) {
  538. prevLayer.__dirty = true;
  539. }
  540. prevLayer.__endIndex = idx;
  541. }
  542. }
  543. if (this._singleCanvas) {
  544. for (var i = 1; i < list.length; i++) {
  545. var el = list[i];
  546. if (el.zlevel !== list[i - 1].zlevel || el.incremental) {
  547. this._needsManuallyCompositing = true;
  548. break;
  549. }
  550. }
  551. }
  552. var prevLayer = null;
  553. var incrementalLayerCount = 0;
  554. for (var i = 0; i < list.length; i++) {
  555. var el = list[i];
  556. var zlevel = el.zlevel;
  557. var layer; // PENDING If change one incremental element style ?
  558. // TODO Where there are non-incremental elements between incremental elements.
  559. if (el.incremental) {
  560. layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing);
  561. layer.incremental = true;
  562. incrementalLayerCount = 1;
  563. } else {
  564. layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing);
  565. }
  566. if (!layer.__builtin__) {
  567. logError('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id);
  568. }
  569. if (layer !== prevLayer) {
  570. layer.__used = true;
  571. if (layer.__startIndex !== i) {
  572. layer.__dirty = true;
  573. }
  574. layer.__startIndex = i;
  575. if (!layer.incremental) {
  576. layer.__drawIndex = i;
  577. } else {
  578. // Mark layer draw index needs to update.
  579. layer.__drawIndex = -1;
  580. }
  581. updatePrevLayer(i);
  582. prevLayer = layer;
  583. }
  584. if (el.__dirty) {
  585. layer.__dirty = true;
  586. if (layer.incremental && layer.__drawIndex < 0) {
  587. // Start draw from the first dirty element.
  588. layer.__drawIndex = i;
  589. }
  590. }
  591. }
  592. updatePrevLayer(i);
  593. this.eachBuiltinLayer(function (layer, z) {
  594. // Used in last frame but not in this frame. Needs clear
  595. if (!layer.__used && layer.getElementCount() > 0) {
  596. layer.__dirty = true;
  597. layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0;
  598. } // For incremental layer. In case start index changed and no elements are dirty.
  599. if (layer.__dirty && layer.__drawIndex < 0) {
  600. layer.__drawIndex = layer.__startIndex;
  601. }
  602. });
  603. },
  604. /**
  605. * 清除hover层外所有内容
  606. */
  607. clear: function () {
  608. this.eachBuiltinLayer(this._clearLayer);
  609. return this;
  610. },
  611. _clearLayer: function (layer) {
  612. layer.clear();
  613. },
  614. setBackgroundColor: function (backgroundColor) {
  615. this._backgroundColor = backgroundColor;
  616. },
  617. /**
  618. * 修改指定zlevel的绘制参数
  619. *
  620. * @param {string} zlevel
  621. * @param {Object} config 配置对象
  622. * @param {string} [config.clearColor=0] 每次清空画布的颜色
  623. * @param {string} [config.motionBlur=false] 是否开启动态模糊
  624. * @param {number} [config.lastFrameAlpha=0.7]
  625. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  626. */
  627. configLayer: function (zlevel, config) {
  628. if (config) {
  629. var layerConfig = this._layerConfig;
  630. if (!layerConfig[zlevel]) {
  631. layerConfig[zlevel] = config;
  632. } else {
  633. util.merge(layerConfig[zlevel], config, true);
  634. }
  635. for (var i = 0; i < this._zlevelList.length; i++) {
  636. var _zlevel = this._zlevelList[i];
  637. if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) {
  638. var layer = this._layers[_zlevel];
  639. util.merge(layer, layerConfig[zlevel], true);
  640. }
  641. }
  642. }
  643. },
  644. /**
  645. * 删除指定层
  646. * @param {number} zlevel 层所在的zlevel
  647. */
  648. delLayer: function (zlevel) {
  649. var layers = this._layers;
  650. var zlevelList = this._zlevelList;
  651. var layer = layers[zlevel];
  652. if (!layer) {
  653. return;
  654. }
  655. layer.dom.parentNode.removeChild(layer.dom);
  656. delete layers[zlevel];
  657. zlevelList.splice(util.indexOf(zlevelList, zlevel), 1);
  658. },
  659. /**
  660. * 区域大小变化后重绘
  661. */
  662. resize: function (width, height) {
  663. if (!this._domRoot.style) {
  664. // Maybe in node or worker
  665. if (width == null || height == null) {
  666. return;
  667. }
  668. this._width = width;
  669. this._height = height;
  670. this.getLayer(CANVAS_ZLEVEL).resize(width, height);
  671. } else {
  672. var domRoot = this._domRoot; // FIXME Why ?
  673. domRoot.style.display = 'none'; // Save input w/h
  674. var opts = this._opts;
  675. width != null && (opts.width = width);
  676. height != null && (opts.height = height);
  677. width = this._getSize(0);
  678. height = this._getSize(1);
  679. domRoot.style.display = ''; // 优化没有实际改变的resize
  680. if (this._width !== width || height !== this._height) {
  681. domRoot.style.width = width + 'px';
  682. domRoot.style.height = height + 'px';
  683. for (var id in this._layers) {
  684. if (this._layers.hasOwnProperty(id)) {
  685. this._layers[id].resize(width, height);
  686. }
  687. }
  688. util.each(this._progressiveLayers, function (layer) {
  689. layer.resize(width, height);
  690. });
  691. this.refresh(true);
  692. }
  693. this._width = width;
  694. this._height = height;
  695. }
  696. return this;
  697. },
  698. /**
  699. * 清除单独的一个层
  700. * @param {number} zlevel
  701. */
  702. clearLayer: function (zlevel) {
  703. var layer = this._layers[zlevel];
  704. if (layer) {
  705. layer.clear();
  706. }
  707. },
  708. /**
  709. * 释放
  710. */
  711. dispose: function () {
  712. this.root.innerHTML = '';
  713. this.root = this.storage = this._domRoot = this._layers = null;
  714. },
  715. /**
  716. * Get canvas which has all thing rendered
  717. * @param {Object} opts
  718. * @param {string} [opts.backgroundColor]
  719. * @param {number} [opts.pixelRatio]
  720. */
  721. getRenderedCanvas: function (opts) {
  722. opts = opts || {};
  723. if (this._singleCanvas && !this._compositeManually) {
  724. return this._layers[CANVAS_ZLEVEL].dom;
  725. }
  726. var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);
  727. imageLayer.initContext();
  728. imageLayer.clear(false, opts.backgroundColor || this._backgroundColor);
  729. if (opts.pixelRatio <= this.dpr) {
  730. this.refresh();
  731. var width = imageLayer.dom.width;
  732. var height = imageLayer.dom.height;
  733. var ctx = imageLayer.ctx;
  734. this.eachLayer(function (layer) {
  735. if (layer.__builtin__) {
  736. ctx.drawImage(layer.dom, 0, 0, width, height);
  737. } else if (layer.renderToCanvas) {
  738. imageLayer.ctx.save();
  739. layer.renderToCanvas(imageLayer.ctx);
  740. imageLayer.ctx.restore();
  741. }
  742. });
  743. } else {
  744. // PENDING, echarts-gl and incremental rendering.
  745. var scope = {};
  746. var displayList = this.storage.getDisplayList(true);
  747. for (var i = 0; i < displayList.length; i++) {
  748. var el = displayList[i];
  749. this._doPaintEl(el, imageLayer, true, scope);
  750. }
  751. }
  752. return imageLayer.dom;
  753. },
  754. /**
  755. * 获取绘图区域宽度
  756. */
  757. getWidth: function () {
  758. return this._width;
  759. },
  760. /**
  761. * 获取绘图区域高度
  762. */
  763. getHeight: function () {
  764. return this._height;
  765. },
  766. _getSize: function (whIdx) {
  767. var opts = this._opts;
  768. var wh = ['width', 'height'][whIdx];
  769. var cwh = ['clientWidth', 'clientHeight'][whIdx];
  770. var plt = ['paddingLeft', 'paddingTop'][whIdx];
  771. var prb = ['paddingRight', 'paddingBottom'][whIdx];
  772. if (opts[wh] != null && opts[wh] !== 'auto') {
  773. return parseFloat(opts[wh]);
  774. }
  775. var root = this.root; // IE8 does not support getComputedStyle, but it use VML.
  776. var stl = document.defaultView.getComputedStyle(root);
  777. return (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) - (parseInt10(stl[plt]) || 0) - (parseInt10(stl[prb]) || 0) | 0;
  778. },
  779. pathToImage: function (path, dpr) {
  780. dpr = dpr || this.dpr;
  781. var canvas = document.createElement('canvas');
  782. var ctx = canvas.getContext('2d');
  783. var rect = path.getBoundingRect();
  784. var style = path.style;
  785. var shadowBlurSize = style.shadowBlur * dpr;
  786. var shadowOffsetX = style.shadowOffsetX * dpr;
  787. var shadowOffsetY = style.shadowOffsetY * dpr;
  788. var lineWidth = style.hasStroke() ? style.lineWidth : 0;
  789. var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize);
  790. var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize);
  791. var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize);
  792. var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize);
  793. var width = rect.width + leftMargin + rightMargin;
  794. var height = rect.height + topMargin + bottomMargin;
  795. canvas.width = width * dpr;
  796. canvas.height = height * dpr;
  797. ctx.scale(dpr, dpr);
  798. ctx.clearRect(0, 0, width, height);
  799. ctx.dpr = dpr;
  800. var pathTransform = {
  801. position: path.position,
  802. rotation: path.rotation,
  803. scale: path.scale
  804. };
  805. path.position = [leftMargin - rect.x, topMargin - rect.y];
  806. path.rotation = 0;
  807. path.scale = [1, 1];
  808. path.updateTransform();
  809. if (path) {
  810. path.brush(ctx);
  811. }
  812. var ImageShape = Image;
  813. var imgShape = new ImageShape({
  814. style: {
  815. x: 0,
  816. y: 0,
  817. image: canvas
  818. }
  819. });
  820. if (pathTransform.position != null) {
  821. imgShape.position = path.position = pathTransform.position;
  822. }
  823. if (pathTransform.rotation != null) {
  824. imgShape.rotation = path.rotation = pathTransform.rotation;
  825. }
  826. if (pathTransform.scale != null) {
  827. imgShape.scale = path.scale = pathTransform.scale;
  828. }
  829. return imgShape;
  830. }
  831. };
  832. var _default = Painter;
  833. module.exports = _default;