UNPKG

12 kBJavaScriptView Raw
1
2const Util = require('./util');
3const Interaction = require('./base');
4// const G2 = require('../core.js');
5
6const BRUSH_TYPES = [ 'X', 'Y', 'XY', 'POLYGON' ];
7const DEFAULT_TYPE = 'XY';
8
9class Brush extends Interaction {
10 getDefaultCfg() {
11 const cfg = super.getDefaultCfg();
12 return Util.mix({}, cfg, {
13 type: DEFAULT_TYPE,
14 startPoint: null,
15 brushing: false,
16 dragging: false,
17 brushShape: null,
18 container: null,
19 polygonPath: null,
20 style: {
21 fill: '#C5D4EB',
22 opacity: 0.3,
23 lineWidth: 1,
24 stroke: '#82A6DD'
25 },
26 draggable: false,
27 dragOffX: 0,
28 dragOffY: 0,
29 inPlot: true,
30 xField: null,
31 yField: null
32 });
33 }
34
35 constructor(cfg, view) {
36 super(cfg, view);
37 const me = this;
38 me.filter = !me.draggable;
39 me.type = me.type.toUpperCase();
40 me.chart = view;
41
42 if (BRUSH_TYPES.indexOf(me.type) === -1) {
43 me.type = DEFAULT_TYPE;
44 }
45 const canvas = me.canvas;
46 if (canvas) {
47 let plotRange;
48 canvas.get('children').map(child => {
49 if (child.get('type') === 'plotBack') {
50 plotRange = child.get('plotRange');
51 return false;
52 }
53 return child;
54 });
55 me.plot = {
56 start: plotRange.bl,
57 end: plotRange.tr
58 };
59 }
60 if (view) {
61 const coord = view.get('coord');
62 me.plot = {
63 start: coord.start,
64 end: coord.end
65 };
66 const xScales = view._getScales('x');
67 const yScales = view._getScales('y');
68 me.xScale = me.xField ? xScales[me.xField] : view.getXScale();
69 me.yScale = me.yField ? yScales[me.yField] : view.getYScales()[0];
70 }
71 }
72
73 // onBurshstart() { }
74 // onBrushmove() { }
75 // onBrushend() {}
76 // onDragstart() {}
77 // onDragmove() {}
78 // onDragend() {}
79
80 start(ev) {
81 const me = this;
82 const { canvas, type, brushShape } = me;
83
84 if (!type) return;
85
86 const startPoint = { x: ev.offsetX, y: ev.offsetY };
87 if (!startPoint.x) return;
88 const isInPlot = me.plot && me.inPlot;
89 const canvasDOM = canvas.get('canvasDOM');
90 const pixelRatio = canvas.get('pixelRatio');
91
92 if (me.selection) me.selection = null;
93
94 if (me.draggable && brushShape && !brushShape.get('destroyed')) { // allow drag the brushShape
95 if (brushShape.isHit(startPoint.x * pixelRatio, startPoint.y * pixelRatio)) {
96 canvasDOM.style.cursor = 'move';
97 me.selection = brushShape;
98 me.dragging = true;
99 if (type === 'X') {
100 me.dragoffX = startPoint.x - brushShape.attr('x');
101 me.dragoffY = 0;
102 } else if (type === 'Y') {
103 me.dragoffX = 0;
104 me.dragoffY = startPoint.y - brushShape.attr('y');
105 } else if (type === 'XY') {
106 me.dragoffX = startPoint.x - brushShape.attr('x');
107 me.dragoffY = startPoint.y - brushShape.attr('y');
108 } else if (type === 'POLYGON') {
109 const box = brushShape.getBBox();
110 me.dragoffX = startPoint.x - box.minX;
111 me.dragoffY = startPoint.y - box.minY;
112 }
113
114 if (isInPlot) {
115 me.selection.attr('clip', canvas.addShape('rect', {
116 attrs: {
117 x: this.plot.start.x,
118 y: this.plot.end.y,
119 width: this.plot.end.x - this.plot.start.x,
120 height: this.plot.start.y - this.plot.end.y,
121 fill: '#fff',
122 fillOpacity: 0
123 }
124 }));
125 }
126 me.onDragstart && me.onDragstart(ev);
127 }
128 me.prePoint = startPoint;
129 }
130
131 if (!me.dragging) { // brush start
132 me.onBrushstart && me.onBrushstart(startPoint);
133 let container = me.container;
134 if (isInPlot) {
135 const { start, end } = me.plot;
136 if (startPoint.x < start.x || startPoint.x > end.x || startPoint.y < end.y || startPoint.y > start.y) return;
137 }
138 canvasDOM.style.cursor = 'crosshair';
139 me.startPoint = startPoint;
140 me.brushShape = null;
141 me.brushing = true;
142
143 if (!container) {
144 container = canvas.addGroup({
145 zIndex: 5 // upper
146 });
147 container.initTransform();
148 } else {
149 container.clear();
150 }
151 me.container = container;
152
153 if (type === 'POLYGON') me.polygonPath = `M ${startPoint.x} ${startPoint.y}`;
154 }
155 }
156 process(ev) {
157 const me = this;
158 const { brushing, dragging, type, plot, startPoint, xScale, yScale, canvas } = me;
159
160 if (!brushing && !dragging) {
161 return;
162 }
163 let currentPoint = {
164 x: ev.offsetX,
165 y: ev.offsetY
166 };
167 const canvasDOM = canvas.get('canvasDOM');
168
169 if (brushing) {
170 canvasDOM.style.cursor = 'crosshair';
171 const { start, end } = plot;
172 let polygonPath = me.polygonPath;
173 let brushShape = me.brushShape;
174 const container = me.container;
175 if (me.plot && me.inPlot) {
176 currentPoint = me._limitCoordScope(currentPoint);
177 }
178
179 let rectStartX;
180 let rectStartY;
181 let rectWidth;
182 let rectHeight;
183
184 if (type === 'Y') {
185 rectStartX = start.x;
186 rectStartY = (currentPoint.y >= startPoint.y) ? startPoint.y : currentPoint.y;
187 rectWidth = Math.abs(start.x - end.x);
188 rectHeight = Math.abs(startPoint.y - currentPoint.y);
189 } else if (type === 'X') {
190 rectStartX = (currentPoint.x >= startPoint.x) ? startPoint.x : currentPoint.x;
191 rectStartY = end.y;
192 rectWidth = Math.abs(startPoint.x - currentPoint.x);
193 rectHeight = Math.abs(end.y - start.y);
194 } else if (type === 'XY') {
195 if (currentPoint.x >= startPoint.x) {
196 rectStartX = startPoint.x;
197 rectStartY = currentPoint.y >= startPoint.y ? startPoint.y : currentPoint.y;
198 } else {
199 rectStartX = currentPoint.x;
200 rectStartY = currentPoint.y >= startPoint.y ? startPoint.y : currentPoint.y;
201 }
202 rectWidth = Math.abs(startPoint.x - currentPoint.x);
203 rectHeight = Math.abs(startPoint.y - currentPoint.y);
204 } else if (type === 'POLYGON') {
205 polygonPath += `L ${currentPoint.x} ${currentPoint.y}`;
206 me.polygonPath = polygonPath;
207 if (!brushShape) {
208 brushShape = container.addShape('path', {
209 attrs: Util.mix(me.style, {
210 path: polygonPath
211 })
212 });
213 } else {
214 !brushShape.get('destroyed') && brushShape.attr(Util.mix({}, brushShape._attrs, {
215 path: polygonPath
216 }));
217 }
218 }
219 if (type !== 'POLYGON') {
220 if (!brushShape) {
221 brushShape = container.addShape('rect', {
222 attrs: Util.mix(me.style, {
223 x: rectStartX,
224 y: rectStartY,
225 width: rectWidth,
226 height: rectHeight
227 })
228 });
229 } else {
230 !brushShape.get('destroyed') && brushShape.attr(Util.mix({}, brushShape._attrs, {
231 x: rectStartX,
232 y: rectStartY,
233 width: rectWidth,
234 height: rectHeight
235 }));
236 }
237 }
238
239 me.brushShape = brushShape;
240 } else if (dragging) {
241 canvasDOM.style.cursor = 'move';
242 const selection = me.selection;
243 if (selection && !selection.get('destroyed')) {
244 if (type === 'POLYGON') {
245 const prePoint = me.prePoint;
246 me.selection.translate(currentPoint.x - prePoint.x, currentPoint.y - prePoint.y);
247 } else {
248 me.dragoffX && selection.attr('x', currentPoint.x - me.dragoffX);
249 me.dragoffY && selection.attr('y', currentPoint.y - me.dragoffY);
250 }
251 }
252 }
253
254 me.prePoint = currentPoint;
255 canvas.draw();
256 const { data, shapes, xValues, yValues } = me._getSelected();
257 const eventObj = {
258 data,
259 shapes,
260 x: currentPoint.x,
261 y: currentPoint.y
262 };
263
264 if (xScale) {
265 eventObj[xScale.field] = xValues;
266 }
267 if (yScale) {
268 eventObj[yScale.field] = yValues;
269 }
270 me.onDragmove && me.onDragmove(eventObj);
271 me.onBrushmove && me.onBrushmove(eventObj);
272 }
273 end(ev) {
274 const me = this;
275 const { data, shapes, xValues, yValues, canvas, type, startPoint, chart, container, xScale, yScale } = me;
276 const { offsetX, offsetY } = ev;
277 const canvasDOM = canvas.get('canvasDOM');
278 canvasDOM.style.cursor = 'default';
279
280 if (Math.abs(startPoint.x - offsetX) <= 1 && Math.abs(startPoint.y - offsetY) <= 1) { // 防止点击事件
281 me.brushing = false;
282 me.dragging = false;
283 return;
284 }
285
286 const eventObj = {
287 data,
288 shapes,
289 x: offsetX,
290 y: offsetY
291 };
292 if (xScale) {
293 eventObj[xScale.field] = xValues;
294 }
295 if (yScale) {
296 eventObj[yScale.field] = yValues;
297 }
298
299 if (me.dragging) {
300 me.dragging = false;
301 me.onDragend && me.onDragend(eventObj);
302 } else if (me.brushing) {
303 me.brushing = false;
304 const brushShape = me.brushShape;
305 let polygonPath = me.polygonPath;
306
307 if (type === 'POLYGON') {
308 polygonPath += 'z';
309
310 brushShape && !brushShape.get('destroyed') && brushShape.attr(Util.mix({}, brushShape._attrs, {
311 path: polygonPath
312 }));
313 me.polygonPath = polygonPath;
314 canvas.draw();
315 }
316
317
318 if (me.onBrushend) {
319 me.onBrushend(eventObj);
320 } else if (chart && me.filter) {
321 container.clear(); // clear the brush
322 // filter data
323 if (type === 'X') {
324 xScale && chart.filter(xScale.field, val => {
325 return xValues.indexOf(val) > -1;
326 });
327 } else if (type === 'Y') {
328 yScale && chart.filter(yScale.field, val => {
329 return yValues.indexOf(val) > -1;
330 });
331 } else {
332 xScale && chart.filter(xScale.field, val => {
333 return xValues.indexOf(val) > -1;
334 });
335 yScale && chart.filter(yScale.field, val => {
336 return yValues.indexOf(val) > -1;
337 });
338 }
339 chart.repaint();
340 }
341 }
342 }
343
344 reset() {
345 const me = this;
346 const { chart, filter } = me;
347 if (chart && filter) {
348 chart.get('options').filters = {};
349 chart.repaint();
350 }
351 }
352
353 _limitCoordScope(point) {
354 const { plot } = this;
355 const { start, end } = plot;
356
357 if (point.x < start.x) {
358 point.x = start.x;
359 }
360 if (point.x > end.x) {
361 point.x = end.x;
362 }
363 if (point.y < end.y) {
364 point.y = end.y;
365 }
366 if (point.y > start.y) {
367 point.y = start.y;
368 }
369 return point;
370 }
371
372 _getSelected() {
373 const me = this;
374 const { chart, xScale, yScale, brushShape, canvas } = me;
375 const pixelRatio = canvas.get('pixelRatio');
376 const selectedShapes = [];
377 const xValues = [];
378 const yValues = [];
379 const selectedData = [];
380 if (chart) {
381 const geoms = chart.get('geoms');
382 geoms.map(geom => {
383 const shapes = geom.getShapes();
384 shapes.map(shape => {
385 let shapeData = shape.get('origin');
386 if (!Array.isArray(shapeData)) { // 线图、区域图等
387 shapeData = [ shapeData ];
388 }
389
390 shapeData.map(each => {
391 if (brushShape.isHit(each.x * pixelRatio, each.y * pixelRatio)) {
392 selectedShapes.push(shape);
393 const origin = each._origin;
394 selectedData.push(origin);
395 xScale && xValues.push(origin[xScale.field]);
396 yScale && yValues.push(origin[yScale.field]);
397 }
398 return each;
399 });
400
401 return shape;
402 });
403 return geom;
404 });
405 }
406 me.shapes = selectedShapes;
407 me.xValues = xValues;
408 me.yValues = yValues;
409 me.data = selectedData;
410 return {
411 data: selectedData,
412 xValues,
413 yValues,
414 shapes: selectedShapes
415 };
416 }
417}
418
419// G2.registerInteraction('brush', Brush);
420// G2.registerInteraction('Brush', Brush);
421
422module.exports = Brush;