| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- var _event = require("../core/event");
- var addEventListener = _event.addEventListener;
- var removeEventListener = _event.removeEventListener;
- var normalizeEvent = _event.normalizeEvent;
- var zrUtil = require("../core/util");
- var Eventful = require("../mixin/Eventful");
- var env = require("../core/env");
- var GestureMgr = require("../core/GestureMgr");
- 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 processGesture(proxy, event, stage) {
- var gestureMgr = proxy._gestureMgr;
- stage === 'start' && gestureMgr.clear();
- var gestureInfo = gestureMgr.recognize(event, proxy.handler.findHover(event.zrX, event.zrY, null).target, proxy.dom);
- stage === 'end' && gestureMgr.clear(); // Do not do any preventDefault here. Upper application do that if necessary.
- if (gestureInfo) {
- var type = gestureInfo.type;
- event.gestureEvent = type;
- proxy.handler.dispatchToElement({
- target: gestureInfo.target
- }, type, gestureInfo.event);
- }
- } // 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();
- processGesture(this, 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;
- processGesture(this, 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;
- processGesture(this, 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;
- /**
- * @private
- * @type {module:zrender/core/GestureMgr}
- */
- this._gestureMgr = new GestureMgr();
- 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.cursor = cursorStyle || 'default';
- };
- zrUtil.mixin(HandlerDomProxy, Eventful);
- var _default = HandlerDomProxy;
- module.exports = _default;
|