graphic.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. import { DEFAULT_COMMON_STYLE } from '../graphic/Displayable';
  2. import PathProxy from '../core/PathProxy';
  3. import { createOrUpdateImage, isImageReady } from '../graphic/helper/image';
  4. import { getCanvasGradient, isClipPathChanged } from './helper';
  5. import Path from '../graphic/Path';
  6. import ZRImage from '../graphic/Image';
  7. import TSpan from '../graphic/TSpan';
  8. import { DEFAULT_FONT } from '../contain/text';
  9. import { map } from '../core/util';
  10. import { normalizeLineDash } from '../graphic/helper/dashStyle';
  11. import IncrementalDisplayable from '../graphic/IncrementalDisplayable';
  12. import { REDRAW_BIT, SHAPE_CHANGED_BIT } from '../graphic/constants';
  13. var pathProxyForDraw = new PathProxy(true);
  14. function styleHasStroke(style) {
  15. var stroke = style.stroke;
  16. return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));
  17. }
  18. function isValidStrokeFillStyle(strokeOrFill) {
  19. return typeof strokeOrFill === 'string' && strokeOrFill !== 'none';
  20. }
  21. function styleHasFill(style) {
  22. var fill = style.fill;
  23. return fill != null && fill !== 'none';
  24. }
  25. function doFillPath(ctx, style) {
  26. if (style.fillOpacity != null && style.fillOpacity !== 1) {
  27. var originalGlobalAlpha = ctx.globalAlpha;
  28. ctx.globalAlpha = style.fillOpacity * style.opacity;
  29. ctx.fill();
  30. ctx.globalAlpha = originalGlobalAlpha;
  31. }
  32. else {
  33. ctx.fill();
  34. }
  35. }
  36. function doStrokePath(ctx, style) {
  37. if (style.strokeOpacity != null && style.strokeOpacity !== 1) {
  38. var originalGlobalAlpha = ctx.globalAlpha;
  39. ctx.globalAlpha = style.strokeOpacity * style.opacity;
  40. ctx.stroke();
  41. ctx.globalAlpha = originalGlobalAlpha;
  42. }
  43. else {
  44. ctx.stroke();
  45. }
  46. }
  47. export function createCanvasPattern(ctx, pattern, el) {
  48. var image = createOrUpdateImage(pattern.image, pattern.__image, el);
  49. if (isImageReady(image)) {
  50. var canvasPattern = ctx.createPattern(image, pattern.repeat || 'repeat');
  51. if (typeof DOMMatrix === 'function'
  52. && canvasPattern.setTransform) {
  53. var matrix = new DOMMatrix();
  54. matrix.rotateSelf(0, 0, (pattern.rotation || 0) / Math.PI * 180);
  55. matrix.scaleSelf((pattern.scaleX || 1), (pattern.scaleY || 1));
  56. matrix.translateSelf((pattern.x || 0), (pattern.y || 0));
  57. canvasPattern.setTransform(matrix);
  58. }
  59. return canvasPattern;
  60. }
  61. }
  62. function brushPath(ctx, el, style, inBatch) {
  63. var hasStroke = styleHasStroke(style);
  64. var hasFill = styleHasFill(style);
  65. var strokePercent = style.strokePercent;
  66. var strokePart = strokePercent < 1;
  67. var firstDraw = !el.path;
  68. if ((!el.silent || strokePart) && firstDraw) {
  69. el.createPathProxy();
  70. }
  71. var path = el.path || pathProxyForDraw;
  72. if (!inBatch) {
  73. var fill = style.fill;
  74. var stroke = style.stroke;
  75. var hasFillGradient = hasFill && !!fill.colorStops;
  76. var hasStrokeGradient = hasStroke && !!stroke.colorStops;
  77. var hasFillPattern = hasFill && !!fill.image;
  78. var hasStrokePattern = hasStroke && !!stroke.image;
  79. var fillGradient = void 0;
  80. var strokeGradient = void 0;
  81. var fillPattern = void 0;
  82. var strokePattern = void 0;
  83. var rect = void 0;
  84. if (hasFillGradient || hasStrokeGradient) {
  85. rect = el.getBoundingRect();
  86. }
  87. if (hasFillGradient) {
  88. fillGradient = el.__dirty
  89. ? getCanvasGradient(ctx, fill, rect)
  90. : el.__canvasFillGradient;
  91. el.__canvasFillGradient = fillGradient;
  92. }
  93. if (hasStrokeGradient) {
  94. strokeGradient = el.__dirty
  95. ? getCanvasGradient(ctx, stroke, rect)
  96. : el.__canvasStrokeGradient;
  97. el.__canvasStrokeGradient = strokeGradient;
  98. }
  99. if (hasFillPattern) {
  100. fillPattern = (el.__dirty || !el.__canvasFillPattern)
  101. ? createCanvasPattern(ctx, fill, el)
  102. : el.__canvasFillPattern;
  103. el.__canvasFillPattern = fillPattern;
  104. }
  105. if (hasStrokePattern) {
  106. strokePattern = (el.__dirty || !el.__canvasStrokePattern)
  107. ? createCanvasPattern(ctx, stroke, el)
  108. : el.__canvasStrokePattern;
  109. el.__canvasStrokePattern = fillPattern;
  110. }
  111. if (hasFillGradient) {
  112. ctx.fillStyle = fillGradient;
  113. }
  114. else if (hasFillPattern) {
  115. if (fillPattern) {
  116. ctx.fillStyle = fillPattern;
  117. }
  118. else {
  119. hasFill = false;
  120. }
  121. }
  122. if (hasStrokeGradient) {
  123. ctx.strokeStyle = strokeGradient;
  124. }
  125. else if (hasStrokePattern) {
  126. if (strokePattern) {
  127. ctx.strokeStyle = strokePattern;
  128. }
  129. else {
  130. hasStroke = false;
  131. }
  132. }
  133. }
  134. var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth);
  135. var lineDashOffset = style.lineDashOffset;
  136. var ctxLineDash = !!ctx.setLineDash;
  137. var scale = el.getGlobalScale();
  138. path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold);
  139. if (lineDash) {
  140. var lineScale_1 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1;
  141. if (lineScale_1 && lineScale_1 !== 1) {
  142. lineDash = map(lineDash, function (rawVal) {
  143. return rawVal / lineScale_1;
  144. });
  145. lineDashOffset /= lineScale_1;
  146. }
  147. }
  148. var needsRebuild = true;
  149. if (firstDraw || (el.__dirty & SHAPE_CHANGED_BIT)
  150. || (lineDash && !ctxLineDash && hasStroke)) {
  151. path.setDPR(ctx.dpr);
  152. if (strokePart) {
  153. path.setContext(null);
  154. }
  155. else {
  156. path.setContext(ctx);
  157. needsRebuild = false;
  158. }
  159. path.reset();
  160. if (lineDash && !ctxLineDash) {
  161. path.setLineDash(lineDash);
  162. path.setLineDashOffset(lineDashOffset);
  163. }
  164. el.buildPath(path, el.shape, inBatch);
  165. path.toStatic();
  166. el.pathUpdated();
  167. }
  168. if (needsRebuild) {
  169. path.rebuildPath(ctx, strokePart ? strokePercent : 1);
  170. }
  171. if (lineDash && ctxLineDash) {
  172. ctx.setLineDash(lineDash);
  173. ctx.lineDashOffset = lineDashOffset;
  174. }
  175. if (!inBatch) {
  176. if (style.strokeFirst) {
  177. if (hasStroke) {
  178. doStrokePath(ctx, style);
  179. }
  180. if (hasFill) {
  181. doFillPath(ctx, style);
  182. }
  183. }
  184. else {
  185. if (hasFill) {
  186. doFillPath(ctx, style);
  187. }
  188. if (hasStroke) {
  189. doStrokePath(ctx, style);
  190. }
  191. }
  192. }
  193. if (lineDash && ctxLineDash) {
  194. ctx.setLineDash([]);
  195. }
  196. }
  197. function brushImage(ctx, el, style) {
  198. var image = el.__image = createOrUpdateImage(style.image, el.__image, el, el.onload);
  199. if (!image || !isImageReady(image)) {
  200. return;
  201. }
  202. var x = style.x || 0;
  203. var y = style.y || 0;
  204. var width = el.getWidth();
  205. var height = el.getHeight();
  206. var aspect = image.width / image.height;
  207. if (width == null && height != null) {
  208. width = height * aspect;
  209. }
  210. else if (height == null && width != null) {
  211. height = width / aspect;
  212. }
  213. else if (width == null && height == null) {
  214. width = image.width;
  215. height = image.height;
  216. }
  217. if (style.sWidth && style.sHeight) {
  218. var sx = style.sx || 0;
  219. var sy = style.sy || 0;
  220. ctx.drawImage(image, sx, sy, style.sWidth, style.sHeight, x, y, width, height);
  221. }
  222. else if (style.sx && style.sy) {
  223. var sx = style.sx;
  224. var sy = style.sy;
  225. var sWidth = width - sx;
  226. var sHeight = height - sy;
  227. ctx.drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height);
  228. }
  229. else {
  230. ctx.drawImage(image, x, y, width, height);
  231. }
  232. }
  233. function brushText(ctx, el, style) {
  234. var text = style.text;
  235. text != null && (text += '');
  236. if (text) {
  237. ctx.font = style.font || DEFAULT_FONT;
  238. ctx.textAlign = style.textAlign;
  239. ctx.textBaseline = style.textBaseline;
  240. var hasLineDash = void 0;
  241. if (ctx.setLineDash) {
  242. var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth);
  243. var lineDashOffset = style.lineDashOffset;
  244. if (lineDash) {
  245. var lineScale_2 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1;
  246. if (lineScale_2 && lineScale_2 !== 1) {
  247. lineDash = map(lineDash, function (rawVal) {
  248. return rawVal / lineScale_2;
  249. });
  250. lineDashOffset /= lineScale_2;
  251. }
  252. ctx.setLineDash(lineDash);
  253. ctx.lineDashOffset = lineDashOffset;
  254. hasLineDash = true;
  255. }
  256. }
  257. if (style.strokeFirst) {
  258. if (styleHasStroke(style)) {
  259. ctx.strokeText(text, style.x, style.y);
  260. }
  261. if (styleHasFill(style)) {
  262. ctx.fillText(text, style.x, style.y);
  263. }
  264. }
  265. else {
  266. if (styleHasFill(style)) {
  267. ctx.fillText(text, style.x, style.y);
  268. }
  269. if (styleHasStroke(style)) {
  270. ctx.strokeText(text, style.x, style.y);
  271. }
  272. }
  273. if (hasLineDash) {
  274. ctx.setLineDash([]);
  275. }
  276. }
  277. }
  278. var SHADOW_NUMBER_PROPS = ['shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];
  279. var STROKE_PROPS = [
  280. ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]
  281. ];
  282. function bindCommonProps(ctx, style, prevStyle, forceSetAll, scope) {
  283. var styleChanged = false;
  284. if (!forceSetAll) {
  285. prevStyle = prevStyle || {};
  286. if (style === prevStyle) {
  287. return false;
  288. }
  289. }
  290. if (forceSetAll || style.opacity !== prevStyle.opacity) {
  291. if (!styleChanged) {
  292. flushPathDrawn(ctx, scope);
  293. styleChanged = true;
  294. }
  295. var opacity = Math.max(Math.min(style.opacity, 1), 0);
  296. ctx.globalAlpha = isNaN(opacity) ? DEFAULT_COMMON_STYLE.opacity : opacity;
  297. }
  298. if (forceSetAll || style.blend !== prevStyle.blend) {
  299. if (!styleChanged) {
  300. flushPathDrawn(ctx, scope);
  301. styleChanged = true;
  302. }
  303. ctx.globalCompositeOperation = style.blend || DEFAULT_COMMON_STYLE.blend;
  304. }
  305. for (var i = 0; i < SHADOW_NUMBER_PROPS.length; i++) {
  306. var propName = SHADOW_NUMBER_PROPS[i];
  307. if (forceSetAll || style[propName] !== prevStyle[propName]) {
  308. if (!styleChanged) {
  309. flushPathDrawn(ctx, scope);
  310. styleChanged = true;
  311. }
  312. ctx[propName] = ctx.dpr * (style[propName] || 0);
  313. }
  314. }
  315. if (forceSetAll || style.shadowColor !== prevStyle.shadowColor) {
  316. if (!styleChanged) {
  317. flushPathDrawn(ctx, scope);
  318. styleChanged = true;
  319. }
  320. ctx.shadowColor = style.shadowColor || DEFAULT_COMMON_STYLE.shadowColor;
  321. }
  322. return styleChanged;
  323. }
  324. function bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetAll, scope) {
  325. var style = getStyle(el, scope.inHover);
  326. var prevStyle = forceSetAll
  327. ? null
  328. : (prevEl && getStyle(prevEl, scope.inHover) || {});
  329. if (style === prevStyle) {
  330. return false;
  331. }
  332. var styleChanged = bindCommonProps(ctx, style, prevStyle, forceSetAll, scope);
  333. if (forceSetAll || style.fill !== prevStyle.fill) {
  334. if (!styleChanged) {
  335. flushPathDrawn(ctx, scope);
  336. styleChanged = true;
  337. }
  338. isValidStrokeFillStyle(style.fill) && (ctx.fillStyle = style.fill);
  339. }
  340. if (forceSetAll || style.stroke !== prevStyle.stroke) {
  341. if (!styleChanged) {
  342. flushPathDrawn(ctx, scope);
  343. styleChanged = true;
  344. }
  345. isValidStrokeFillStyle(style.stroke) && (ctx.strokeStyle = style.stroke);
  346. }
  347. if (forceSetAll || style.opacity !== prevStyle.opacity) {
  348. if (!styleChanged) {
  349. flushPathDrawn(ctx, scope);
  350. styleChanged = true;
  351. }
  352. ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
  353. }
  354. if (el.hasStroke()) {
  355. var lineWidth = style.lineWidth;
  356. var newLineWidth = lineWidth / ((style.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1);
  357. if (ctx.lineWidth !== newLineWidth) {
  358. if (!styleChanged) {
  359. flushPathDrawn(ctx, scope);
  360. styleChanged = true;
  361. }
  362. ctx.lineWidth = newLineWidth;
  363. }
  364. }
  365. for (var i = 0; i < STROKE_PROPS.length; i++) {
  366. var prop = STROKE_PROPS[i];
  367. var propName = prop[0];
  368. if (forceSetAll || style[propName] !== prevStyle[propName]) {
  369. if (!styleChanged) {
  370. flushPathDrawn(ctx, scope);
  371. styleChanged = true;
  372. }
  373. ctx[propName] = style[propName] || prop[1];
  374. }
  375. }
  376. return styleChanged;
  377. }
  378. function bindImageStyle(ctx, el, prevEl, forceSetAll, scope) {
  379. return bindCommonProps(ctx, getStyle(el, scope.inHover), prevEl && getStyle(prevEl, scope.inHover), forceSetAll, scope);
  380. }
  381. function setContextTransform(ctx, el) {
  382. var m = el.transform;
  383. var dpr = ctx.dpr || 1;
  384. if (m) {
  385. ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
  386. }
  387. else {
  388. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  389. }
  390. }
  391. function updateClipStatus(clipPaths, ctx, scope) {
  392. var allClipped = false;
  393. for (var i = 0; i < clipPaths.length; i++) {
  394. var clipPath = clipPaths[i];
  395. allClipped = allClipped || clipPath.isZeroArea();
  396. setContextTransform(ctx, clipPath);
  397. ctx.beginPath();
  398. clipPath.buildPath(ctx, clipPath.shape);
  399. ctx.clip();
  400. }
  401. scope.allClipped = allClipped;
  402. }
  403. function isTransformChanged(m0, m1) {
  404. if (m0 && m1) {
  405. return m0[0] !== m1[0]
  406. || m0[1] !== m1[1]
  407. || m0[2] !== m1[2]
  408. || m0[3] !== m1[3]
  409. || m0[4] !== m1[4]
  410. || m0[5] !== m1[5];
  411. }
  412. else if (!m0 && !m1) {
  413. return false;
  414. }
  415. return true;
  416. }
  417. var DRAW_TYPE_PATH = 1;
  418. var DRAW_TYPE_IMAGE = 2;
  419. var DRAW_TYPE_TEXT = 3;
  420. var DRAW_TYPE_INCREMENTAL = 4;
  421. function canPathBatch(style) {
  422. var hasFill = styleHasFill(style);
  423. var hasStroke = styleHasStroke(style);
  424. return !(style.lineDash
  425. || !(+hasFill ^ +hasStroke)
  426. || (hasFill && typeof style.fill !== 'string')
  427. || (hasStroke && typeof style.stroke !== 'string')
  428. || style.strokePercent < 1
  429. || style.strokeOpacity < 1
  430. || style.fillOpacity < 1);
  431. }
  432. function flushPathDrawn(ctx, scope) {
  433. scope.batchFill && ctx.fill();
  434. scope.batchStroke && ctx.stroke();
  435. scope.batchFill = '';
  436. scope.batchStroke = '';
  437. }
  438. function getStyle(el, inHover) {
  439. return inHover ? (el.__hoverStyle || el.style) : el.style;
  440. }
  441. export function brushSingle(ctx, el) {
  442. brush(ctx, el, { inHover: false, viewWidth: 0, viewHeight: 0 }, true);
  443. }
  444. export function brush(ctx, el, scope, isLast) {
  445. var m = el.transform;
  446. if (!el.shouldBePainted(scope.viewWidth, scope.viewHeight, false, false)) {
  447. el.__dirty &= ~REDRAW_BIT;
  448. el.__isRendered = false;
  449. return;
  450. }
  451. var clipPaths = el.__clipPaths;
  452. var prevElClipPaths = scope.prevElClipPaths;
  453. var forceSetTransform = false;
  454. var forceSetStyle = false;
  455. if (!prevElClipPaths || isClipPathChanged(clipPaths, prevElClipPaths)) {
  456. if (prevElClipPaths && prevElClipPaths.length) {
  457. flushPathDrawn(ctx, scope);
  458. ctx.restore();
  459. forceSetStyle = forceSetTransform = true;
  460. scope.prevElClipPaths = null;
  461. scope.allClipped = false;
  462. scope.prevEl = null;
  463. }
  464. if (clipPaths && clipPaths.length) {
  465. flushPathDrawn(ctx, scope);
  466. ctx.save();
  467. updateClipStatus(clipPaths, ctx, scope);
  468. forceSetTransform = true;
  469. }
  470. scope.prevElClipPaths = clipPaths;
  471. }
  472. if (scope.allClipped) {
  473. el.__isRendered = false;
  474. return;
  475. }
  476. el.beforeBrush && el.beforeBrush();
  477. el.innerBeforeBrush();
  478. var prevEl = scope.prevEl;
  479. if (!prevEl) {
  480. forceSetStyle = forceSetTransform = true;
  481. }
  482. var canBatchPath = el instanceof Path
  483. && el.autoBatch
  484. && canPathBatch(el.style);
  485. if (forceSetTransform || isTransformChanged(m, prevEl.transform)) {
  486. flushPathDrawn(ctx, scope);
  487. setContextTransform(ctx, el);
  488. }
  489. else if (!canBatchPath) {
  490. flushPathDrawn(ctx, scope);
  491. }
  492. var style = getStyle(el, scope.inHover);
  493. if (el instanceof Path) {
  494. if (scope.lastDrawType !== DRAW_TYPE_PATH) {
  495. forceSetStyle = true;
  496. scope.lastDrawType = DRAW_TYPE_PATH;
  497. }
  498. bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);
  499. if (!canBatchPath || (!scope.batchFill && !scope.batchStroke)) {
  500. ctx.beginPath();
  501. }
  502. brushPath(ctx, el, style, canBatchPath);
  503. if (canBatchPath) {
  504. scope.batchFill = style.fill || '';
  505. scope.batchStroke = style.stroke || '';
  506. }
  507. }
  508. else {
  509. if (el instanceof TSpan) {
  510. if (scope.lastDrawType !== DRAW_TYPE_TEXT) {
  511. forceSetStyle = true;
  512. scope.lastDrawType = DRAW_TYPE_TEXT;
  513. }
  514. bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);
  515. brushText(ctx, el, style);
  516. }
  517. else if (el instanceof ZRImage) {
  518. if (scope.lastDrawType !== DRAW_TYPE_IMAGE) {
  519. forceSetStyle = true;
  520. scope.lastDrawType = DRAW_TYPE_IMAGE;
  521. }
  522. bindImageStyle(ctx, el, prevEl, forceSetStyle, scope);
  523. brushImage(ctx, el, style);
  524. }
  525. else if (el instanceof IncrementalDisplayable) {
  526. if (scope.lastDrawType !== DRAW_TYPE_INCREMENTAL) {
  527. forceSetStyle = true;
  528. scope.lastDrawType = DRAW_TYPE_INCREMENTAL;
  529. }
  530. brushIncremental(ctx, el, scope);
  531. }
  532. }
  533. if (canBatchPath && isLast) {
  534. flushPathDrawn(ctx, scope);
  535. }
  536. el.innerAfterBrush();
  537. el.afterBrush && el.afterBrush();
  538. scope.prevEl = el;
  539. el.__dirty = 0;
  540. el.__isRendered = true;
  541. }
  542. function brushIncremental(ctx, el, scope) {
  543. var displayables = el.getDisplayables();
  544. var temporalDisplayables = el.getTemporalDisplayables();
  545. ctx.save();
  546. var innerScope = {
  547. prevElClipPaths: null,
  548. prevEl: null,
  549. allClipped: false,
  550. viewWidth: scope.viewWidth,
  551. viewHeight: scope.viewHeight,
  552. inHover: scope.inHover
  553. };
  554. var i;
  555. var len;
  556. for (i = el.getCursor(), len = displayables.length; i < len; i++) {
  557. var displayable = displayables[i];
  558. displayable.beforeBrush && displayable.beforeBrush();
  559. displayable.innerBeforeBrush();
  560. brush(ctx, displayable, innerScope, i === len - 1);
  561. displayable.innerAfterBrush();
  562. displayable.afterBrush && displayable.afterBrush();
  563. innerScope.prevEl = displayable;
  564. }
  565. for (var i_1 = 0, len_1 = temporalDisplayables.length; i_1 < len_1; i_1++) {
  566. var displayable = temporalDisplayables[i_1];
  567. displayable.beforeBrush && displayable.beforeBrush();
  568. displayable.innerBeforeBrush();
  569. brush(ctx, displayable, innerScope, i_1 === len_1 - 1);
  570. displayable.innerAfterBrush();
  571. displayable.afterBrush && displayable.afterBrush();
  572. innerScope.prevEl = displayable;
  573. }
  574. el.clearTemporalDisplayables();
  575. el.notClear = true;
  576. ctx.restore();
  577. }