| Current Path : /home/cbequiperp/www/components/com_flexicontent/librairies/zrender/ |
| Current File : /home/cbequiperp/www/components/com_flexicontent/librairies/zrender/Handler.js |
/**
* Handler控制模块
* @module zrender/Handler
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
* errorrik (errorrik@gmail.com)
*
*/
// TODO mouseover 只触发一次
define(
function (require) {
'use strict';
var config = require('./config');
var env = require('./tool/env');
var eventTool = require('./tool/event');
var util = require('./tool/util');
var vec2 = require('./tool/vector');
var mat2d = require('./tool/matrix');
var EVENT = config.EVENT;
var Eventful = require('./mixin/Eventful');
var domHandlerNames = [
'resize', 'click', 'dblclick',
'mousewheel', 'mousemove', 'mouseout', 'mouseup', 'mousedown',
'touchstart', 'touchend', 'touchmove'
];
var domHandlers = {
/**
* 窗口大小改变响应函数
* @inner
* @param {Event} event
*/
resize: function (event) {
event = event || window.event;
this._lastHover = null;
this._isMouseDown = 0;
// 分发config.EVENT.RESIZE事件,global
this.dispatch(EVENT.RESIZE, event);
},
/**
* 点击响应函数
* @inner
* @param {Event} event
*/
click: function (event) {
event = this._zrenderEventFixed(event);
// 分发config.EVENT.CLICK事件
var _lastHover = this._lastHover;
if ((_lastHover && _lastHover.clickable)
|| !_lastHover
) {
// 判断没有发生拖拽才触发click事件
if (this._clickThreshold < 5) {
this._dispatchAgency(_lastHover, EVENT.CLICK, event);
}
}
this._mousemoveHandler(event);
},
/**
* 双击响应函数
* @inner
* @param {Event} event
*/
dblclick: function (event) {
event = event || window.event;
event = this._zrenderEventFixed(event);
// 分发config.EVENT.DBLCLICK事件
var _lastHover = this._lastHover;
if ((_lastHover && _lastHover.clickable)
|| !_lastHover
) {
// 判断没有发生拖拽才触发dblclick事件
if (this._clickThreshold < 5) {
this._dispatchAgency(_lastHover, EVENT.DBLCLICK, event);
}
}
this._mousemoveHandler(event);
},
/**
* 鼠标滚轮响应函数
* @inner
* @param {Event} event
*/
mousewheel: function (event) {
event = this._zrenderEventFixed(event);
// http://www.sitepoint.com/html5-javascript-mouse-wheel/
// https://developer.mozilla.org/en-US/docs/DOM/DOM_event_reference/mousewheel
var delta = event.wheelDelta // Webkit
|| -event.detail; // Firefox
var scale = delta > 0 ? 1.1 : 1 / 1.1;
var needsRefresh = false;
var mouseX = this._mouseX;
var mouseY = this._mouseY;
this.painter.eachBuildinLayer(function (layer) {
var pos = layer.position;
if (layer.zoomable) {
layer.__zoom = layer.__zoom || 1;
var newZoom = layer.__zoom;
newZoom *= scale;
newZoom = Math.max(
Math.min(layer.maxZoom, newZoom),
layer.minZoom
);
scale = newZoom / layer.__zoom;
layer.__zoom = newZoom;
// Keep the mouse center when scaling
pos[0] -= (mouseX - pos[0]) * (scale - 1);
pos[1] -= (mouseY - pos[1]) * (scale - 1);
layer.scale[0] *= scale;
layer.scale[1] *= scale;
layer.dirty = true;
needsRefresh = true;
// Prevent browser default scroll action
eventTool.stop(event);
}
});
if (needsRefresh) {
this.painter.refresh();
}
// 分发config.EVENT.MOUSEWHEEL事件
this._dispatchAgency(this._lastHover, EVENT.MOUSEWHEEL, event);
this._mousemoveHandler(event);
},
/**
* 鼠标(手指)移动响应函数
* @inner
* @param {Event} event
*/
mousemove: function (event) {
if (this.painter.isLoading()) {
return;
}
event = this._zrenderEventFixed(event);
this._lastX = this._mouseX;
this._lastY = this._mouseY;
this._mouseX = eventTool.getX(event);
this._mouseY = eventTool.getY(event);
var dx = this._mouseX - this._lastX;
var dy = this._mouseY - this._lastY;
// 可能出现config.EVENT.DRAGSTART事件
// 避免手抖点击误认为拖拽
// if (this._mouseX - this._lastX > 1 || this._mouseY - this._lastY > 1) {
this._processDragStart(event);
// }
this._hasfound = 0;
this._event = event;
this._iterateAndFindHover();
// 找到的在迭代函数里做了处理,没找到得在迭代完后处理
if (!this._hasfound) {
// 过滤首次拖拽产生的mouseout和dragLeave
if (!this._draggingTarget
|| (this._lastHover && this._lastHover != this._draggingTarget)
) {
// 可能出现config.EVENT.MOUSEOUT事件
this._processOutShape(event);
// 可能出现config.EVENT.DRAGLEAVE事件
this._processDragLeave(event);
}
this._lastHover = null;
this.storage.delHover();
this.painter.clearHover();
}
// set cursor for root element
var cursor = 'default';
// 如果存在拖拽中元素,被拖拽的图形元素最后addHover
if (this._draggingTarget) {
this.storage.drift(this._draggingTarget.id, dx, dy);
this._draggingTarget.modSelf();
this.storage.addHover(this._draggingTarget);
// 拖拽不触发click事件
this._clickThreshold++;
}
else if (this._isMouseDown) {
var needsRefresh = false;
// Layer dragging
this.painter.eachBuildinLayer(function (layer) {
if (layer.panable) {
// PENDING
cursor = 'move';
// Keep the mouse center when scaling
layer.position[0] += dx;
layer.position[1] += dy;
needsRefresh = true;
layer.dirty = true;
}
});
if (needsRefresh) {
this.painter.refresh();
}
}
if (this._draggingTarget || (this._hasfound && this._lastHover.draggable)) {
cursor = 'move';
}
else if (this._hasfound && this._lastHover.clickable) {
cursor = 'pointer';
}
this.root.style.cursor = cursor;
// 分发config.EVENT.MOUSEMOVE事件
this._dispatchAgency(this._lastHover, EVENT.MOUSEMOVE, event);
if (this._draggingTarget || this._hasfound || this.storage.hasHoverShape()) {
this.painter.refreshHover();
}
},
/**
* 鼠标(手指)离开响应函数
* @inner
* @param {Event} event
*/
mouseout: function (event) {
event = this._zrenderEventFixed(event);
var element = event.toElement || event.relatedTarget;
if (element != this.root) {
while (element && element.nodeType != 9) {
// 忽略包含在root中的dom引起的mouseOut
if (element == this.root) {
this._mousemoveHandler(event);
return;
}
element = element.parentNode;
}
}
event.zrenderX = this._lastX;
event.zrenderY = this._lastY;
this.root.style.cursor = 'default';
this._isMouseDown = 0;
this._processOutShape(event);
this._processDrop(event);
this._processDragEnd(event);
if (!this.painter.isLoading()) {
this.painter.refreshHover();
}
this.dispatch(EVENT.GLOBALOUT, event);
},
/**
* 鼠标(手指)按下响应函数
* @inner
* @param {Event} event
*/
mousedown: function (event) {
// 重置 clickThreshold
this._clickThreshold = 0;
if (this._lastDownButton == 2) {
this._lastDownButton = event.button;
this._mouseDownTarget = null;
// 仅作为关闭右键菜单使用
return;
}
this._lastMouseDownMoment = new Date();
event = this._zrenderEventFixed(event);
this._isMouseDown = 1;
// 分发config.EVENT.MOUSEDOWN事件
this._mouseDownTarget = this._lastHover;
this._dispatchAgency(this._lastHover, EVENT.MOUSEDOWN, event);
this._lastDownButton = event.button;
},
/**
* 鼠标(手指)抬起响应函数
* @inner
* @param {Event} event
*/
mouseup: function (event) {
event = this._zrenderEventFixed(event);
this.root.style.cursor = 'default';
this._isMouseDown = 0;
this._mouseDownTarget = null;
// 分发config.EVENT.MOUSEUP事件
this._dispatchAgency(this._lastHover, EVENT.MOUSEUP, event);
this._processDrop(event);
this._processDragEnd(event);
},
/**
* Touch开始响应函数
* @inner
* @param {Event} event
*/
touchstart: function (event) {
// eventTool.stop(event);// 阻止浏览器默认事件,重要
event = this._zrenderEventFixed(event, true);
this._lastTouchMoment = new Date();
// 平板补充一次findHover
this._mobileFindFixed(event);
this._mousedownHandler(event);
},
/**
* Touch移动响应函数
* @inner
* @param {Event} event
*/
touchmove: function (event) {
event = this._zrenderEventFixed(event, true);
this._mousemoveHandler(event);
if (this._isDragging) {
eventTool.stop(event);// 阻止浏览器默认事件,重要
}
},
/**
* Touch结束响应函数
* @inner
* @param {Event} event
*/
touchend: function (event) {
// eventTool.stop(event);// 阻止浏览器默认事件,重要
event = this._zrenderEventFixed(event, true);
this._mouseupHandler(event);
var now = new Date();
if (now - this._lastTouchMoment < EVENT.touchClickDelay) {
this._mobileFindFixed(event);
this._clickHandler(event);
if (now - this._lastClickMoment < EVENT.touchClickDelay / 2) {
this._dblclickHandler(event);
if (this._lastHover && this._lastHover.clickable) {
eventTool.stop(event);// 阻止浏览器默认事件,重要
}
}
this._lastClickMoment = now;
}
this.painter.clearHover();
}
};
/**
* bind一个参数的function
*
* @inner
* @param {Function} handler 要bind的function
* @param {Object} context 运行时this环境
* @return {Function}
*/
function bind1Arg(handler, context) {
return function (e) {
return handler.call(context, e);
};
}
/**function bind2Arg(handler, context) {
return function (arg1, arg2) {
return handler.call(context, arg1, arg2);
};
}*/
function bind3Arg(handler, context) {
return function (arg1, arg2, arg3) {
return handler.call(context, arg1, arg2, arg3);
};
}
/**
* 为控制类实例初始化dom 事件处理函数
*
* @inner
* @param {module:zrender/Handler} instance 控制类实例
*/
function initDomHandler(instance) {
var len = domHandlerNames.length;
while (len--) {
var name = domHandlerNames[len];
instance['_' + name + 'Handler'] = bind1Arg(domHandlers[name], instance);
}
}
/**
* @alias module:zrender/Handler
* @constructor
* @extends module:zrender/mixin/Eventful
* @param {HTMLElement} root 绘图区域
* @param {module:zrender/Storage} storage Storage实例
* @param {module:zrender/Painter} painter Painter实例
*/
var Handler = function(root, storage, painter) {
// 添加事件分发器特性
Eventful.call(this);
this.root = root;
this.storage = storage;
this.painter = painter;
// 各种事件标识的私有变量
// this._hasfound = false; //是否找到hover图形元素
// this._lastHover = null; //最后一个hover图形元素
// this._mouseDownTarget = null;
// this._draggingTarget = null; //当前被拖拽的图形元素
// this._isMouseDown = false;
// this._isDragging = false;
// this._lastMouseDownMoment;
// this._lastTouchMoment;
// this._lastDownButton;
this._lastX =
this._lastY =
this._mouseX =
this._mouseY = 0;
this._findHover = bind3Arg(findHover, this);
this._domHover = painter.getDomHover();
initDomHandler(this);
// 初始化,事件绑定,支持的所有事件都由如下原生事件计算得来
if (window.addEventListener) {
window.addEventListener('resize', this._resizeHandler);
if (env.os.tablet || env.os.phone) {
// mobile支持
root.addEventListener('touchstart', this._touchstartHandler);
root.addEventListener('touchmove', this._touchmoveHandler);
root.addEventListener('touchend', this._touchendHandler);
}
else {
// mobile的click/move/up/down自己模拟
root.addEventListener('click', this._clickHandler);
root.addEventListener('dblclick', this._dblclickHandler);
root.addEventListener('mousewheel', this._mousewheelHandler);
root.addEventListener('mousemove', this._mousemoveHandler);
root.addEventListener('mousedown', this._mousedownHandler);
root.addEventListener('mouseup', this._mouseupHandler);
}
root.addEventListener('DOMMouseScroll', this._mousewheelHandler);
root.addEventListener('mouseout', this._mouseoutHandler);
}
else {
window.attachEvent('onresize', this._resizeHandler);
root.attachEvent('onclick', this._clickHandler);
//root.attachEvent('ondblclick ', this._dblclickHandler);
root.ondblclick = this._dblclickHandler;
root.attachEvent('onmousewheel', this._mousewheelHandler);
root.attachEvent('onmousemove', this._mousemoveHandler);
root.attachEvent('onmouseout', this._mouseoutHandler);
root.attachEvent('onmousedown', this._mousedownHandler);
root.attachEvent('onmouseup', this._mouseupHandler);
}
};
/**
* 自定义事件绑定
* @param {string} eventName 事件名称,resize,hover,drag,etc~
* @param {Function} handler 响应函数
* @param {Object} [context] 响应函数
*/
Handler.prototype.on = function (eventName, handler, context) {
this.bind(eventName, handler, context);
return this;
};
/**
* 自定义事件解绑
* @param {string} eventName 事件名称,resize,hover,drag,etc~
* @param {Function} handler 响应函数
*/
Handler.prototype.un = function (eventName, handler) {
this.unbind(eventName, handler);
return this;
};
/**
* 事件触发
* @param {string} eventName 事件名称,resize,hover,drag,etc~
* @param {event=} eventArgs event dom事件对象
*/
Handler.prototype.trigger = function (eventName, eventArgs) {
switch (eventName) {
case EVENT.RESIZE:
case EVENT.CLICK:
case EVENT.DBLCLICK:
case EVENT.MOUSEWHEEL:
case EVENT.MOUSEMOVE:
case EVENT.MOUSEDOWN:
case EVENT.MOUSEUP:
case EVENT.MOUSEOUT:
this['_' + eventName + 'Handler'](eventArgs);
break;
}
};
/**
* 释放,解绑所有事件
*/
Handler.prototype.dispose = function () {
var root = this.root;
if (window.removeEventListener) {
window.removeEventListener('resize', this._resizeHandler);
if (env.os.tablet || env.os.phone) {
// mobile支持
root.removeEventListener('touchstart', this._touchstartHandler);
root.removeEventListener('touchmove', this._touchmoveHandler);
root.removeEventListener('touchend', this._touchendHandler);
}
else {
// mobile的click自己模拟
root.removeEventListener('click', this._clickHandler);
root.removeEventListener('dblclick', this._dblclickHandler);
root.removeEventListener('mousewheel', this._mousewheelHandler);
root.removeEventListener('mousemove', this._mousemoveHandler);
root.removeEventListener('mousedown', this._mousedownHandler);
root.removeEventListener('mouseup', this._mouseupHandler);
}
root.removeEventListener('DOMMouseScroll', this._mousewheelHandler);
root.removeEventListener('mouseout', this._mouseoutHandler);
}
else {
window.detachEvent('onresize', this._resizeHandler);
root.detachEvent('onclick', this._clickHandler);
root.detachEvent('dblclick', this._dblclickHandler);
root.detachEvent('onmousewheel', this._mousewheelHandler);
root.detachEvent('onmousemove', this._mousemoveHandler);
root.detachEvent('onmouseout', this._mouseoutHandler);
root.detachEvent('onmousedown', this._mousedownHandler);
root.detachEvent('onmouseup', this._mouseupHandler);
}
this.root =
this._domHover =
this.storage =
this.painter = null;
this.un();
};
/**
* 拖拽开始
*
* @private
* @param {Object} event 事件对象
*/
Handler.prototype._processDragStart = function (event) {
var _lastHover = this._lastHover;
if (this._isMouseDown
&& _lastHover
&& _lastHover.draggable
&& !this._draggingTarget
&& this._mouseDownTarget == _lastHover
) {
// 拖拽点击生效时长阀门,某些场景需要降低拖拽敏感度
if (_lastHover.dragEnableTime &&
new Date() - this._lastMouseDownMoment < _lastHover.dragEnableTime
) {
return;
}
var _draggingTarget = _lastHover;
this._draggingTarget = _draggingTarget;
this._isDragging = 1;
_draggingTarget.invisible = true;
this.storage.mod(_draggingTarget.id);
// 分发config.EVENT.DRAGSTART事件
this._dispatchAgency(
_draggingTarget,
EVENT.DRAGSTART,
event
);
this.painter.refresh();
}
};
/**
* 拖拽进入目标元素
*
* @private
* @param {Object} event 事件对象
*/
Handler.prototype._processDragEnter = function (event) {
if (this._draggingTarget) {
// 分发config.EVENT.DRAGENTER事件
this._dispatchAgency(
this._lastHover,
EVENT.DRAGENTER,
event,
this._draggingTarget
);
}
};
/**
* 拖拽在目标元素上移动
*
* @private
* @param {Object} event 事件对象
*/
Handler.prototype._processDragOver = function (event) {
if (this._draggingTarget) {
// 分发config.EVENT.DRAGOVER事件
this._dispatchAgency(
this._lastHover,
EVENT.DRAGOVER,
event,
this._draggingTarget
);
}
};
/**
* 拖拽离开目标元素
*
* @private
* @param {Object} event 事件对象
*/
Handler.prototype._processDragLeave = function (event) {
if (this._draggingTarget) {
// 分发config.EVENT.DRAGLEAVE事件
this._dispatchAgency(
this._lastHover,
EVENT.DRAGLEAVE,
event,
this._draggingTarget
);
}
};
/**
* 拖拽在目标元素上完成
*
* @private
* @param {Object} event 事件对象
*/
Handler.prototype._processDrop = function (event) {
if (this._draggingTarget) {
this._draggingTarget.invisible = false;
this.storage.mod(this._draggingTarget.id);
this.painter.refresh();
// 分发config.EVENT.DROP事件
this._dispatchAgency(
this._lastHover,
EVENT.DROP,
event,
this._draggingTarget
);
}
};
/**
* 拖拽结束
*
* @private
* @param {Object} event 事件对象
*/
Handler.prototype._processDragEnd = function (event) {
if (this._draggingTarget) {
// 分发config.EVENT.DRAGEND事件
this._dispatchAgency(
this._draggingTarget,
EVENT.DRAGEND,
event
);
this._lastHover = null;
}
this._isDragging = 0;
this._draggingTarget = null;
};
/**
* 鼠标在某个图形元素上移动
*
* @private
* @param {Object} event 事件对象
*/
Handler.prototype._processOverShape = function (event) {
// 分发config.EVENT.MOUSEOVER事件
this._dispatchAgency(this._lastHover, EVENT.MOUSEOVER, event);
};
/**
* 鼠标离开某个图形元素
*
* @private
* @param {Object} event 事件对象
*/
Handler.prototype._processOutShape = function (event) {
// 分发config.EVENT.MOUSEOUT事件
this._dispatchAgency(this._lastHover, EVENT.MOUSEOUT, event);
};
/**
* 事件分发代理
*
* @private
* @param {Object} targetShape 目标图形元素
* @param {string} eventName 事件名称
* @param {Object} event 事件对象
* @param {Object=} draggedShape 拖拽事件特有,当前被拖拽图形元素
*/
Handler.prototype._dispatchAgency = function (targetShape, eventName, event, draggedShape) {
var eventHandler = 'on' + eventName;
var eventPacket = {
type : eventName,
event : event,
target : targetShape,
cancelBubble: false
};
var el = targetShape;
if (draggedShape) {
eventPacket.dragged = draggedShape;
}
while (el) {
el[eventHandler]
&& (eventPacket.cancelBubble = el[eventHandler](eventPacket));
el.dispatch(eventName, eventPacket);
el = el.parent;
if (eventPacket.cancelBubble) {
break;
}
}
if (targetShape) {
// 冒泡到顶级 zrender 对象
if (!eventPacket.cancelBubble) {
this.dispatch(eventName, eventPacket);
}
}
else if (!draggedShape) {
// 无hover目标,无拖拽对象,原生事件分发
var eveObj = {
type: eventName,
event: event
};
this.dispatch(eventName, eveObj);
// 分发事件到用户自定义层
this.painter.eachOtherLayer(function (layer) {
if (typeof(layer[eventHandler]) == 'function') {
layer[eventHandler](eveObj);
}
if (layer.dispatch) {
layer.dispatch(eventName, eveObj);
}
});
}
};
/**
* 迭代寻找hover shape
* @private
* @method
*/
Handler.prototype._iterateAndFindHover = (function() {
var invTransform = mat2d.create();
return function() {
var list = this.storage.getShapeList();
var currentZLevel;
var currentLayer;
var tmp = [ 0, 0 ];
for (var i = list.length - 1; i >= 0 ; i--) {
var shape = list[i];
if (currentZLevel !== shape.zlevel) {
currentLayer = this.painter.getLayer(shape.zlevel, currentLayer);
tmp[0] = this._mouseX;
tmp[1] = this._mouseY;
if (currentLayer.needTransform) {
mat2d.invert(invTransform, currentLayer.transform);
vec2.applyTransform(tmp, tmp, invTransform);
}
}
if (this._findHover(shape, tmp[0], tmp[1])) {
break;
}
}
};
})();
// touch指尖错觉的尝试偏移量配置
var MOBILE_TOUCH_OFFSETS = [
{ x: 10 },
{ x: -20 },
{ x: 10, y: 10 },
{ y: -20 }
];
// touch有指尖错觉,四向尝试,让touch上的点击更好触发事件
Handler.prototype._mobileFindFixed = function (event) {
this._lastHover = null;
this._mouseX = event.zrenderX;
this._mouseY = event.zrenderY;
this._event = event;
this._iterateAndFindHover();
for (var i = 0; !this._lastHover && i < MOBILE_TOUCH_OFFSETS.length ; i++) {
var offset = MOBILE_TOUCH_OFFSETS[ i ];
offset.x && (this._mouseX += offset.x);
offset.y && (this._mouseY += offset.y);
this._iterateAndFindHover();
}
if (this._lastHover) {
event.zrenderX = this._mouseX;
event.zrenderY = this._mouseY;
}
};
/**
* 迭代函数,查找hover到的图形元素并即时做些事件分发
*
* @inner
* @param {Object} shape 图形元素
* @param {number} x
* @param {number} y
*/
function findHover(shape, x, y) {
if (
(this._draggingTarget && this._draggingTarget.id == shape.id) // 迭代到当前拖拽的图形上
|| shape.isSilent() // 打酱油的路过,啥都不响应的shape~
) {
return false;
}
var event = this._event;
if (shape.isCover(x, y)) {
if (shape.hoverable) {
this.storage.addHover(shape);
}
// 查找是否在 clipShape 中
var p = shape.parent;
while (p) {
if (p.clipShape && !p.clipShape.isCover(this._mouseX, this._mouseY)) {
// 已经被祖先 clip 掉了
return false;
}
p = p.parent;
}
if (this._lastHover != shape) {
this._processOutShape(event);
// 可能出现config.EVENT.DRAGLEAVE事件
this._processDragLeave(event);
this._lastHover = shape;
// 可能出现config.EVENT.DRAGENTER事件
this._processDragEnter(event);
}
this._processOverShape(event);
// 可能出现config.EVENT.DRAGOVER
this._processDragOver(event);
this._hasfound = 1;
return true; // 找到则中断迭代查找
}
return false;
}
/**
* 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标
*
* @private
*/
Handler.prototype._zrenderEventFixed = function (event, isTouch) {
if (event.zrenderFixed) {
return event;
}
if (!isTouch) {
event = event || window.event;
// 进入对象优先~
var target = event.toElement
|| event.relatedTarget
|| event.srcElement
|| event.target;
if (target && target != this._domHover) {
event.zrenderX = (typeof event.offsetX != 'undefined'
? event.offsetX
: event.layerX)
+ target.offsetLeft;
event.zrenderY = (typeof event.offsetY != 'undefined'
? event.offsetY
: event.layerY)
+ target.offsetTop;
}
}
else {
var touch = event.type != 'touchend'
? event.targetTouches[0]
: event.changedTouches[0];
if (touch) {
var rBounding = this.painter._domRoot.getBoundingClientRect();
// touch事件坐标是全屏的~
event.zrenderX = touch.clientX - rBounding.left;
event.zrenderY = touch.clientY - rBounding.top;
}
}
event.zrenderFixed = 1;
return event;
};
util.merge(Handler.prototype, Eventful.prototype, true);
return Handler;
}
);