@antv/g6
Version:
graph visualization frame work
348 lines (302 loc) • 12.8 kB
JavaScript
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* @fileOverview dom event handler
* @author wuyue.lwy <wyueliu@gmail.com>
*/
var Base = require('./base');
var Util = require('../util/');
var EVENT = {
MOUSEMOVE: 'mousemove',
MOUSEDOWN: 'mousedown',
MOUSEUP: 'mouseup',
MOUSEENTER: 'mouseenter',
MOUSELEAVE: 'mouseleave',
CLICK: 'click',
DBLCLICK: 'dblclick',
DRAGSTART: 'dragstart',
DRAG: 'drag',
DRAGENTER: 'dragenter',
DRAGLEAVE: 'dragleave',
DRAGEND: 'dragend',
DROP: 'drop',
CONTEXTMENU: 'contextmenu',
MOUSEWHEEL: 'mousewheel',
KEYDOWN: 'keydown',
KEYUP: 'keyup',
KEYPRESS: 'keypress'
};
var SHAKE_TOLERANCE = 9; // use to tolerate click shake prevent drag shake. The distance is tolerance sqrt.
// native dom events list:
var MouseEventTypes = [EVENT.DBLCLICK, EVENT.MOUSEDOWN, EVENT.MOUSEUP, EVENT.MOUSEENTER, EVENT.MOUSELEAVE, EVENT.MOUSEMOVE, EVENT.CONTEXTMENU, EVENT.MOUSEWHEEL];
var KeyboardEventTypes = [EVENT.KEYDOWN, EVENT.KEYUP, EVENT.KEYPRESS];
var CANVAS = 'canvas:';
var Controller = function (_Base) {
_inherits(Controller, _Base);
function Controller(cfg) {
_classCallCheck(this, Controller);
var _this = _possibleConstructorReturn(this, _Base.call(this, cfg));
_this._domEvents = [];
_this._initEventStates();
_this._registerEvents();
return _this;
}
// init evnet states
Controller.prototype._initEventStates = function _initEventStates() {
this._pressing = false;
this._dragging = false;
this._currentEventObj = null;
this._dragEventObj = {};
};
// register all native dom events
Controller.prototype._registerEvents = function _registerEvents() {
this._registerMouseEvents();
this._registerKeyboardEvents();
};
Controller.prototype._registerKeyboardEvents = function _registerKeyboardEvents() {
var graph = this.graph;
var el = graph.getKeyboardEventWrapper();
var _events = this._domEvents;
var keyboardEnable = graph.get('keyboardEnable');
Util.each(KeyboardEventTypes, function (item) {
_events.push(Util.addEventListener(el, item, function (ev) {
var enable = true;
if (Util.isFunction(keyboardEnable)) {
enable = keyboardEnable();
}
enable && graph.emit(item, {
domEvent: ev
});
}));
});
};
Controller.prototype._registerMouseEvents = function _registerMouseEvents() {
var _this2 = this;
var graph = this.graph;
var self = this;
var el = graph.getMouseEventWrapper();
var _events = this._domEvents;
Util.each(MouseEventTypes, function (item) {
_events.push(Util.addEventListener(el, item, function (ev) {
var oldEventObj = _this2._currentEventObj;
_this2._oldEventObj = oldEventObj;
_this2._processEventObj(ev);
var currentEventObj = _this2._currentEventObj;
// emit simulate events like click, dragstart, dragend, drop, dtagover, mouseenter, mouseleave
self._simulateEvents(ev, oldEventObj, currentEventObj);
// emit normal events
if ([EVENT.MOUSELEAVE, EVENT.MOUSEENTER].indexOf(ev.type) !== -1) {
self._triggerEvent(CANVAS + ev.type);
}
self._triggerEvent(ev.type);
if (ev.type === EVENT.MOUSELEAVE) {
// trigger canvas dragleave when out of canvas , user can clear things that record by themselves
if (_this2._dragging) {
self._triggerEvent(EVENT.DRAGLEAVE, Util.mix({}, currentEventObj, { item: null, shape: null, currentItem: _this2._dragEventObj.item, currentShape: _this2._dragEventObj.shape }));
}
self._initEventStates();
}
}));
});
};
// delete listeners
Controller.prototype.destroy = function destroy() {
var events = this._domEvents;
Util.each(events, function (ev) {
ev && ev.remove();
});
this._domEvents = null;
};
/**
* trigger event
* @param {string} type - event type
* @param {object} eventObj - event object
*/
Controller.prototype._triggerEvent = function _triggerEvent(type, eventObj) {
if (!eventObj) {
if (type === 'mouseleave') {
eventObj = this._oldEventObj;
} else {
eventObj = this._currentEventObj;
}
}
if (type === 'mousedown') {
eventObj.button = this._button;
}
// emit shape event
eventObj._type = type;
this.emitGraphEvent(type, eventObj);
if ([CANVAS + EVENT.MOUSELEAVE, CANVAS + EVENT.MOUSEENTER].indexOf(type) !== -1) {
return;
}
var eventPreFix = eventObj.shape && eventObj.shape.eventPreFix;
if ([EVENT.DRAGSTART, EVENT.DRAG, EVENT.DRAGEND].indexOf(type) !== -1) {
// get correct prefix
eventPreFix = eventObj.currentShape && eventObj.currentShape.eventPreFix;
}
if (eventPreFix) {
var _type = eventPreFix + ':' + type;
eventObj._type = _type;
if (Util.isBoolean(eventObj._isItemChange)) {
if (eventObj._isItemChange) {
this.emitGraphEvent(_type, eventObj);
}
} else {
this.emitGraphEvent(_type, eventObj);
}
}
};
/**
* emit graph event
* @param {object} type - event type
* @param {object} eventObj - event object
*/
Controller.prototype.emitGraphEvent = function emitGraphEvent(type, eventObj) {
var graph = this.graph;
graph.emit(type, eventObj);
};
Controller.prototype._getDistanceToPress = function _getDistanceToPress(ev) {
return Math.pow(ev.clientX - this._pressX, 2) + Math.pow(ev.clientY - this._pressY, 2);
};
/**
* check whether or not click and drag
* @param {object} ev - native dom event
* @param {object} oldEventObj - old event object
* @param {object} currentEventObj - current event object
*/
Controller.prototype._simulateEvents = function _simulateEvents(ev) {
var oldEventObj = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var currentEventObj = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var currentItem = this._dragEventObj.item;
var currentShape = this._dragEventObj.shape;
switch (ev.type) {
case EVENT.MOUSEDOWN:
this._pressing = true;
this._button = ev.button;
this._pressX = ev.clientX;
this._pressY = ev.clientY;
break;
case EVENT.MOUSEMOVE:
// record the element that was dragging
if (this._dragging) {
this._triggerEvent(EVENT.DRAG, Util.mix({}, currentEventObj, { button: this._button, currentItem: currentItem, currentShape: currentShape }));
if (oldEventObj.shape !== currentEventObj.shape) {
var _isItemChange = this._isItemChange(oldEventObj, currentEventObj);
if (oldEventObj.shape) {
this._triggerEvent(EVENT.DRAGLEAVE, Util.mix({}, currentEventObj, { button: this._button, item: oldEventObj.item, shape: oldEventObj.shape,
toItem: currentEventObj.item, toShape: currentEventObj.shape, currentItem: currentItem, currentShape: currentShape, _isItemChange: _isItemChange }));
}
if (currentEventObj.shape) {
this._triggerEvent(EVENT.DRAGENTER, Util.mix({}, currentEventObj, { button: this._button, currentItem: currentItem, currentShape: currentShape, fromItem: oldEventObj.item,
fromShape: oldEventObj.shape, _isItemChange: _isItemChange }));
}
}
} else if (this._pressing && this._getDistanceToPress(ev) > SHAKE_TOLERANCE) {
this._dragging = true;
this._dragEventObj = currentEventObj;
currentItem = this._dragEventObj.item;
currentShape = this._dragEventObj.shape;
this._triggerEvent(EVENT.DRAGSTART, Util.mix({}, currentEventObj, { button: this._button, currentItem: currentItem, currentShape: currentShape }));
}
// normal move
if (oldEventObj.shape !== currentEventObj.shape) {
var _isItemChange2 = this._isItemChange(oldEventObj, currentEventObj);
if (oldEventObj.shape) {
// just canvas has no shape, it should not trigger leave
this._triggerEvent(EVENT.MOUSELEAVE, Util.mix({}, currentEventObj, { item: oldEventObj.item, shape: oldEventObj.shape,
toItem: currentEventObj.item, toShape: currentEventObj.shape, _isItemChange: _isItemChange2 }));
}
if (currentEventObj.shape) {
// canvas should not trigger enter
this._triggerEvent(EVENT.MOUSEENTER, Util.mix({}, currentEventObj, { fromtItem: oldEventObj.item, fromShape: oldEventObj.shape, _isItemChange: _isItemChange2 }));
}
}
break;
case EVENT.MOUSEUP:
if (!this._dragging && this._pressing) {
this._triggerEvent(EVENT.CLICK, Util.mix({}, currentEventObj, { button: this._button }));
} else {
this._triggerEvent(EVENT.DROP, Util.mix({}, currentEventObj, { button: this._button, currentItem: currentItem, currentShape: currentShape }));
this._triggerEvent(EVENT.DRAGEND, Util.mix({}, currentEventObj, { button: this._button, currentItem: currentItem, currentShape: currentShape }));
}
this._pressing = false;
this._dragging = false;
this._dragEventObj = {};
break;
default:
return;
}
};
/**
* checkout item is change
* @param {object} oldEventObj - old event obj
* @param {object} currentEventObj - current event obj
* @return {boolean} rst
*/
Controller.prototype._isItemChange = function _isItemChange(oldEventObj, currentEventObj) {
var oldShape = oldEventObj.shape;
var currentShape = currentEventObj.shape;
var shapeIsItemChange = oldShape && currentShape && (oldShape.get('isItemChange') || currentShape.get('isItemChange'));
if (shapeIsItemChange) {
return shapeIsItemChange(currentShape, oldShape);
}
if (Util.isObject(oldEventObj.item) && Util.isObject(currentEventObj.item)) {
return oldEventObj.item.id !== currentEventObj.item.id;
}
return oldEventObj.item !== currentEventObj.item;
};
/**
* handle the native event by browser
* @param {object} ev - native event by browser
*/
Controller.prototype._processEventObj = function _processEventObj(ev) {
var graph = this.graph;
var canvas = graph.get('_canvas');
var frontCanvas = graph.get('_frontCanvas');
var evObj = this._getEventObj(ev, canvas);
var frontEvObj = this._getEventObj(ev, frontCanvas);
// frontEvObj is the first
if (frontEvObj.shape) {
evObj.shape = frontEvObj.shape;
evObj.item = frontEvObj.item;
}
evObj.frontEvObj = frontEvObj;
this._currentEventObj = evObj;
};
// transform point position by pixel Ratio
Controller.prototype._parsePoint = function _parsePoint(x, y) {
var graph = this.graph;
return graph.getPointByCanvas({
x: x,
y: y
});
};
/**
* get the source object which emitted event
* @param {object} ev -native event by browser
* @param {object} canvas -the scene that event occurred
* @return {object} - event object
*/
Controller.prototype._getEventObj = function _getEventObj(ev, canvas) {
var graph = this.graph;
var clientX = ev.clientX;
var clientY = ev.clientY;
var canvasPoint = canvas.getPointByClient(clientX, clientY);
var point = this._parsePoint(canvasPoint.x, canvasPoint.y);
var shape = canvas.getShape(canvasPoint.x, canvasPoint.y);
var item = graph.getItemByShape(shape);
var pixelRatio = canvas.get('pixelRatio');
return {
item: item,
shape: shape,
x: point.x,
y: point.y,
domX: canvasPoint.x / pixelRatio,
domY: canvasPoint.y / pixelRatio,
domEvent: ev
};
};
return Controller;
}(Base);
module.exports = Controller;