UNPKG

16.9 kBJavaScriptView Raw
1import { __assign, __extends } from "tslib";
2import { Component, isEqual, jsx } from '@antv/f-engine';
3import { updateRange, updateFollow } from './zoomUtil';
4import { each, isNumberEqual, isArray } from '@antv/util';
5import { quadraticOut as easeing } from './easing';
6function lerp(min, max, fraction) {
7 return (max - min) * fraction + min;
8}
9function isNumberEqualRange(aRange, bRange) {
10 if (!bRange) return false;
11 for (var i = 0, len = aRange.length; i < len; i++) {
12 if (!isNumberEqual(aRange[i], bRange[i])) return false;
13 }
14 return true;
15}
16function isEqualRange(aRange, bRange) {
17 if (!bRange) return false;
18 if (isArray(aRange)) {
19 return isNumberEqualRange(aRange, bRange);
20 }
21 // object
22 for (var i in aRange) {
23 if (!isNumberEqualRange(aRange[i], bRange[i])) return false;
24 }
25 return true;
26}
27function cloneScale(scale, scaleConfig) {
28 // @ts-ignore
29 return new scale.constructor(__assign(__assign({}, scale.__cfg__), scaleConfig));
30}
31export default (function (View) {
32 return /** @class */function (_super) {
33 __extends(Zoom, _super);
34 function Zoom(props) {
35 var _this = this;
36 var defaultProps = {
37 onPanStart: function onPanStart() {},
38 onPinchStart: function onPinchStart() {},
39 onPan: function onPan() {},
40 onPinch: function onPinch() {},
41 onInit: function onInit() {},
42 onPanEnd: function onPanEnd() {},
43 onPinchEnd: function onPinchEnd() {},
44 minCount: 10
45 };
46 _this = _super.call(this, __assign(__assign({}, defaultProps), props)) || this;
47 _this.scale = {};
48 _this.originScale = {};
49 //swipe end x y
50 _this.swipeEnd = {
51 startX: 0,
52 startY: 0,
53 endX: 0,
54 endY: 0
55 };
56 _this.onStart = function () {
57 var state = _this.state;
58 var range = state.range;
59 _this.startRange = range;
60 _this.loop && cancelAnimationFrame(_this.loop);
61 };
62 _this.onPan = function (ev) {
63 var dims = _this.dims;
64 var range = {};
65 each(dims, function (dim) {
66 if (dim === 'x') {
67 range['x'] = _this._doXPan(ev);
68 return;
69 }
70 if (dim === 'y') {
71 range['y'] = _this._doYPan(ev);
72 return;
73 }
74 });
75 _this.renderRange(range);
76 };
77 _this.onSwipe = function (ev) {
78 var _a = _this,
79 props = _a.props,
80 state = _a.state;
81 // 滑动速率
82 var velocity = ev.velocity,
83 direction = ev.direction,
84 _b = ev.velocityX,
85 velocityX = _b === void 0 ? 0 : _b,
86 _c = ev.velocityY,
87 velocityY = _c === void 0 ? 0 : _c,
88 points = ev.points;
89 var mode = props.mode,
90 swipe = props.swipe;
91 var range = state.range;
92 if (!swipe || !mode) {
93 return;
94 }
95 if (mode.length === 1) {
96 _this.animateSwipe(mode, range[mode], direction === 'right' || direction === 'down' ? -velocity : velocity);
97 return;
98 }
99 var _d = points[0],
100 x = _d.x,
101 y = _d.y;
102 // 边界处理
103 if (Math.abs((range === null || range === void 0 ? void 0 : range.x[0]) - 0) < 0.0005 && velocityX > 0) return;
104 if (Math.abs((range === null || range === void 0 ? void 0 : range.x[1]) - 1) < 0.0005 && velocityX < 0) return;
105 if (Math.abs((range === null || range === void 0 ? void 0 : range.y[0]) - 0) < 0.0005 && velocityY < 0) return;
106 if (Math.abs((range === null || range === void 0 ? void 0 : range.x[1]) - 1) < 0.0005 && velocityY > 0) return;
107 _this.swipeEnd = {
108 startX: x,
109 startY: y,
110 endX: x + velocityX * 50,
111 endY: y - velocityY * 50
112 };
113 _this.onStart();
114 _this.update();
115 };
116 _this.onPinch = function (ev) {
117 var dims = _this.dims;
118 var range = {};
119 each(dims, function (dim) {
120 if (dim === 'x') {
121 range['x'] = _this._doXPinch(ev);
122 return;
123 }
124 if (dim === 'y') {
125 range['y'] = _this._doYPinch(ev);
126 return;
127 }
128 });
129 _this.renderRange(range);
130 };
131 _this.onEnd = function () {
132 _this.startRange = null;
133 };
134 var mode = props.mode;
135 _this.dims = isArray(mode) ? mode : [mode];
136 return _this;
137 }
138 Zoom.prototype.didMount = function () {
139 var scale = this.scale;
140 var onInit = this.props.onInit;
141 onInit({
142 scale: scale
143 });
144 this._bindEvents();
145 };
146 Zoom.prototype.willReceiveProps = function (nextProps) {
147 var nextRange = nextProps.range;
148 var lastRange = this.props.range;
149 if (!isEqual(nextRange, lastRange)) {
150 var cacheRange_1 = {};
151 each(this.dims, function (dim) {
152 cacheRange_1[dim] = nextRange;
153 });
154 this.state = {
155 range: cacheRange_1
156 };
157 }
158 };
159 Zoom.prototype.willMount = function () {
160 var _this = this;
161 var _a = this,
162 props = _a.props,
163 dims = _a.dims;
164 var minCount = props.minCount,
165 range = props.range;
166 var valueLength = Number.MIN_VALUE;
167 var cacheRange = {};
168 each(dims, function (dim) {
169 var scale = _this._getScale(dim);
170 var values = scale.values;
171 valueLength = values.length > valueLength ? values.length : valueLength;
172 _this.scale[dim] = scale;
173 _this.originScale[dim] = cloneScale(scale);
174 _this.updateRange(range, dim);
175 cacheRange[dim] = range;
176 });
177 // 图表上最少显示 MIN_COUNT 个数据
178 this.minScale = minCount / valueLength;
179 this.renderRange(cacheRange);
180 };
181 Zoom.prototype.willUpdate = function () {
182 var _this = this;
183 var _a = this,
184 props = _a.props,
185 state = _a.state,
186 dims = _a.dims;
187 var minCount = props.minCount,
188 range = props.range;
189 var valueLength = Number.MIN_VALUE;
190 var cacheRange = {};
191 each(dims, function (dim) {
192 var scale = _this._getScale(dim);
193 // scale 没有变化, 不处理
194 if (scale === _this.scale[dim]) {
195 return;
196 }
197 var values = scale.values;
198 valueLength = values.length > valueLength ? values.length : valueLength;
199 _this.scale[dim] = scale;
200 _this.originScale[dim] = cloneScale(scale);
201 // 让 range 触发更新
202 _this.state.range[dim] = [0, 1];
203 _this.updateRange(range, dim);
204 cacheRange[dim] = range;
205 });
206 // 有变化
207 if (Object.keys(cacheRange).length > 0) {
208 this.minScale = minCount / valueLength;
209 var newRange = __assign(__assign({}, state.range), cacheRange);
210 this.renderRange(newRange);
211 }
212 };
213 Zoom.prototype.didUnmount = function () {
214 this.loop && cancelAnimationFrame(this.loop);
215 };
216 Zoom.prototype._bindEvents = function () {
217 var _this = this;
218 var scale = this.scale;
219 var _a = this.props,
220 chart = _a.chart,
221 onPinchStart = _a.onPinchStart,
222 onPanStart = _a.onPanStart,
223 onPanEnd = _a.onPanEnd,
224 pan = _a.pan,
225 pinch = _a.pinch,
226 swipe = _a.swipe,
227 onPan = _a.onPan,
228 onPinch = _a.onPinch,
229 onPinchEnd = _a.onPinchEnd;
230 // 统一绑定事件
231 if (pan !== false) {
232 chart.on('panstart', function () {
233 _this.onStart();
234 onPanStart({
235 scale: scale
236 });
237 });
238 chart.on('pan', function (ev) {
239 _this.onPan(ev);
240 onPan(ev);
241 });
242 chart.on('panend', function () {
243 _this.onEnd();
244 onPanEnd({
245 scale: scale
246 });
247 });
248 }
249 if (pinch !== false) {
250 chart.on('pinchstart', function () {
251 _this.onStart();
252 onPinchStart();
253 });
254 chart.on('pinch', function (ev) {
255 _this.onPinch(ev);
256 onPinch(ev);
257 });
258 chart.on('pinchend', function () {
259 _this.onEnd();
260 onPinchEnd({
261 scale: scale
262 });
263 });
264 }
265 if (swipe !== false) {
266 chart.on('swipe', this.onSwipe);
267 }
268 };
269 Zoom.prototype.update = function () {
270 var _this = this;
271 var _a = this.swipeEnd,
272 startX = _a.startX,
273 startY = _a.startY,
274 endX = _a.endX,
275 endY = _a.endY;
276 var x = lerp(startX, endX, 0.05);
277 var y = lerp(startY, endY, 0.05);
278 this.swipeEnd = {
279 startX: x,
280 startY: y,
281 endX: endX,
282 endY: endY
283 };
284 var props = this.props;
285 var coord = props.coord;
286 var coordWidth = coord.width,
287 coordHeight = coord.height;
288 var range = {};
289 range['x'] = this._doPan((x - startX) / coordWidth, 'x');
290 range['y'] = this._doPan((y - startY) / coordHeight, 'y');
291 this.renderRange(range);
292 this.startRange = range;
293 this.loop = requestAnimationFrame(function () {
294 return _this.update();
295 });
296 if (Math.abs(x - endX) < 0.0005 && Math.abs(y - endY) < 0.0005) {
297 this.onEnd();
298 cancelAnimationFrame(this.loop);
299 }
300 };
301 Zoom.prototype.animateSwipe = function (dim, dimRange, velocity) {
302 var _this = this;
303 var _a = this,
304 context = _a.context,
305 props = _a.props;
306 var requestAnimationFrame = context.canvas.requestAnimationFrame;
307 var _b = props.swipeDuration,
308 swipeDuration = _b === void 0 ? 1000 : _b;
309 var diff = (dimRange[1] - dimRange[0]) * velocity;
310 var startTime = Date.now();
311 var updateRange = function updateRange(t) {
312 var newDimRange = [dimRange[0] + diff * t, dimRange[1] + diff * t];
313 var newRange = _this.updateRange(newDimRange, dim);
314 _this.renderRange({
315 x: newRange
316 });
317 };
318 // 更新动画
319 var update = function update() {
320 // 计算动画已经进行的时间
321 var currentTime = Date.now() - startTime;
322 // 如果动画已经结束,则清除定时器
323 if (currentTime >= swipeDuration) {
324 updateRange(1);
325 return;
326 }
327 // 计算缓动值
328 var progress = currentTime / swipeDuration;
329 var easedProgress = easeing(progress);
330 updateRange(easedProgress);
331 requestAnimationFrame(function () {
332 update();
333 });
334 };
335 update();
336 };
337 Zoom.prototype._doXPan = function (ev) {
338 var direction = ev.direction,
339 deltaX = ev.deltaX;
340 if (this.props.mode.length === 1 && (direction === 'up' || direction === 'down')) {
341 return this.state.range['x'];
342 }
343 ev.preventDefault && ev.preventDefault();
344 var props = this.props;
345 var coord = props.coord,
346 _a = props.panSensitive,
347 panSensitive = _a === void 0 ? 1 : _a;
348 var coordWidth = coord.width;
349 var ratio = deltaX / coordWidth * panSensitive;
350 var newRange = this._doPan(ratio, 'x');
351 return newRange;
352 };
353 Zoom.prototype._doYPan = function (ev) {
354 var direction = ev.direction,
355 deltaY = ev.deltaY;
356 if (this.props.mode.length === 1 && (direction === 'left' || direction === 'right')) {
357 return this.state.range['y'];
358 }
359 ev.preventDefault && ev.preventDefault();
360 var props = this.props;
361 var coord = props.coord,
362 _a = props.panSensitive,
363 panSensitive = _a === void 0 ? 1 : _a;
364 var coordHeight = coord.height;
365 var ratio = -deltaY / coordHeight * panSensitive;
366 var newRange = this._doPan(ratio, 'y');
367 return newRange;
368 };
369 Zoom.prototype._doPan = function (ratio, dim) {
370 var startRange = this.startRange;
371 var _a = startRange[dim],
372 start = _a[0],
373 end = _a[1];
374 var rangeLen = end - start;
375 var rangeOffset = rangeLen * ratio;
376 var newStart = start - rangeOffset;
377 var newEnd = end - rangeOffset;
378 var newRange = this.updateRange([newStart, newEnd], dim);
379 return newRange;
380 };
381 Zoom.prototype._doXPinch = function (ev) {
382 ev.preventDefault && ev.preventDefault();
383 var zoom = ev.zoom,
384 center = ev.center;
385 var props = this.props;
386 var coord = props.coord;
387 var coordWidth = coord.width,
388 left = coord.left,
389 right = coord.right;
390 var leftLen = Math.abs(center.x - left);
391 var rightLen = Math.abs(right - center.x);
392 // 计算左右缩放的比例
393 var leftZoom = leftLen / coordWidth;
394 var rightZoom = rightLen / coordWidth;
395 var newRange = this._doPinch(leftZoom, rightZoom, zoom, 'x');
396 return newRange;
397 };
398 Zoom.prototype._doYPinch = function (ev) {
399 ev.preventDefault && ev.preventDefault();
400 var zoom = ev.zoom,
401 center = ev.center;
402 var props = this.props;
403 var coord = props.coord;
404 var coordHeight = coord.height,
405 top = coord.top,
406 bottom = coord.bottom;
407 var topLen = Math.abs(center.y - top);
408 var bottomLen = Math.abs(bottom - center.y);
409 // 计算左右缩放的比例
410 var topZoom = topLen / coordHeight;
411 var bottomZoom = bottomLen / coordHeight;
412 var newRange = this._doPinch(topZoom, bottomZoom, zoom, 'y');
413 return newRange;
414 };
415 Zoom.prototype._doPinch = function (startRatio, endRatio, zoom, dim) {
416 var _a = this,
417 startRange = _a.startRange,
418 minScale = _a.minScale,
419 props = _a.props;
420 var _b = props.pinchSensitive,
421 pinchSensitive = _b === void 0 ? 1 : _b;
422 var _c = startRange[dim],
423 start = _c[0],
424 end = _c[1];
425 var zoomOffset = zoom < 1 ? (1 / zoom - 1) * pinchSensitive : (1 - zoom) * pinchSensitive;
426 var rangeLen = end - start;
427 var rangeOffset = rangeLen * zoomOffset;
428 var startOffset = rangeOffset * startRatio;
429 var endOffset = rangeOffset * endRatio;
430 var newStart = Math.max(0, start - startOffset);
431 var newEnd = Math.min(1, end + endOffset);
432 var newRange = [newStart, newEnd];
433 // 如果已经到了最小比例,则不能再继续再放大
434 if (newEnd - newStart < minScale) {
435 return this.state.range[dim];
436 }
437 return this.updateRange(newRange, dim);
438 };
439 Zoom.prototype.updateRange = function (originalRange, dim) {
440 if (!originalRange) return;
441 var start = originalRange[0],
442 end = originalRange[1];
443 var rangeLength = end - start;
444 // 处理边界值
445 var newRange;
446 if (start < 0) {
447 newRange = [0, rangeLength];
448 } else if (end > 1) {
449 newRange = [1 - rangeLength, 1];
450 } else {
451 newRange = originalRange;
452 }
453 var _a = this,
454 props = _a.props,
455 scale = _a.scale,
456 originScale = _a.originScale,
457 state = _a.state;
458 var data = props.data,
459 autoFit = props.autoFit;
460 var range = state.range;
461 if (range && isEqualRange(newRange, range[dim])) return newRange;
462 // 更新主 scale
463 updateRange(scale[dim], originScale[dim], newRange);
464 if (autoFit) {
465 var followScale = this._getFollowScales(dim);
466 this.updateFollow(followScale, scale[dim], data);
467 }
468 return newRange;
469 };
470 Zoom.prototype.updateFollow = function (scales, mainScale, data) {
471 updateFollow(scales, mainScale, data);
472 };
473 Zoom.prototype._getScale = function (dim) {
474 var _a = this.props,
475 coord = _a.coord,
476 chart = _a.chart;
477 if (dim === 'x') {
478 return coord.transposed ? chart.getYScales()[0] : chart.getXScales()[0];
479 } else {
480 return coord.transposed ? chart.getXScales()[0] : chart.getYScales()[0];
481 }
482 };
483 Zoom.prototype._getFollowScales = function (dim) {
484 var _a = this.props,
485 coord = _a.coord,
486 chart = _a.chart;
487 if (dim === 'x') {
488 return coord.transposed ? chart.getXScales() : chart.getYScales();
489 }
490 if (dim === 'y') {
491 return coord.transposed ? chart.getYScales() : chart.getXScales();
492 }
493 };
494 Zoom.prototype.renderRange = function (range) {
495 var _a = this,
496 state = _a.state,
497 props = _a.props;
498 if (isEqualRange(range, state.range)) return;
499 var chart = props.chart;
500 // 手势变化不执行动画
501 var animate = chart.animate;
502 chart.setAnimate(false);
503 // 后面的 forceUpdate 会强制更新,所以不用 setState,直接更新
504 state.range = range;
505 chart.forceUpdate(function () {
506 chart.setAnimate(animate);
507 });
508 };
509 Zoom.prototype.render = function () {
510 return jsx(View, __assign({}, this.props, this.state));
511 };
512 return Zoom;
513 }(Component);
514});
\No newline at end of file