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')) {
95 // allow drag the brushShape
96 if (brushShape.isHit(startPoint.x * pixelRatio, startPoint.y * pixelRatio)) {
97 canvasDOM.style.cursor = 'move';
98 me.selection = brushShape;
99 me.dragging = true;
100 if (type === 'X') {
101 me.dragoffX = startPoint.x - brushShape.attr('x');
102 me.dragoffY = 0;
103 } else if (type === 'Y') {
104 me.dragoffX = 0;
105 me.dragoffY = startPoint.y - brushShape.attr('y');
106 } else if (type === 'XY') {
107 me.dragoffX = startPoint.x - brushShape.attr('x');
108 me.dragoffY = startPoint.y - brushShape.attr('y');
109 } else if (type === 'POLYGON') {
110 const box = brushShape.getBBox();
111 me.dragoffX = startPoint.x - box.minX;
112 me.dragoffY = startPoint.y - box.minY;
113 }
114
115 if (isInPlot) {
116 me.selection.attr('clip', canvas.addShape('rect', {
117 attrs: {
118 x: this.plot.start.x,
119 y: this.plot.end.y,
120 width: this.plot.end.x - this.plot.start.x,
121 height: this.plot.start.y - this.plot.end.y,
122 fill: '#fff',
123 fillOpacity: 0
124 }
125 }));
126 }
127 me.onDragstart && me.onDragstart(ev);
128 }
129 me.prePoint = startPoint;
130 }
131
132 if (!me.dragging) {
133 // brush start
134 me.onBrushstart && me.onBrushstart(startPoint);
135 let container = me.container;
136 if (isInPlot) {
137 const { start, end } = me.plot;
138 if (startPoint.x < start.x || startPoint.x > end.x || startPoint.y < end.y || startPoint.y > start.y) return;
139 }
140 canvasDOM.style.cursor = 'crosshair';
141 me.startPoint = startPoint;
142 me.brushShape = null;
143 me.brushing = true;
144
145 if (!container) {
146 container = canvas.addGroup({
147 zIndex: 5 // upper
148 });
149 container.initTransform();
150 } else {
151 container.clear();
152 }
153 me.container = container;
154
155 if (type === 'POLYGON') me.polygonPath = `M ${startPoint.x} ${startPoint.y}`;
156 }
157 }
158 process(ev) {
159 const me = this;
160 const { brushing, dragging, type, plot, startPoint, xScale, yScale, canvas } = me;
161
162 if (!brushing && !dragging) {
163 return;
164 }
165 let currentPoint = {
166 x: ev.offsetX,
167 y: ev.offsetY
168 };
169 const canvasDOM = canvas.get('canvasDOM');
170
171 if (brushing) {
172 canvasDOM.style.cursor = 'crosshair';
173 const { start, end } = plot;
174 let polygonPath = me.polygonPath;
175 let brushShape = me.brushShape;
176 const container = me.container;
177 if (me.plot && me.inPlot) {
178 currentPoint = me._limitCoordScope(currentPoint);
179 }
180
181 let rectStartX;
182 let rectStartY;
183 let rectWidth;
184 let rectHeight;
185
186 if (type === 'Y') {
187 rectStartX = start.x;
188 rectStartY = currentPoint.y >= startPoint.y ? startPoint.y : currentPoint.y;
189 rectWidth = Math.abs(start.x - end.x);
190 rectHeight = Math.abs(startPoint.y - currentPoint.y);
191 } else if (type === 'X') {
192 rectStartX = currentPoint.x >= startPoint.x ? startPoint.x : currentPoint.x;
193 rectStartY = end.y;
194 rectWidth = Math.abs(startPoint.x - currentPoint.x);
195 rectHeight = Math.abs(end.y - start.y);
196 } else if (type === 'XY') {
197 if (currentPoint.x >= startPoint.x) {
198 rectStartX = startPoint.x;
199 rectStartY = currentPoint.y >= startPoint.y ? startPoint.y : currentPoint.y;
200 } else {
201 rectStartX = currentPoint.x;
202 rectStartY = currentPoint.y >= startPoint.y ? startPoint.y : currentPoint.y;
203 }
204 rectWidth = Math.abs(startPoint.x - currentPoint.x);
205 rectHeight = Math.abs(startPoint.y - currentPoint.y);
206 } else if (type === 'POLYGON') {
207 polygonPath += `L ${currentPoint.x} ${currentPoint.y}`;
208 me.polygonPath = polygonPath;
209 if (!brushShape) {
210 brushShape = container.addShape('path', {
211 attrs: Util.mix(me.style, {
212 path: polygonPath
213 })
214 });
215 } else {
216 !brushShape.get('destroyed') && brushShape.attr(Util.mix({}, brushShape.__attrs, {
217 path: polygonPath
218 }));
219 }
220 }
221 if (type !== 'POLYGON') {
222 if (!brushShape) {
223 brushShape = container.addShape('rect', {
224 attrs: Util.mix(me.style, {
225 x: rectStartX,
226 y: rectStartY,
227 width: rectWidth,
228 height: rectHeight
229 })
230 });
231 } else {
232 !brushShape.get('destroyed') && brushShape.attr(Util.mix({}, brushShape.__attrs, {
233 x: rectStartX,
234 y: rectStartY,
235 width: rectWidth,
236 height: rectHeight
237 }));
238 }
239 }
240
241 me.brushShape = brushShape;
242 } else if (dragging) {
243 canvasDOM.style.cursor = 'move';
244 const selection = me.selection;
245 if (selection && !selection.get('destroyed')) {
246 if (type === 'POLYGON') {
247 const prePoint = me.prePoint;
248 me.selection.translate(currentPoint.x - prePoint.x, currentPoint.y - prePoint.y);
249 } else {
250 me.dragoffX && selection.attr('x', currentPoint.x - me.dragoffX);
251 me.dragoffY && selection.attr('y', currentPoint.y - me.dragoffY);
252 }
253 }
254 }
255
256 me.prePoint = currentPoint;
257 canvas.draw();
258 const { data, shapes, xValues, yValues } = me._getSelected();
259 const eventObj = {
260 data,
261 shapes,
262 x: currentPoint.x,
263 y: currentPoint.y
264 };
265
266 if (xScale) {
267 eventObj[xScale.field] = xValues;
268 }
269 if (yScale) {
270 eventObj[yScale.field] = yValues;
271 }
272 me.onDragmove && me.onDragmove(eventObj);
273 me.onBrushmove && me.onBrushmove(eventObj);
274 }
275 end(ev) {
276 const me = this;
277 const { data, shapes, xValues, yValues, canvas, type, startPoint, chart, container, xScale, yScale } = me;
278 const { offsetX, offsetY } = ev;
279 const canvasDOM = canvas.get('canvasDOM');
280 canvasDOM.style.cursor = 'default';
281
282 if (Math.abs(startPoint.x - offsetX) <= 1 && Math.abs(startPoint.y - offsetY) <= 1) {
283 // 防止点击事件
284 me.brushing = false;
285 me.dragging = false;
286 return;
287 }
288
289 const eventObj = {
290 data,
291 shapes,
292 x: offsetX,
293 y: offsetY
294 };
295 if (xScale) {
296 eventObj[xScale.field] = xValues;
297 }
298 if (yScale) {
299 eventObj[yScale.field] = yValues;
300 }
301
302 if (me.dragging) {
303 me.dragging = false;
304 me.onDragend && me.onDragend(eventObj);
305 } else if (me.brushing) {
306 me.brushing = false;
307 const brushShape = me.brushShape;
308 let polygonPath = me.polygonPath;
309
310 if (type === 'POLYGON') {
311 polygonPath += 'z';
312
313 brushShape && !brushShape.get('destroyed') && brushShape.attr(Util.mix({}, brushShape.__attrs, {
314 path: polygonPath
315 }));
316 me.polygonPath = polygonPath;
317 canvas.draw();
318 }
319
320 if (me.onBrushend) {
321 me.onBrushend(eventObj);
322 } else if (chart && me.filter) {
323 container.clear(); // clear the brush
324 // filter data
325 if (type === 'X') {
326 xScale && chart.filter(xScale.field, val => {
327 return xValues.indexOf(val) > -1;
328 });
329 } else if (type === 'Y') {
330 yScale && chart.filter(yScale.field, val => {
331 return yValues.indexOf(val) > -1;
332 });
333 } else {
334 xScale && chart.filter(xScale.field, val => {
335 return xValues.indexOf(val) > -1;
336 });
337 yScale && chart.filter(yScale.field, val => {
338 return yValues.indexOf(val) > -1;
339 });
340 }
341 chart.repaint();
342 }
343 }
344 }
345
346 reset() {
347 const me = this;
348 const { chart, filter } = me;
349 if (chart && filter) {
350 chart.get('options').filters = {};
351 chart.repaint();
352 }
353 }
354
355 _limitCoordScope(point) {
356 const { plot } = this;
357 const { start, end } = plot;
358
359 if (point.x < start.x) {
360 point.x = start.x;
361 }
362 if (point.x > end.x) {
363 point.x = end.x;
364 }
365 if (point.y < end.y) {
366 point.y = end.y;
367 }
368 if (point.y > start.y) {
369 point.y = start.y;
370 }
371 return point;
372 }
373
374 _getSelected() {
375 const me = this;
376 const { chart, xScale, yScale, brushShape, canvas } = me;
377 const pixelRatio = canvas.get('pixelRatio');
378 const selectedShapes = [];
379 const xValues = [];
380 const yValues = [];
381 const selectedData = [];
382 if (chart) {
383 const geoms = chart.get('geoms');
384 geoms.map(geom => {
385 const shapes = geom.getShapes();
386 shapes.map(shape => {
387 let shapeData = shape.get('origin');
388 if (!Array.isArray(shapeData)) {
389 // 线图、区域图等
390 shapeData = [shapeData];
391 }
392
393 shapeData.map(each => {
394 if (brushShape.isHit(each.x * pixelRatio, each.y * pixelRatio)) {
395 selectedShapes.push(shape);
396 const origin = each._origin;
397 selectedData.push(origin);
398 xScale && xValues.push(origin[xScale.field]);
399 yScale && yValues.push(origin[yScale.field]);
400 }
401 return each;
402 });
403
404 return shape;
405 });
406 return geom;
407 });
408 }
409 me.shapes = selectedShapes;
410 me.xValues = xValues;
411 me.yValues = yValues;
412 me.data = selectedData;
413 return {
414 data: selectedData,
415 xValues,
416 yValues,
417 shapes: selectedShapes
418 };
419 }
420}
421
422// G2.registerInteraction('brush', Brush);
423// G2.registerInteraction('Brush', Brush);
424
425module.exports = Brush;
\No newline at end of file