zrender.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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.txt
  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. type PainterBaseCtor = {
  28. new(dom: HTMLElement, storage: Storage, ...args: any[]): PainterBase
  29. }
  30. const painterCtors: Dictionary<PainterBaseCtor> = {};
  31. let instances: { [key: number]: ZRender } = {};
  32. function delInstance(id: number) {
  33. delete instances[id];
  34. }
  35. function isDarkMode(backgroundColor: string | GradientObject | PatternObject): boolean {
  36. if (!backgroundColor) {
  37. return false;
  38. }
  39. if (typeof backgroundColor === 'string') {
  40. return lum(backgroundColor, 1) < DARK_MODE_THRESHOLD;
  41. }
  42. else if ((backgroundColor as GradientObject).colorStops) {
  43. const colorStops = (backgroundColor as GradientObject).colorStops;
  44. let totalLum = 0;
  45. const len = colorStops.length;
  46. // Simply do the math of average the color. Not consider the offset
  47. for (let i = 0; i < len; i++) {
  48. totalLum += lum(colorStops[i].color, 1);
  49. }
  50. totalLum /= len;
  51. return totalLum < DARK_MODE_THRESHOLD;
  52. }
  53. // Can't determine
  54. return false;
  55. }
  56. class ZRender {
  57. /**
  58. * Not necessary if using SSR painter like svg-ssr
  59. */
  60. dom?: HTMLElement
  61. id: number
  62. storage: Storage
  63. painter: PainterBase
  64. handler: Handler
  65. animation: Animation
  66. private _sleepAfterStill = 10;
  67. private _stillFrameAccum = 0;
  68. private _needsRefresh = true
  69. private _needsRefreshHover = true
  70. /**
  71. * If theme is dark mode. It will determine the color strategy for labels.
  72. */
  73. private _darkMode = false;
  74. private _backgroundColor: string | GradientObject | PatternObject;
  75. constructor(id: number, dom?: HTMLElement, opts?: ZRenderInitOpt) {
  76. opts = opts || {};
  77. /**
  78. * @type {HTMLDomElement}
  79. */
  80. this.dom = dom;
  81. this.id = id;
  82. const storage = new Storage();
  83. let rendererType = opts.renderer || 'canvas';
  84. if (!painterCtors[rendererType]) {
  85. // Use the first registered renderer.
  86. rendererType = zrUtil.keys(painterCtors)[0];
  87. }
  88. if (process.env.NODE_ENV !== 'production') {
  89. if (!painterCtors[rendererType]) {
  90. throw new Error(`Renderer '${rendererType}' is not imported. Please import it first.`);
  91. }
  92. }
  93. opts.useDirtyRect = opts.useDirtyRect == null
  94. ? false
  95. : opts.useDirtyRect;
  96. const painter = new painterCtors[rendererType](dom, storage, opts, id);
  97. const ssrMode = opts.ssr || painter.ssrOnly;
  98. this.storage = storage;
  99. this.painter = painter;
  100. const handerProxy = (!env.node && !env.worker && !ssrMode)
  101. ? new HandlerProxy(painter.getViewportRoot(), painter.root)
  102. : null;
  103. const useCoarsePointer = opts.useCoarsePointer;
  104. const usePointerSize = (useCoarsePointer == null || useCoarsePointer === 'auto')
  105. ? env.touchEventsSupported
  106. : !!useCoarsePointer;
  107. const defaultPointerSize = 44;
  108. let pointerSize;
  109. if (usePointerSize) {
  110. pointerSize = zrUtil.retrieve2(opts.pointerSize, defaultPointerSize);
  111. }
  112. this.handler = new Handler(storage, painter, handerProxy, painter.root, pointerSize);
  113. this.animation = new Animation({
  114. stage: {
  115. update: ssrMode ? null : () => this._flush(true)
  116. }
  117. });
  118. if (!ssrMode) {
  119. this.animation.start();
  120. }
  121. }
  122. /**
  123. * 添加元素
  124. */
  125. add(el: Element) {
  126. if (!el) {
  127. return;
  128. }
  129. this.storage.addRoot(el);
  130. el.addSelfToZr(this);
  131. this.refresh();
  132. }
  133. /**
  134. * 删除元素
  135. */
  136. remove(el: Element) {
  137. if (!el) {
  138. return;
  139. }
  140. this.storage.delRoot(el);
  141. el.removeSelfFromZr(this);
  142. this.refresh();
  143. }
  144. /**
  145. * Change configuration of layer
  146. */
  147. configLayer(zLevel: number, config: LayerConfig) {
  148. if (this.painter.configLayer) {
  149. this.painter.configLayer(zLevel, config);
  150. }
  151. this.refresh();
  152. }
  153. /**
  154. * Set background color
  155. */
  156. setBackgroundColor(backgroundColor: string | GradientObject | PatternObject) {
  157. if (this.painter.setBackgroundColor) {
  158. this.painter.setBackgroundColor(backgroundColor);
  159. }
  160. this.refresh();
  161. this._backgroundColor = backgroundColor;
  162. this._darkMode = isDarkMode(backgroundColor);
  163. }
  164. getBackgroundColor() {
  165. return this._backgroundColor;
  166. }
  167. /**
  168. * Force to set dark mode
  169. */
  170. setDarkMode(darkMode: boolean) {
  171. this._darkMode = darkMode;
  172. }
  173. isDarkMode() {
  174. return this._darkMode;
  175. }
  176. /**
  177. * Repaint the canvas immediately
  178. */
  179. refreshImmediately(fromInside?: boolean) {
  180. // const start = new Date();
  181. if (!fromInside) {
  182. // Update animation if refreshImmediately is invoked from outside.
  183. // Not trigger stage update to call flush again. Which may refresh twice
  184. this.animation.update(true);
  185. }
  186. // Clear needsRefresh ahead to avoid something wrong happens in refresh
  187. // Or it will cause zrender refreshes again and again.
  188. this._needsRefresh = false;
  189. this.painter.refresh();
  190. // Avoid trigger zr.refresh in Element#beforeUpdate hook
  191. this._needsRefresh = false;
  192. }
  193. /**
  194. * Mark and repaint the canvas in the next frame of browser
  195. */
  196. refresh() {
  197. this._needsRefresh = true;
  198. // Active the animation again.
  199. this.animation.start();
  200. }
  201. /**
  202. * Perform all refresh
  203. */
  204. flush() {
  205. this._flush(false);
  206. }
  207. private _flush(fromInside?: boolean) {
  208. let triggerRendered;
  209. const start = getTime();
  210. if (this._needsRefresh) {
  211. triggerRendered = true;
  212. this.refreshImmediately(fromInside);
  213. }
  214. if (this._needsRefreshHover) {
  215. triggerRendered = true;
  216. this.refreshHoverImmediately();
  217. }
  218. const end = getTime();
  219. if (triggerRendered) {
  220. this._stillFrameAccum = 0;
  221. this.trigger('rendered', {
  222. elapsedTime: end - start
  223. } as RenderedEvent);
  224. }
  225. else if (this._sleepAfterStill > 0) {
  226. this._stillFrameAccum++;
  227. // Stop the animiation after still for 10 frames.
  228. if (this._stillFrameAccum > this._sleepAfterStill) {
  229. this.animation.stop();
  230. }
  231. }
  232. }
  233. /**
  234. * Set sleep after still for frames.
  235. * Disable auto sleep when it's 0.
  236. */
  237. setSleepAfterStill(stillFramesCount: number) {
  238. this._sleepAfterStill = stillFramesCount;
  239. }
  240. /**
  241. * Wake up animation loop. But not render.
  242. */
  243. wakeUp() {
  244. this.animation.start();
  245. // Reset the frame count.
  246. this._stillFrameAccum = 0;
  247. }
  248. /**
  249. * Refresh hover in next frame
  250. */
  251. refreshHover() {
  252. this._needsRefreshHover = true;
  253. }
  254. /**
  255. * Refresh hover immediately
  256. */
  257. refreshHoverImmediately() {
  258. this._needsRefreshHover = false;
  259. if (this.painter.refreshHover && this.painter.getType() === 'canvas') {
  260. this.painter.refreshHover();
  261. }
  262. }
  263. /**
  264. * Resize the canvas.
  265. * Should be invoked when container size is changed
  266. */
  267. resize(opts?: {
  268. width?: number| string
  269. height?: number | string
  270. }) {
  271. opts = opts || {};
  272. this.painter.resize(opts.width, opts.height);
  273. this.handler.resize();
  274. }
  275. /**
  276. * Stop and clear all animation immediately
  277. */
  278. clearAnimation() {
  279. this.animation.clear();
  280. }
  281. /**
  282. * Get container width
  283. */
  284. getWidth(): number {
  285. return this.painter.getWidth();
  286. }
  287. /**
  288. * Get container height
  289. */
  290. getHeight(): number {
  291. return this.painter.getHeight();
  292. }
  293. /**
  294. * Set default cursor
  295. * @param cursorStyle='default' 例如 crosshair
  296. */
  297. setCursorStyle(cursorStyle: string) {
  298. this.handler.setCursorStyle(cursorStyle);
  299. }
  300. /**
  301. * Find hovered element
  302. * @param x
  303. * @param y
  304. * @return {target, topTarget}
  305. */
  306. findHover(x: number, y: number): {
  307. target: Displayable
  308. topTarget: Displayable
  309. } {
  310. return this.handler.findHover(x, y);
  311. }
  312. on<Ctx>(eventName: ElementEventName, eventHandler: ElementEventCallback<Ctx, ZRenderType>, context?: Ctx): this
  313. // eslint-disable-next-line max-len
  314. on<Ctx>(eventName: string, eventHandler: WithThisType<EventCallback<any[]>, unknown extends Ctx ? ZRenderType : Ctx>, context?: Ctx): this
  315. // eslint-disable-next-line max-len
  316. on<Ctx>(eventName: string, eventHandler: (...args: any) => any, context?: Ctx): this {
  317. this.handler.on(eventName, eventHandler, context);
  318. return this;
  319. }
  320. /**
  321. * Unbind event
  322. * @param eventName Event name
  323. * @param eventHandler Handler function
  324. */
  325. // eslint-disable-next-line max-len
  326. off(eventName?: string, eventHandler?: EventCallback) {
  327. this.handler.off(eventName, eventHandler);
  328. }
  329. /**
  330. * Trigger event manually
  331. *
  332. * @param eventName Event name
  333. * @param event Event object
  334. */
  335. trigger(eventName: string, event?: unknown) {
  336. this.handler.trigger(eventName, event);
  337. }
  338. /**
  339. * Clear all objects and the canvas.
  340. */
  341. clear() {
  342. const roots = this.storage.getRoots();
  343. for (let i = 0; i < roots.length; i++) {
  344. if (roots[i] instanceof Group) {
  345. roots[i].removeSelfFromZr(this);
  346. }
  347. }
  348. this.storage.delAllRoots();
  349. this.painter.clear();
  350. }
  351. /**
  352. * Dispose self.
  353. */
  354. dispose() {
  355. this.animation.stop();
  356. this.clear();
  357. this.storage.dispose();
  358. this.painter.dispose();
  359. this.handler.dispose();
  360. this.animation =
  361. this.storage =
  362. this.painter =
  363. this.handler = null;
  364. delInstance(this.id);
  365. }
  366. }
  367. export interface ZRenderInitOpt {
  368. renderer?: string // 'canvas' or 'svg
  369. devicePixelRatio?: number
  370. width?: number | string // 10, 10px, 'auto'
  371. height?: number | string
  372. useDirtyRect?: boolean
  373. useCoarsePointer?: 'auto' | boolean
  374. pointerSize?: number
  375. ssr?: boolean // If enable ssr mode.
  376. }
  377. /**
  378. * Initializing a zrender instance
  379. *
  380. * @param dom Not necessary if using SSR painter like svg-ssr
  381. */
  382. export function init(dom?: HTMLElement | null, opts?: ZRenderInitOpt) {
  383. const zr = new ZRender(zrUtil.guid(), dom, opts);
  384. instances[zr.id] = zr;
  385. return zr;
  386. }
  387. /**
  388. * Dispose zrender instance
  389. */
  390. export function dispose(zr: ZRender) {
  391. zr.dispose();
  392. }
  393. /**
  394. * Dispose all zrender instances
  395. */
  396. export function disposeAll() {
  397. for (let key in instances) {
  398. if (instances.hasOwnProperty(key)) {
  399. instances[key].dispose();
  400. }
  401. }
  402. instances = {};
  403. }
  404. /**
  405. * Get zrender instance by id
  406. */
  407. export function getInstance(id: number): ZRender {
  408. return instances[id];
  409. }
  410. export function registerPainter(name: string, Ctor: PainterBaseCtor) {
  411. painterCtors[name] = Ctor;
  412. }
  413. /**
  414. * @type {string}
  415. */
  416. export const version = '5.4.3';
  417. export interface ZRenderType extends ZRender {};