UNPKG

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