blob: c82e0c6ec6d7c1427a110204f7e053775c5b4b7a [file] [log] [blame]
import { addEventListener, removeEventListener, normalizeEvent } from '../core/event';
import * as zrUtil from '../core/util';
import Eventful from '../mixin/Eventful';
import env from '../core/env';
var TOUCH_CLICK_DELAY = 300;
var mouseHandlerNames = ['click', 'dblclick', 'mousewheel', 'mouseout', 'mouseup', 'mousedown', 'mousemove', 'contextmenu'];
var touchHandlerNames = ['touchstart', 'touchend', 'touchmove'];
var pointerEventNames = {
pointerdown: 1,
pointerup: 1,
pointermove: 1,
pointerout: 1
};
var pointerHandlerNames = zrUtil.map(mouseHandlerNames, function (name) {
var nm = name.replace('mouse', 'pointer');
return pointerEventNames[nm] ? nm : name;
});
function eventNameFix(name) {
return name === 'mousewheel' && env.browser.firefox ? 'DOMMouseScroll' : name;
} // function onMSGestureChange(proxy, event) {
// if (event.translationX || event.translationY) {
// // mousemove is carried by MSGesture to reduce the sensitivity.
// proxy.handler.dispatchToElement(event.target, 'mousemove', event);
// }
// if (event.scale !== 1) {
// event.pinchX = event.offsetX;
// event.pinchY = event.offsetY;
// event.pinchScale = event.scale;
// proxy.handler.dispatchToElement(event.target, 'pinch', event);
// }
// }
/**
* Prevent mouse event from being dispatched after Touch Events action
* @see <https://github.com/deltakosh/handjs/blob/master/src/hand.base.js>
* 1. Mobile browsers dispatch mouse events 300ms after touchend.
* 2. Chrome for Android dispatch mousedown for long-touch about 650ms
* Result: Blocking Mouse Events for 700ms.
*/
function setTouchTimer(instance) {
instance._touching = true;
clearTimeout(instance._touchTimer);
instance._touchTimer = setTimeout(function () {
instance._touching = false;
}, 700);
}
var domHandlers = {
/**
* Mouse move handler
* @inner
* @param {Event} event
*/
mousemove: function (event) {
event = normalizeEvent(this.dom, event);
this.trigger('mousemove', event);
},
/**
* Mouse out handler
* @inner
* @param {Event} event
*/
mouseout: function (event) {
event = normalizeEvent(this.dom, event);
var element = event.toElement || event.relatedTarget;
if (element !== this.dom) {
while (element && element.nodeType !== 9) {
// 忽略包含在root中的dom引起的mouseOut
if (element === this.dom) {
return;
}
element = element.parentNode;
}
}
this.trigger('mouseout', event);
},
/**
* Touch开始响应函数
* @inner
* @param {Event} event
*/
touchstart: function (event) {
// Default mouse behaviour should not be disabled here.
// For example, page may needs to be slided.
event = normalizeEvent(this.dom, event); // Mark touch, which is useful in distinguish touch and
// mouse event in upper applicatoin.
event.zrByTouch = true;
this._lastTouchMoment = new Date();
this.handler.processGesture(event, 'start'); // In touch device, trigger `mousemove`(`mouseover`) should
// be triggered, and must before `mousedown` triggered.
domHandlers.mousemove.call(this, event);
domHandlers.mousedown.call(this, event);
setTouchTimer(this);
},
/**
* Touch移动响应函数
* @inner
* @param {Event} event
*/
touchmove: function (event) {
event = normalizeEvent(this.dom, event); // Mark touch, which is useful in distinguish touch and
// mouse event in upper applicatoin.
event.zrByTouch = true;
this.handler.processGesture(event, 'change'); // Mouse move should always be triggered no matter whether
// there is gestrue event, because mouse move and pinch may
// be used at the same time.
domHandlers.mousemove.call(this, event);
setTouchTimer(this);
},
/**
* Touch结束响应函数
* @inner
* @param {Event} event
*/
touchend: function (event) {
event = normalizeEvent(this.dom, event); // Mark touch, which is useful in distinguish touch and
// mouse event in upper applicatoin.
event.zrByTouch = true;
this.handler.processGesture(event, 'end');
domHandlers.mouseup.call(this, event); // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is
// triggered in `touchstart`. This seems to be illogical, but by this mechanism,
// we can conveniently implement "hover style" in both PC and touch device just
// by listening to `mouseover` to add "hover style" and listening to `mouseout`
// to remove "hover style" on an element, without any additional code for
// compatibility. (`mouseout` will not be triggered in `touchend`, so "hover
// style" will remain for user view)
// click event should always be triggered no matter whether
// there is gestrue event. System click can not be prevented.
if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) {
domHandlers.click.call(this, event);
}
setTouchTimer(this);
},
pointerdown: function (event) {
domHandlers.mousedown.call(this, event); // if (useMSGuesture(this, event)) {
// this._msGesture.addPointer(event.pointerId);
// }
},
pointermove: function (event) {
// FIXME
// pointermove is so sensitive that it always triggered when
// tap(click) on touch screen, which affect some judgement in
// upper application. So, we dont support mousemove on MS touch
// device yet.
if (!isPointerFromTouch(event)) {
domHandlers.mousemove.call(this, event);
}
},
pointerup: function (event) {
domHandlers.mouseup.call(this, event);
},
pointerout: function (event) {
// pointerout will be triggered when tap on touch screen
// (IE11+/Edge on MS Surface) after click event triggered,
// which is inconsistent with the mousout behavior we defined
// in touchend. So we unify them.
// (check domHandlers.touchend for detailed explanation)
if (!isPointerFromTouch(event)) {
domHandlers.mouseout.call(this, event);
}
}
};
function isPointerFromTouch(event) {
var pointerType = event.pointerType;
return pointerType === 'pen' || pointerType === 'touch';
} // function useMSGuesture(handlerProxy, event) {
// return isPointerFromTouch(event) && !!handlerProxy._msGesture;
// }
// Common handlers
zrUtil.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
domHandlers[name] = function (event) {
event = normalizeEvent(this.dom, event);
this.trigger(name, event);
};
});
/**
* 为控制类实例初始化dom 事件处理函数
*
* @inner
* @param {module:zrender/Handler} instance 控制类实例
*/
function initDomHandler(instance) {
zrUtil.each(touchHandlerNames, function (name) {
instance._handlers[name] = zrUtil.bind(domHandlers[name], instance);
});
zrUtil.each(pointerHandlerNames, function (name) {
instance._handlers[name] = zrUtil.bind(domHandlers[name], instance);
});
zrUtil.each(mouseHandlerNames, function (name) {
instance._handlers[name] = makeMouseHandler(domHandlers[name], instance);
});
function makeMouseHandler(fn, instance) {
return function () {
if (instance._touching) {
return;
}
return fn.apply(instance, arguments);
};
}
}
function HandlerDomProxy(dom) {
Eventful.call(this);
this.dom = dom;
/**
* @private
* @type {boolean}
*/
this._touching = false;
/**
* @private
* @type {number}
*/
this._touchTimer;
this._handlers = {};
initDomHandler(this);
if (env.pointerEventsSupported) {
// Only IE11+/Edge
// 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240),
// IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event
// at the same time.
// 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on
// screen, which do not occurs in pointer event.
// So we use pointer event to both detect touch gesture and mouse behavior.
mountHandlers(pointerHandlerNames, this); // FIXME
// Note: MS Gesture require CSS touch-action set. But touch-action is not reliable,
// which does not prevent defuault behavior occasionally (which may cause view port
// zoomed in but use can not zoom it back). And event.preventDefault() does not work.
// So we have to not to use MSGesture and not to support touchmove and pinch on MS
// touch screen. And we only support click behavior on MS touch screen now.
// MS Gesture Event is only supported on IE11+/Edge and on Windows 8+.
// We dont support touch on IE on win7.
// See <https://msdn.microsoft.com/en-us/library/dn433243(v=vs.85).aspx>
// if (typeof MSGesture === 'function') {
// (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line
// dom.addEventListener('MSGestureChange', onMSGestureChange);
// }
} else {
if (env.touchEventsSupported) {
mountHandlers(touchHandlerNames, this); // Handler of 'mouseout' event is needed in touch mode, which will be mounted below.
// addEventListener(root, 'mouseout', this._mouseoutHandler);
} // 1. Considering some devices that both enable touch and mouse event (like on MS Surface
// and lenovo X240, @see #2350), we make mouse event be always listened, otherwise
// mouse event can not be handle in those devices.
// 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent
// mouseevent after touch event triggered, see `setTouchTimer`.
mountHandlers(mouseHandlerNames, this);
}
function mountHandlers(handlerNames, instance) {
zrUtil.each(handlerNames, function (name) {
addEventListener(dom, eventNameFix(name), instance._handlers[name]);
}, instance);
}
}
var handlerDomProxyProto = HandlerDomProxy.prototype;
handlerDomProxyProto.dispose = function () {
var handlerNames = mouseHandlerNames.concat(touchHandlerNames);
for (var i = 0; i < handlerNames.length; i++) {
var name = handlerNames[i];
removeEventListener(this.dom, eventNameFix(name), this._handlers[name]);
}
};
handlerDomProxyProto.setCursor = function (cursorStyle) {
this.dom.style && (this.dom.style.cursor = cursorStyle || 'default');
};
zrUtil.mixin(HandlerDomProxy, Eventful);
export default HandlerDomProxy;