UNPKG

9.45 kBJavaScriptView Raw
1import { __extends } from "tslib";
2import { AbstractCanvas } from '@antv/g-base';
3import { getShape } from './util/hit';
4import * as Shape from './shape';
5import Group from './group';
6import { each, getPixelRatio, requestAnimationFrame, clearAnimationFrame } from './util/util';
7import { applyAttrsToContext, drawChildren, getMergedRegion, mergeView, checkRefresh, clearChanged } from './util/draw';
8var Canvas = /** @class */ (function (_super) {
9 __extends(Canvas, _super);
10 function Canvas() {
11 return _super !== null && _super.apply(this, arguments) || this;
12 }
13 Canvas.prototype.getDefaultCfg = function () {
14 var cfg = _super.prototype.getDefaultCfg.call(this);
15 // 设置渲染引擎为 canvas,只读属性
16 cfg['renderer'] = 'canvas';
17 // 是否自动绘制,不需要用户调用 draw 方法
18 cfg['autoDraw'] = true;
19 // 是否允许局部刷新图表
20 cfg['localRefresh'] = true;
21 cfg['refreshElements'] = [];
22 // 是否在视图内自动裁剪
23 cfg['clipView'] = true;
24 // 是否使用快速拾取的方案,默认为 false,上层可以打开
25 cfg['quickHit'] = false;
26 return cfg;
27 };
28 /**
29 * 一些方法调用会引起画布变化
30 * @param {ChangeType} changeType 改变的类型
31 */
32 Canvas.prototype.onCanvasChange = function (changeType) {
33 /**
34 * 触发画布更新的三种 changeType
35 * 1. attr: 修改画布的绘图属性
36 * 2. sort: 画布排序,图形的层次会发生变化
37 * 3. changeSize: 改变画布大小
38 */
39 if (changeType === 'attr' || changeType === 'sort' || changeType === 'changeSize') {
40 this.set('refreshElements', [this]);
41 this.draw();
42 }
43 };
44 Canvas.prototype.getShapeBase = function () {
45 return Shape;
46 };
47 Canvas.prototype.getGroupBase = function () {
48 return Group;
49 };
50 /**
51 * 获取屏幕像素比
52 */
53 Canvas.prototype.getPixelRatio = function () {
54 var pixelRatio = this.get('pixelRatio') || getPixelRatio();
55 // 不足 1 的取 1,超出 1 的取整
56 return pixelRatio >= 1 ? Math.ceil(pixelRatio) : 1;
57 };
58 Canvas.prototype.getViewRange = function () {
59 return {
60 minX: 0,
61 minY: 0,
62 maxX: this.cfg.width,
63 maxY: this.cfg.height,
64 };
65 };
66 // 复写基类的方法生成标签
67 Canvas.prototype.createDom = function () {
68 var element = document.createElement('canvas');
69 var context = element.getContext('2d');
70 // 缓存 context 对象
71 this.set('context', context);
72 return element;
73 };
74 Canvas.prototype.setDOMSize = function (width, height) {
75 _super.prototype.setDOMSize.call(this, width, height);
76 var context = this.get('context');
77 var el = this.get('el');
78 var pixelRatio = this.getPixelRatio();
79 el.width = pixelRatio * width;
80 el.height = pixelRatio * height;
81 // 设置 canvas 元素的宽度和高度,会重置缩放,因此 context.scale 需要在每次设置宽、高后调用
82 if (pixelRatio > 1) {
83 context.scale(pixelRatio, pixelRatio);
84 }
85 };
86 // 复写基类方法
87 Canvas.prototype.clear = function () {
88 _super.prototype.clear.call(this);
89 this._clearFrame(); // 需要清理掉延迟绘制的帧
90 var context = this.get('context');
91 var element = this.get('el');
92 context.clearRect(0, 0, element.width, element.height);
93 };
94 Canvas.prototype.getShape = function (x, y) {
95 var shape;
96 if (this.get('quickHit')) {
97 shape = getShape(this, x, y);
98 }
99 else {
100 shape = _super.prototype.getShape.call(this, x, y, null);
101 }
102 return shape;
103 };
104 // 对绘制区域边缘取整,避免浮点数问题
105 Canvas.prototype._getRefreshRegion = function () {
106 var elements = this.get('refreshElements');
107 var viewRegion = this.getViewRange();
108 var region;
109 // 如果是当前画布整体发生了变化,则直接重绘整个画布
110 if (elements.length && elements[0] === this) {
111 region = viewRegion;
112 }
113 else {
114 region = getMergedRegion(elements);
115 if (region) {
116 region.minX = Math.floor(region.minX);
117 region.minY = Math.floor(region.minY);
118 region.maxX = Math.ceil(region.maxX);
119 region.maxY = Math.ceil(region.maxY);
120 region.maxY += 1; // 在很多环境下字体的高低会不一致,附加一像素,避免残影
121 var clipView = this.get('clipView');
122 // 自动裁剪不在 view 内的区域
123 if (clipView) {
124 region = mergeView(region, viewRegion);
125 }
126 }
127 }
128 return region;
129 };
130 /**
131 * 刷新图形元素,这里仅仅是放入队列,下次绘制时进行绘制
132 * @param {IElement} element 图形元素
133 */
134 Canvas.prototype.refreshElement = function (element) {
135 var refreshElements = this.get('refreshElements');
136 refreshElements.push(element);
137 // if (this.get('autoDraw')) {
138 // this._startDraw();
139 // }
140 };
141 // 清理还在进行的绘制
142 Canvas.prototype._clearFrame = function () {
143 var drawFrame = this.get('drawFrame');
144 if (drawFrame) {
145 // 如果全部渲染时,存在局部渲染,则抛弃掉局部渲染
146 clearAnimationFrame(drawFrame);
147 this.set('drawFrame', null);
148 this.set('refreshElements', []);
149 }
150 };
151 // 手工调用绘制接口
152 Canvas.prototype.draw = function () {
153 var drawFrame = this.get('drawFrame');
154 if (this.get('autoDraw') && drawFrame) {
155 return;
156 }
157 this._startDraw();
158 };
159 // 绘制所有图形
160 Canvas.prototype._drawAll = function () {
161 var context = this.get('context');
162 var element = this.get('el');
163 var children = this.getChildren();
164 context.clearRect(0, 0, element.width, element.height);
165 applyAttrsToContext(context, this);
166 drawChildren(context, children);
167 // 对于 https://github.com/antvis/g/issues/422 的场景,全局渲染的模式下也会记录更新的元素队列,因此全局渲染完后也需要置空
168 this.set('refreshElements', []);
169 };
170 // 绘制局部
171 Canvas.prototype._drawRegion = function () {
172 var context = this.get('context');
173 var refreshElements = this.get('refreshElements');
174 var children = this.getChildren();
175 var region = this._getRefreshRegion();
176 // 需要注意可能没有 region 的场景
177 // 一般发生在设置了 localRefresh ,在没有图形发生变化的情况下,用户调用了 draw
178 if (region) {
179 // 清理指定区域
180 context.clearRect(region.minX, region.minY, region.maxX - region.minX, region.maxY - region.minY);
181 // 保存上下文,设置 clip
182 context.save();
183 context.beginPath();
184 context.rect(region.minX, region.minY, region.maxX - region.minX, region.maxY - region.minY);
185 context.clip();
186 applyAttrsToContext(context, this);
187 // 确认更新的元素,这个优化可以提升 10 倍左右的性能,10W 个带有 group 的节点,局部渲染会从 90ms 下降到 5-6 ms
188 checkRefresh(this, children, region);
189 // 绘制子元素
190 drawChildren(context, children, region);
191 context.restore();
192 }
193 else if (refreshElements.length) {
194 // 防止发生改变的 elements 没有 region 的场景,这会发生在多个情况下
195 // 1. 空的 group
196 // 2. 所有 elements 没有在绘图区域
197 // 3. group 下面的 elements 隐藏掉
198 // 如果不进行清理 hasChanged 的状态会不正确
199 clearChanged(refreshElements);
200 }
201 each(refreshElements, function (element) {
202 if (element.get('hasChanged')) {
203 // 在视窗外的 Group 元素会加入到更新队列里,但实际却没有执行 draw() 逻辑,也就没有清除 hasChanged 标记
204 // 即已经重绘完、但 hasChanged 标记没有清除的元素,需要统一清除掉。主要是 Group 存在问题,具体原因待排查
205 element.set('hasChanged', false);
206 }
207 });
208 this.set('refreshElements', []);
209 };
210 // 触发绘制
211 Canvas.prototype._startDraw = function () {
212 var _this = this;
213 var drawFrame = this.get('drawFrame');
214 if (!drawFrame) {
215 drawFrame = requestAnimationFrame(function () {
216 if (_this.get('localRefresh')) {
217 _this._drawRegion();
218 }
219 else {
220 _this._drawAll();
221 }
222 _this.set('drawFrame', null);
223 });
224 this.set('drawFrame', drawFrame);
225 }
226 };
227 Canvas.prototype.skipDraw = function () { };
228 return Canvas;
229}(AbstractCanvas));
230export default Canvas;
231//# sourceMappingURL=canvas.js.map
\No newline at end of file