@antv/g2
Version:
the Grammar of Graphics in Javascript
174 lines (156 loc) • 4.99 kB
text/typescript
import { Point, Scale } from '../../../dependents';
import { FilterCondition, EventPayload } from '../../../interface';
import { View, Event } from '../../../chart';
import Action from '../base';
import { isMask } from '../util';
// 获取对应的 scale
function getFilter(scale: Scale, dim: string, point1: Point, point2: Point): FilterCondition {
let min = Math.min(point1[dim], point2[dim]);
let max = Math.max(point1[dim], point2[dim]);
const [rangeMin, rangeMax] = scale.range;
// 约束值在 scale 的 range 之间
if (min < rangeMin) {
min = rangeMin;
}
if (max > rangeMax) {
max = rangeMax;
}
// 范围大于整个 view 的范围,则返回 null
if (min === rangeMax && max === rangeMax) {
return null;
}
const minValue = scale.invert(min);
const maxValue = scale.invert(max);
if (scale.isCategory) {
const minIndex = scale.values.indexOf(minValue);
const maxIndex = scale.values.indexOf(maxValue);
const arr = scale.values.slice(minIndex, maxIndex + 1);
return (value) => {
return arr.includes(value);
};
} else {
return (value) => {
return value >= minValue && value <= maxValue;
};
}
}
/** range-filter 只用于:brush-filter, brush-x-filter, brush-y-filter */
enum EVENTS {
FILTER = 'brush-filter-processing',
RESET = 'brush-filter-reset',
BEFORE_FILTER = 'brush-filter:beforefilter',
AFTER_FILTER = 'brush-filter:afterfilter',
BEFORE_RESET = 'brush-filter:beforereset',
AFTER_RESET = 'brush-filter:afterreset',
}
export { EVENTS as BRUSH_FILTER_EVENTS };
/**
* 范围过滤的 Action
* @ignore
*/
class RangeFilter extends Action {
/** 允许外部传入 dims */
protected cfgFields: ['dims'];
/**
* 范围过滤生效的字段/维度,可以是 x, y
*/
protected dims: string[] = ['x', 'y'];
/** 起始点 */
protected startPoint: Point = null;
private isStarted: boolean = false;
// x,y 是否生效
private hasDim(dim: string) {
return this.dims.includes(dim);
}
/**
* 开始范围过滤,记录范围过滤的起点
*/
public start() {
const context = this.context;
this.isStarted = true;
this.startPoint = context.getCurrentPoint();
}
/**
* 过滤,以开始的点和当前点对数据进行过滤
*/
public filter() {
let startPoint;
let currentPoint;
if (isMask(this.context)) {
const maskShape = this.context.event.target;
const bbox = maskShape.getCanvasBBox();
startPoint = { x: bbox.x, y: bbox.y };
currentPoint = { x: bbox.maxX, y: bbox.maxY };
} else {
if (!this.isStarted) {
// 如果没有开始,则不执行过滤
return;
}
startPoint = this.startPoint;
currentPoint = this.context.getCurrentPoint();
}
if (Math.abs(startPoint.x - currentPoint.x) < 5 || Math.abs(startPoint.x - currentPoint.y) < 5) {
// 距离过小也不生效
return;
}
const { view, event } = this.context;
const payload = { view, event, dims: this.dims };
view.emit(EVENTS.BEFORE_FILTER, Event.fromData(view, EVENTS.BEFORE_FILTER, payload));
const coord = view.getCoordinate();
const normalCurrent = coord.invert(currentPoint);
const normalStart = coord.invert(startPoint);
// 设置 x 方向的 filter
if (this.hasDim('x')) {
const xScale = view.getXScale();
const filter = getFilter(xScale, 'x', normalCurrent, normalStart);
this.filterView(view, xScale.field, filter);
}
// 设置 y 方向的 filter
if (this.hasDim('y')) {
const yScale = view.getYScales()[0];
const filter = getFilter(yScale, 'y', normalCurrent, normalStart);
this.filterView(view, yScale.field, filter);
}
this.reRender(view, { source: EVENTS.FILTER });
view.emit(EVENTS.AFTER_FILTER, Event.fromData(view, EVENTS.AFTER_FILTER, payload));
}
/**
* 结束
*/
public end() {
this.isStarted = false;
}
/**
* 取消同当前 Action 相关的过滤,指定的 x,y
*/
public reset() {
const view = this.context.view;
view.emit(EVENTS.BEFORE_RESET, Event.fromData(view, EVENTS.BEFORE_RESET, {}));
this.isStarted = false;
if (this.hasDim('x')) {
const xScale = view.getXScale();
this.filterView(view, xScale.field, null); // 取消过滤
}
if (this.hasDim('y')) {
// y 轴过滤仅取第一个 yScale
const yScale = view.getYScales()[0];
this.filterView(view, yScale.field, null); // 取消过滤
}
this.reRender(view, { source: EVENTS.RESET });
view.emit(EVENTS.AFTER_RESET, Event.fromData(view, EVENTS.AFTER_RESET, {}));
}
/**
* 对 view 进行过滤
*/
protected filterView(view: View, field: string, filter: FilterCondition) {
view.filter(field, filter);
}
/**
* 重新渲染
* @param view
*/
protected reRender(view: View, payload?: EventPayload) {
view.render(true, payload);
}
}
export default RangeFilter;