zrender.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. /*!
  2. * ZRender, a high performance 2d drawing library.
  3. *
  4. * Copyright (c) 2013, Baidu Inc.
  5. * All rights reserved.
  6. *
  7. * LICENSE
  8. * https://github.com/ecomfe/zrender/blob/master/LICENSE
  9. */
  10. import env from './core/env';
  11. import * as zrUtil from './core/util';
  12. import Handler from './Handler';
  13. import Storage from './Storage';
  14. import {PainterBase} from './PainterBase';
  15. import Animation, {getTime} from './animation/Animation';
  16. import HandlerProxy from './dom/HandlerProxy';
  17. import Element, { ElementEventCallback } from './Element';
  18. import { Dictionary, ElementEventName, RenderedEvent, WithThisType } from './core/types';
  19. import { LayerConfig } from './canvas/Layer';
  20. import { GradientObject } from './graphic/Gradient';
  21. import { PatternObject } from './graphic/Pattern';
  22. import { EventCallback } from './core/Eventful';
  23. import Displayable from './graphic/Displayable';
  24. import { lum } from './tool/color';
  25. import { DARK_MODE_THRESHOLD } from './config';
  26. import Group from './graphic/Group';
  27. import { CanvasPainterRefreshOpt } from './canvas/Painter';
  28. type PainterBaseCtor = {
  29. new(dom: HTMLElement, storage: Storage, ...args: any[]): PainterBase
  30. }
  31. const painterCtors: Dictionary<PainterBaseCtor> = {};
  32. let instances: { [key: number]: ZRender } = {};
  33. function delInstance(id: number) {
  34. delete instances[id];
  35. }
  36. function isDarkMode(backgroundColor: string | GradientObject | PatternObject): boolean {
  37. if (!backgroundColor) {
  38. return false;
  39. }
  40. if (typeof backgroundColor === 'string') {
  41. return lum(backgroundColor, 1) < DARK_MODE_THRESHOLD;
  42. }
  43. else if ((backgroundColor as GradientObject).colorStops) {
  44. const colorStops = (backgroundColor as GradientObject).colorStops;
  45. let totalLum = 0;
  46. const len = colorStops.length;
  47. // Simply do the math of average the color. Not consider the offset
  48. for (let i = 0; i < len; i++) {
  49. totalLum += lum(colorStops[i].color, 1);
  50. }
  51. totalLum /= len;
  52. return totalLum < DARK_MODE_THRESHOLD;
  53. }
  54. // Can't determine
  55. return false;
  56. }
  57. class ZRender {
  58. /**
  59. * Not necessary if using SSR painter like svg-ssr
  60. */
  61. dom?: HTMLElement
  62. id: number
  63. storage: Storage
  64. painter: PainterBase
  65. handler: Handler
  66. animation: Animation
  67. private _sleepAfterStill = 10;
  68. private _stillFrameAccum = 0;
  69. private _needsRefresh = true
  70. // true can lead to creating a hover layer. Do not set true unless required.
  71. private _needsRefreshHover = false
  72. private _disposed: boolean;
  73. /**
  74. * If theme is dark mode. It will determine the color strategy for labels.
  75. */
  76. private _darkMode = false;
  77. private _backgroundColor: string | GradientObject | PatternObject;
  78. constructor(id: number, dom?: HTMLElement, opts?: ZRenderInitOpt) {
  79. opts = opts || {};
  80. /**
  81. * @type {HTMLDomElement}
  82. */
  83. this.dom = dom;
  84. this.id = id;
  85. const storage = new Storage();
  86. let rendererType = opts.renderer || 'canvas';
  87. if (!painterCtors[rendererType]) {
  88. // Use the first registered renderer.
  89. rendererType = zrUtil.keys(painterCtors)[0];
  90. }
  91. if (process.env.NODE_ENV !== 'production') {
  92. if (!painterCtors[rendererType]) {
  93. throw new Error(`Renderer '${rendererType}' is not imported. Please import it first.`);
  94. }
  95. }
  96. opts.useDirtyRect = opts.useDirtyRect == null
  97. ? false
  98. : opts.useDirtyRect;
  99. const painter = new painterCtors[rendererType](dom, storage, opts, id);
  100. const ssrMode = opts.ssr || painter.ssrOnly;
  101. this.storage = storage;
  102. this.painter = painter;
  103. const handlerProxy = (!env.node && !env.worker && !ssrMode)
  104. ? new HandlerProxy(painter.getViewportRoot(), painter.root)
  105. : null;
  106. const useCoarsePointer = opts.useCoarsePointer;
  107. const usePointerSize = (useCoarsePointer == null || useCoarsePointer === 'auto')
  108. ? env.touchEventsSupported
  109. : !!useCoarsePointer;
  110. const defaultPointerSize = 44;
  111. let pointerSize;
  112. if (usePointerSize) {
  113. pointerSize = zrUtil.retrieve2(opts.pointerSize, defaultPointerSize);
  114. }
  115. this.handler = new Handler(storage, painter, handlerProxy, painter.root, pointerSize);
  116. this.animation = new Animation({
  117. stage: {
  118. update: ssrMode ? null : () => this._flush(false)
  119. }
  120. });
  121. if (!ssrMode) {
  122. this.animation.start();
  123. }
  124. }
  125. /**
  126. * 添加元素
  127. */
  128. add(el: Element) {
  129. if (this._disposed || !el) {
  130. return;
  131. }
  132. this.storage.addRoot(el);
  133. el.addSelfToZr(this);
  134. this.refresh();
  135. }
  136. /**
  137. * 删除元素
  138. */
  139. remove(el: Element) {
  140. if (this._disposed || !el) {
  141. return;
  142. }
  143. this.storage.delRoot(el);
  144. el.removeSelfFromZr(this);
  145. this.refresh();
  146. }
  147. /**
  148. * Change configuration of layer
  149. */
  150. configLayer(zLevel: number, config: LayerConfig) {
  151. if (this._disposed) {
  152. return;
  153. }
  154. if (this.painter.configLayer) {
  155. this.painter.configLayer(zLevel, config);
  156. }
  157. this.refresh();
  158. }
  159. /**
  160. * Set background color
  161. */
  162. setBackgroundColor(backgroundColor: string | GradientObject | PatternObject) {
  163. if (this._disposed) {
  164. return;
  165. }
  166. if (this.painter.setBackgroundColor) {
  167. this.painter.setBackgroundColor(backgroundColor);
  168. }
  169. this.refresh();
  170. this._backgroundColor = backgroundColor;
  171. this._darkMode = isDarkMode(backgroundColor);
  172. }
  173. getBackgroundColor() {
  174. return this._backgroundColor;
  175. }
  176. /**
  177. * Force to set dark mode
  178. */
  179. setDarkMode(darkMode: boolean) {
  180. this._darkMode = darkMode;
  181. }
  182. isDarkMode() {
  183. return this._darkMode;
  184. }
  185. /**
  186. * Repaint the canvas immediately
  187. */
  188. refreshImmediately(noAnimationUpdate?: boolean) {
  189. if (this._disposed) {
  190. return;
  191. }
  192. this._refresh({
  193. animUpdate: !noAnimationUpdate,
  194. refresh: true,
  195. refreshHover: false,
  196. });
  197. }
  198. private _refresh(opt: {
  199. animUpdate: boolean;
  200. refresh: CanvasPainterRefreshOpt['refresh']; // work for only CanvasPainter
  201. refreshHover: CanvasPainterRefreshOpt['refreshHover']; // work for only CanvasPainter
  202. }) {
  203. if (opt.animUpdate) {
  204. // Update animation if refreshImmediately is invoked from outside.
  205. // Not trigger stage update to call flush again. Which may refresh twice
  206. this.animation.update(true);
  207. }
  208. // Clear needsRefresh ahead to avoid something wrong happens in refresh
  209. // Or it will cause zrender refreshes again and again.
  210. this._needsRefresh = this._needsRefreshHover = false;
  211. this.painter.refresh({
  212. refresh: opt.refresh,
  213. refreshHover: opt.refreshHover,
  214. });
  215. // Avoid trigger zr.refresh in Element#beforeUpdate hook.
  216. // Hover layer is always refreshed when refreshing normal layers.
  217. this._needsRefresh = this._needsRefreshHover = false;
  218. }
  219. /**
  220. * Mark and repaint the canvas in the next frame of browser
  221. */
  222. refresh() {
  223. if (this._disposed) {
  224. return;
  225. }
  226. this._needsRefresh = true;
  227. // Active the animation again.
  228. this.animation.start();
  229. }
  230. /**
  231. * Perform all refresh
  232. */
  233. flush() {
  234. if (this._disposed) {
  235. return;
  236. }
  237. this._flush(true);
  238. }
  239. private _flush(animationUpdate: boolean) {
  240. let triggerRendered;
  241. const start = getTime();
  242. const needsRefresh = this._needsRefresh;
  243. const needsRefreshHover = this._needsRefreshHover;
  244. if (needsRefresh || needsRefreshHover) {
  245. triggerRendered = true;
  246. this._refresh({
  247. animUpdate: animationUpdate,
  248. refresh: needsRefresh,
  249. refreshHover: needsRefreshHover,
  250. });
  251. }
  252. const end = getTime();
  253. if (triggerRendered) {
  254. this._stillFrameAccum = 0;
  255. this.trigger('rendered', {
  256. elapsedTime: end - start
  257. } as RenderedEvent);
  258. }
  259. else if (this._sleepAfterStill > 0) {
  260. this._stillFrameAccum++;
  261. // Stop the animation after still for 10 frames.
  262. if (this._stillFrameAccum > this._sleepAfterStill) {
  263. this.animation.stop();
  264. }
  265. }
  266. }
  267. /**
  268. * Set sleep after still for frames.
  269. * Disable auto sleep when it's 0.
  270. */
  271. setSleepAfterStill(stillFramesCount: number) {
  272. this._sleepAfterStill = stillFramesCount;
  273. }
  274. /**
  275. * Wake up animation loop. But not render.
  276. */
  277. wakeUp() {
  278. if (this._disposed) {
  279. return;
  280. }
  281. this.animation.start();
  282. // Reset the frame count.
  283. this._stillFrameAccum = 0;
  284. }
  285. /**
  286. * Refresh hover in next frame
  287. */
  288. refreshHover() {
  289. this._needsRefreshHover = true;
  290. }
  291. /**
  292. * @deprecated
  293. * Refresh hover immediately
  294. */
  295. refreshHoverImmediately() {
  296. if (this._disposed) {
  297. return;
  298. }
  299. this._refresh({
  300. animUpdate: false,
  301. refresh: false,
  302. refreshHover: true
  303. });
  304. }
  305. /**
  306. * Resize the canvas.
  307. * Should be invoked when container size is changed
  308. */
  309. resize(opts?: {
  310. width?: number| string
  311. height?: number | string
  312. }) {
  313. if (this._disposed) {
  314. return;
  315. }
  316. opts = opts || {};
  317. this.painter.resize(opts.width, opts.height);
  318. this.handler.resize();
  319. }
  320. /**
  321. * Stop and clear all animation immediately
  322. */
  323. clearAnimation() {
  324. if (this._disposed) {
  325. return;
  326. }
  327. this.animation.clear();
  328. }
  329. /**
  330. * Get container width
  331. */
  332. getWidth(): number | undefined {
  333. if (this._disposed) {
  334. return;
  335. }
  336. return this.painter.getWidth();
  337. }
  338. /**
  339. * Get container height
  340. */
  341. getHeight(): number | undefined {
  342. if (this._disposed) {
  343. return;
  344. }
  345. return this.painter.getHeight();
  346. }
  347. /**
  348. * Set default cursor
  349. * @param cursorStyle='default' 例如 crosshair
  350. */
  351. setCursorStyle(cursorStyle: string) {
  352. if (this._disposed) {
  353. return;
  354. }
  355. this.handler.setCursorStyle(cursorStyle);
  356. }
  357. /**
  358. * Find hovered element
  359. * @param x
  360. * @param y
  361. * @return {target, topTarget}
  362. */
  363. findHover(x: number, y: number): {
  364. target: Displayable
  365. topTarget: Displayable
  366. } | undefined {
  367. if (this._disposed) {
  368. return;
  369. }
  370. return this.handler.findHover(x, y);
  371. }
  372. on<Ctx>(eventName: ElementEventName, eventHandler: ElementEventCallback<Ctx, ZRenderType>, context?: Ctx): this
  373. // eslint-disable-next-line max-len
  374. on<Ctx>(eventName: string, eventHandler: WithThisType<EventCallback<any[]>, unknown extends Ctx ? ZRenderType : Ctx>, context?: Ctx): this
  375. // eslint-disable-next-line max-len
  376. on<Ctx>(eventName: string, eventHandler: (...args: any) => any, context?: Ctx): this {
  377. if (!this._disposed) {
  378. this.handler.on(eventName, eventHandler, context);
  379. }
  380. return this;
  381. }
  382. /**
  383. * Unbind event
  384. * @param eventName Event name
  385. * @param eventHandler Handler function
  386. */
  387. // eslint-disable-next-line max-len
  388. off(eventName?: string, eventHandler?: EventCallback) {
  389. if (this._disposed) {
  390. return;
  391. }
  392. this.handler.off(eventName, eventHandler);
  393. }
  394. /**
  395. * Trigger event manually
  396. *
  397. * @param eventName Event name
  398. * @param event Event object
  399. */
  400. trigger(eventName: string, event?: unknown) {
  401. if (this._disposed) {
  402. return;
  403. }
  404. this.handler.trigger(eventName, event);
  405. }
  406. /**
  407. * Clear all objects and the canvas.
  408. */
  409. clear() {
  410. if (this._disposed) {
  411. return;
  412. }
  413. const roots = this.storage.getRoots();
  414. for (let i = 0; i < roots.length; i++) {
  415. if (roots[i] instanceof Group) {
  416. roots[i].removeSelfFromZr(this);
  417. }
  418. }
  419. this.storage.delAllRoots();
  420. this.painter.clear();
  421. }
  422. /**
  423. * Dispose self.
  424. */
  425. dispose() {
  426. if (this._disposed) {
  427. return;
  428. }
  429. this.animation.stop();
  430. this.clear();
  431. this.storage.dispose();
  432. this.painter.dispose();
  433. this.handler.dispose();
  434. this.animation =
  435. this.storage =
  436. this.painter =
  437. this.handler = null;
  438. this._disposed = true;
  439. delInstance(this.id);
  440. }
  441. }
  442. export interface ZRenderInitOpt {
  443. renderer?: string // 'canvas' or 'svg
  444. devicePixelRatio?: number
  445. width?: number | string // 10, 10px, 'auto'
  446. height?: number | string
  447. useDirtyRect?: boolean
  448. useCoarsePointer?: 'auto' | boolean
  449. pointerSize?: number
  450. ssr?: boolean // If enable ssr mode.
  451. }
  452. /**
  453. * Initializing a zrender instance
  454. *
  455. * @param dom Not necessary if using SSR painter like svg-ssr
  456. */
  457. export function init(dom?: HTMLElement | null, opts?: ZRenderInitOpt) {
  458. const zr = new ZRender(zrUtil.guid(), dom, opts);
  459. instances[zr.id] = zr;
  460. return zr;
  461. }
  462. /**
  463. * Dispose zrender instance
  464. */
  465. export function dispose(zr: ZRender) {
  466. zr.dispose();
  467. }
  468. /**
  469. * Dispose all zrender instances
  470. */
  471. export function disposeAll() {
  472. for (let key in instances) {
  473. if (instances.hasOwnProperty(key)) {
  474. instances[key].dispose();
  475. }
  476. }
  477. instances = {};
  478. }
  479. /**
  480. * Get zrender instance by id
  481. */
  482. export function getInstance(id: number): ZRender {
  483. return instances[id];
  484. }
  485. export function registerPainter(name: string, Ctor: PainterBaseCtor) {
  486. painterCtors[name] = Ctor;
  487. }
  488. export type ElementSSRData = zrUtil.HashMap<unknown>;
  489. export type ElementSSRDataGetter<T> = (el: Element) => zrUtil.HashMap<T>;
  490. let ssrDataGetter: ElementSSRDataGetter<unknown>;
  491. export function getElementSSRData(el: Element): ElementSSRData {
  492. if (typeof ssrDataGetter === 'function') {
  493. return ssrDataGetter(el);
  494. }
  495. }
  496. export function registerSSRDataGetter<T>(getter: ElementSSRDataGetter<T>) {
  497. ssrDataGetter = getter;
  498. }
  499. /**
  500. * @type {string}
  501. */
  502. export const version = '6.1.0';
  503. export interface ZRenderType extends ZRender {};