@antv/f2
Version:
Charts for mobile visualization.
530 lines (528 loc) • 17.4 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _createSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/createSuper"));
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
var _component = _interopRequireDefault(require("../../base/component"));
var _zoomUtil = require("./zoomUtil");
var _util = require("@antv/util");
var _equal = _interopRequireDefault(require("../../base/equal"));
function lerp(min, max, fraction) {
return (max - min) * fraction + min;
}
function isEqual(aRange, bRange) {
for (var i in aRange) {
if (!(0, _util.isNumberEqual)(aRange[i], bRange[i])) return false;
}
return true;
}
function cloneScale(scale, scaleConfig) {
// @ts-ignore
return new scale.constructor((0, _objectSpread2.default)((0, _objectSpread2.default)({}, scale.__cfg__), scaleConfig));
}
// 缩放
var Zoom = /*#__PURE__*/function (_Component) {
(0, _inherits2.default)(Zoom, _Component);
var _super = (0, _createSuper2.default)(Zoom);
function Zoom(props) {
var _this;
(0, _classCallCheck2.default)(this, Zoom);
var defaultProps = {
onPanStart: function onPanStart() {},
onPinchStart: function onPinchStart() {},
onPan: function onPan() {},
onPinch: function onPinch() {},
onInit: function onInit() {},
onPanEnd: function onPanEnd() {},
onPinchEnd: function onPinchEnd() {},
minCount: 10
};
_this = _super.call(this, (0, _objectSpread2.default)((0, _objectSpread2.default)({}, defaultProps), props));
_this.scale = {};
_this.originScale = {};
//swipe end x y
_this.swipeEnd = {
startX: 0,
startY: 0,
endX: 0,
endY: 0
};
_this.onStart = function () {
var _assertThisInitialize = (0, _assertThisInitialized2.default)(_this),
state = _assertThisInitialize.state;
var range = state.range;
_this.startRange = range;
_this.loop && cancelAnimationFrame(_this.loop);
};
_this.onPan = function (ev) {
var _assertThisInitialize2 = (0, _assertThisInitialized2.default)(_this),
dims = _assertThisInitialize2.dims;
var range = {};
(0, _util.each)(dims, function (dim) {
if (dim === 'x') {
range['x'] = _this._doXPan(ev);
return;
}
if (dim === 'y') {
range['y'] = _this._doYPan(ev);
return;
}
});
if (isEqual(range, _this.state.range)) return;
_this.setState({
range: range
});
// console.log('pan range', range);
};
_this.onSwipe = function (ev) {
var swipe = _this.props.swipe;
if (_this.props.mode.length < 2 || !swipe) return;
var _ev$velocityX = ev.velocityX,
velocityX = _ev$velocityX === void 0 ? 0 : _ev$velocityX,
_ev$velocityY = ev.velocityY,
velocityY = _ev$velocityY === void 0 ? 0 : _ev$velocityY,
points = ev.points;
var range = _this.state.range;
var _points$ = points[0],
x = _points$.x,
y = _points$.y;
// 边界处理
if (Math.abs((range === null || range === void 0 ? void 0 : range.x[0]) - 0) < 0.0005 && velocityX > 0) return;
if (Math.abs((range === null || range === void 0 ? void 0 : range.x[1]) - 1) < 0.0005 && velocityX < 0) return;
if (Math.abs((range === null || range === void 0 ? void 0 : range.y[0]) - 0) < 0.0005 && velocityY < 0) return;
if (Math.abs((range === null || range === void 0 ? void 0 : range.x[1]) - 1) < 0.0005 && velocityY > 0) return;
_this.swipeEnd = {
startX: x,
startY: y,
endX: x + velocityX * 50,
endY: y - velocityY * 50
};
_this.onStart();
_this.update();
};
_this.onPinch = function (ev) {
var _assertThisInitialize3 = (0, _assertThisInitialized2.default)(_this),
dims = _assertThisInitialize3.dims;
var range = {};
(0, _util.each)(dims, function (dim) {
if (dim === 'x') {
range['x'] = _this._doXPinch(ev);
return;
}
if (dim === 'y') {
range['y'] = _this._doYPinch(ev);
return;
}
});
if (isEqual(range, _this.state.range)) return;
_this.setState({
range: range
});
};
_this.onEnd = function () {
_this.startRange = null;
};
var _props$range = props.range,
range = _props$range === void 0 ? [0, 1] : _props$range,
mode = props.mode;
_this.dims = mode instanceof Array ? mode : [mode];
return _this;
}
(0, _createClass2.default)(Zoom, [{
key: "didMount",
value: function didMount() {
this._bindEvents();
}
}, {
key: "willReceiveProps",
value: function willReceiveProps(nextProps) {
var nextRange = nextProps.range;
var lastRange = this.props.range;
if (!(0, _equal.default)(nextRange, lastRange)) {
var cacheRange = {};
(0, _util.each)(this.dims, function (dim) {
cacheRange[dim] = nextRange;
});
this.state = {
range: cacheRange
};
}
}
}, {
key: "willMount",
value: function willMount() {
var _this2 = this;
var props = this.props,
dims = this.dims,
state = this.state;
var minCount = props.minCount,
range = props.range;
// const { range } = state;
var valueLength = Number.MIN_VALUE;
var cacheRange = {};
(0, _util.each)(dims, function (dim) {
var scale = _this2._getScale(dim);
var values = scale.values;
valueLength = values.length > valueLength ? values.length : valueLength;
_this2.scale[dim] = scale;
_this2.originScale[dim] = cloneScale(scale);
_this2.updateRange(range, dim);
cacheRange[dim] = range;
});
// 图表上最少显示 MIN_COUNT 个数据
this.minScale = minCount / valueLength;
this.state = {
range: cacheRange
};
}
}, {
key: "didUnmount",
value: function didUnmount() {
this.loop && cancelAnimationFrame(this.loop);
this._clearEvents();
}
}, {
key: "update",
value: function update() {
var _this3 = this;
var _this$swipeEnd = this.swipeEnd,
startX = _this$swipeEnd.startX,
startY = _this$swipeEnd.startY,
endX = _this$swipeEnd.endX,
endY = _this$swipeEnd.endY;
var x = lerp(startX, endX, 0.05);
var y = lerp(startY, endY, 0.05);
this.swipeEnd = {
startX: x,
startY: y,
endX: endX,
endY: endY
};
var props = this.props;
var coord = props.coord;
var coordWidth = coord.width,
coordHeight = coord.height;
var range = {};
range['x'] = this._doPan((x - startX) / coordWidth, 'x');
range['y'] = this._doPan((y - startY) / coordHeight, 'y');
this.setState({
range: range
});
this.startRange = range;
this.loop = requestAnimationFrame(function () {
return _this3.update();
});
if (Math.abs(x - endX) < 0.0005 && Math.abs(y - endY) < 0.0005) {
this.onEnd();
cancelAnimationFrame(this.loop);
}
}
}, {
key: "_doXPan",
value: function _doXPan(ev) {
var direction = ev.direction,
deltaX = ev.deltaX;
if (this.props.mode.length === 1 && (direction === 'up' || direction === 'down')) {
return this.state.range['x'];
}
ev.preventDefault && ev.preventDefault();
var props = this.props;
var coord = props.coord,
_props$panSensitive = props.panSensitive,
panSensitive = _props$panSensitive === void 0 ? 1 : _props$panSensitive;
var coordWidth = coord.width;
var ratio = deltaX / coordWidth * panSensitive;
var newRange = this._doPan(ratio, 'x');
return newRange;
}
}, {
key: "_doYPan",
value: function _doYPan(ev) {
var direction = ev.direction,
deltaY = ev.deltaY;
if (this.props.mode.length === 1 && (direction === 'left' || direction === 'right')) {
return this.state.range['y'];
}
ev.preventDefault && ev.preventDefault();
var props = this.props;
var coord = props.coord,
_props$panSensitive2 = props.panSensitive,
panSensitive = _props$panSensitive2 === void 0 ? 1 : _props$panSensitive2;
var coordHeight = coord.height;
var ratio = -deltaY / coordHeight * panSensitive;
var newRange = this._doPan(ratio, 'y');
return newRange;
}
}, {
key: "_doPan",
value: function _doPan(ratio, dim) {
var startRange = this.startRange;
var _startRange$dim = (0, _slicedToArray2.default)(startRange[dim], 2),
start = _startRange$dim[0],
end = _startRange$dim[1];
var rangeLen = end - start;
var rangeOffset = rangeLen * ratio;
var newStart = start - rangeOffset;
var newEnd = end - rangeOffset;
var newRange = this.updateRange([newStart, newEnd], dim);
return newRange;
}
}, {
key: "_doXPinch",
value: function _doXPinch(ev) {
ev.preventDefault && ev.preventDefault();
var zoom = ev.zoom,
center = ev.center;
var props = this.props;
var coord = props.coord;
var coordWidth = coord.width,
left = coord.left,
right = coord.right;
var leftLen = Math.abs(center.x - left);
var rightLen = Math.abs(right - center.x);
// 计算左右缩放的比例
var leftZoom = leftLen / coordWidth;
var rightZoom = rightLen / coordWidth;
var newRange = this._doPinch(leftZoom, rightZoom, zoom, 'x');
return newRange;
}
}, {
key: "_doYPinch",
value: function _doYPinch(ev) {
ev.preventDefault && ev.preventDefault();
var zoom = ev.zoom,
center = ev.center;
var props = this.props;
var coord = props.coord;
var coordHeight = coord.height,
top = coord.top,
bottom = coord.bottom;
var topLen = Math.abs(center.y - top);
var bottomLen = Math.abs(bottom - center.y);
// 计算左右缩放的比例
var topZoom = topLen / coordHeight;
var bottomZoom = bottomLen / coordHeight;
var newRange = this._doPinch(topZoom, bottomZoom, zoom, 'y');
return newRange;
}
}, {
key: "_doPinch",
value: function _doPinch(startRatio, endRatio, zoom, dim) {
var startRange = this.startRange,
minScale = this.minScale,
props = this.props;
var _props$pinchSensitive = props.pinchSensitive,
pinchSensitive = _props$pinchSensitive === void 0 ? 1 : _props$pinchSensitive;
var _startRange$dim2 = (0, _slicedToArray2.default)(startRange[dim], 2),
start = _startRange$dim2[0],
end = _startRange$dim2[1];
var zoomOffset = zoom < 1 ? (1 / zoom - 1) * pinchSensitive : (1 - zoom) * pinchSensitive;
var rangeLen = end - start;
var rangeOffset = rangeLen * zoomOffset;
var startOffset = rangeOffset * startRatio;
var endOffset = rangeOffset * endRatio;
var newStart = Math.max(0, start - startOffset);
var newEnd = Math.min(1, end + endOffset);
var newRange = [newStart, newEnd];
// 如果已经到了最小比例,则不能再继续再放大
if (newEnd - newStart < minScale) {
return this.state.range[dim];
}
return this.updateRange(newRange, dim);
}
}, {
key: "updateRange",
value: function updateRange(originalRange, dim) {
if (!originalRange) return;
var _originalRange = (0, _slicedToArray2.default)(originalRange, 2),
start = _originalRange[0],
end = _originalRange[1];
var rangeLength = end - start;
// 处理边界值
var newRange;
if (start < 0) {
newRange = [0, rangeLength];
} else if (end > 1) {
newRange = [1 - rangeLength, 1];
} else {
newRange = originalRange;
}
var props = this.props,
scale = this.scale,
originScale = this.originScale,
state = this.state;
var chart = props.chart,
data = props.data,
autoFit = props.autoFit;
var range = state.range;
if (range && isEqual(newRange, range[dim])) return newRange;
// 更新主 scale
(0, _zoomUtil.updateRange)(scale[dim], originScale[dim], newRange);
if (autoFit) {
var followScale = this._getFollowScales(dim);
this.updateFollow(followScale, scale[dim], data);
}
// 手势变化不执行动画
var animate = chart.animate;
chart.setAnimate(false);
chart.forceUpdate(function () {
chart.setAnimate(animate);
});
return newRange;
}
}, {
key: "updateFollow",
value: function updateFollow(scales, mainScale, data) {
(0, _zoomUtil.updateFollow)(scales, mainScale, data);
}
}, {
key: "_getScale",
value: function _getScale(dim) {
var _this$props = this.props,
coord = _this$props.coord,
chart = _this$props.chart;
if (dim === 'x') {
return coord.transposed ? chart.getYScales()[0] : chart.getXScales()[0];
} else {
return coord.transposed ? chart.getXScales()[0] : chart.getYScales()[0];
}
}
}, {
key: "_getFollowScales",
value: function _getFollowScales(dim) {
var _this$props2 = this.props,
coord = _this$props2.coord,
chart = _this$props2.chart;
if (dim === 'x') {
return coord.transposed ? chart.getXScales() : chart.getYScales();
}
if (dim === 'y') {
return coord.transposed ? chart.getYScales() : chart.getXScales();
}
}
}, {
key: "_bindEvents",
value: function _bindEvents() {
var _this4 = this;
var context = this.context,
props = this.props,
scale = this.scale;
var canvas = context.canvas;
var onPinchStart = props.onPinchStart,
onPanStart = props.onPanStart,
onPanEnd = props.onPanEnd,
pan = props.pan,
pinch = props.pinch,
swipe = props.swipe,
onInit = props.onInit,
onPan = props.onPan,
onPinch = props.onPinch,
onPinchEnd = props.onPinchEnd;
// 统一绑定事件
if (pan !== false) {
canvas.on('panstart', function () {
_this4.onStart();
onPanStart({
scale: scale
});
});
canvas.on('pan', function (ev) {
_this4.onPan(ev);
onPan(ev);
});
canvas.on('panend', function () {
_this4.onEnd();
onPanEnd({
scale: scale
});
});
}
if (pinch !== false) {
canvas.on('pinchstart', function () {
_this4.onStart();
onPinchStart();
});
canvas.on('pinch', function (ev) {
_this4.onPinch(ev);
onPinch(ev);
});
canvas.on('pinchend', function () {
_this4.onEnd();
onPinchEnd({
scale: scale
});
});
}
if (swipe !== false) {
canvas.on('swipe', this.onSwipe);
}
onInit({
scale: scale
});
}
}, {
key: "_clearEvents",
value: function _clearEvents() {
var _this5 = this;
var context = this.context,
props = this.props,
scale = this.scale;
var canvas = context.canvas;
var onPinchEnd = props.onPinchEnd,
onPanEnd = props.onPanEnd,
onPinchStart = props.onPinchStart,
pan = props.pan,
pinch = props.pinch,
onPan = props.onPan,
onPinch = props.onPinch,
swipe = props.swipe;
// 统一解绑事件
if (pan !== false) {
canvas.off('panstart', function () {
_this5.onStart();
onPinchStart();
});
canvas.off('pan', function (ev) {
_this5.onPan(ev);
onPan(ev);
});
canvas.off('panend', function () {
_this5.onEnd();
onPanEnd({
scale: scale
});
});
}
if (pinch !== false) {
canvas.off('pinchstart', function () {
_this5.onStart();
onPinchStart();
});
canvas.off('pinch', function (ev) {
_this5.onPinch(ev);
onPinch(ev);
});
canvas.off('pinchend', function () {
_this5.onEnd();
onPinchEnd({
scale: scale
});
});
}
if (swipe !== false) {
canvas.off('swipe', this.onSwipe);
}
}
}]);
return Zoom;
}(_component.default);
var _default = Zoom;
exports.default = _default;