b2f81ec58bd5c27ab3fc07b66e5446bf72beec042a7bf26e1a2e72d27a8f1979cfdf46c73738cb95ddcf9401ca49e6db43f0b10e605b7e0c8ae632861f237a 29 KB

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