You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
11 KiB
363 lines
11 KiB
2 years ago
|
(function(window, document, exportName, undefined) {
|
||
|
"use strict";
|
||
|
|
||
|
var isMultiTouch = false;
|
||
|
var multiTouchStartPos;
|
||
|
var eventTarget;
|
||
|
var touchElements = {};
|
||
|
|
||
|
// polyfills
|
||
|
if(!document.createTouch) {
|
||
|
document.createTouch = function(view, target, identifier, pageX, pageY, screenX, screenY, clientX, clientY) {
|
||
|
// auto set
|
||
|
if(clientX == undefined || clientY == undefined) {
|
||
|
clientX = pageX - window.pageXOffset;
|
||
|
clientY = pageY - window.pageYOffset;
|
||
|
}
|
||
|
|
||
|
return new Touch(target, identifier, {
|
||
|
pageX: pageX,
|
||
|
pageY: pageY,
|
||
|
screenX: screenX,
|
||
|
screenY: screenY,
|
||
|
clientX: clientX,
|
||
|
clientY: clientY
|
||
|
});
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if(!document.createTouchList) {
|
||
|
document.createTouchList = function() {
|
||
|
var touchList = new TouchList();
|
||
|
for (var i = 0; i < arguments.length; i++) {
|
||
|
touchList[i] = arguments[i];
|
||
|
}
|
||
|
touchList.length = arguments.length;
|
||
|
return touchList;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* create an touch point
|
||
|
* @constructor
|
||
|
* @param target
|
||
|
* @param identifier
|
||
|
* @param pos
|
||
|
* @param deltaX
|
||
|
* @param deltaY
|
||
|
* @returns {Object} touchPoint
|
||
|
*/
|
||
|
function Touch(target, identifier, pos, deltaX, deltaY) {
|
||
|
deltaX = deltaX || 0;
|
||
|
deltaY = deltaY || 0;
|
||
|
|
||
|
this.identifier = identifier;
|
||
|
this.target = target;
|
||
|
this.clientX = pos.clientX + deltaX;
|
||
|
this.clientY = pos.clientY + deltaY;
|
||
|
this.screenX = pos.screenX + deltaX;
|
||
|
this.screenY = pos.screenY + deltaY;
|
||
|
this.pageX = pos.pageX + deltaX;
|
||
|
this.pageY = pos.pageY + deltaY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* create empty touchlist with the methods
|
||
|
* @constructor
|
||
|
* @returns touchList
|
||
|
*/
|
||
|
function TouchList() {
|
||
|
var touchList = [];
|
||
|
|
||
|
touchList.item = function(index) {
|
||
|
return this[index] || null;
|
||
|
};
|
||
|
|
||
|
// specified by Mozilla
|
||
|
touchList.identifiedTouch = function(id) {
|
||
|
return this[id + 1] || null;
|
||
|
};
|
||
|
|
||
|
return touchList;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Simple trick to fake touch event support
|
||
|
* this is enough for most libraries like Modernizr and Hammer
|
||
|
*/
|
||
|
function fakeTouchSupport() {
|
||
|
var objs = [window, document.documentElement];
|
||
|
var props = ['ontouchstart', 'ontouchmove', 'ontouchcancel', 'ontouchend'];
|
||
|
|
||
|
for(var o=0; o<objs.length; o++) {
|
||
|
for(var p=0; p<props.length; p++) {
|
||
|
if(objs[o] && objs[o][props[p]] == undefined) {
|
||
|
objs[o][props[p]] = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* we don't have to emulate on a touch device
|
||
|
* @returns {boolean}
|
||
|
*/
|
||
|
function hasTouchSupport() {
|
||
|
return ("ontouchstart" in window) || // touch events
|
||
|
(window.Modernizr && window.Modernizr.touch) || // modernizr
|
||
|
(navigator.msMaxTouchPoints || navigator.maxTouchPoints) > 2; // pointer events
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* disable mouseevents on the page
|
||
|
* @param ev
|
||
|
*/
|
||
|
function preventMouseEvents(ev) {
|
||
|
// 注释启用默认事件
|
||
|
// ev.preventDefault();
|
||
|
// ev.stopPropagation();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* only trigger touches when the left mousebutton has been pressed
|
||
|
* @param touchType
|
||
|
* @returns {Function}
|
||
|
*/
|
||
|
function onMouse(touchType) {
|
||
|
return function(ev) {
|
||
|
// prevent mouse events
|
||
|
preventMouseEvents(ev);
|
||
|
|
||
|
if (ev.which !== 1) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// The EventTarget on which the touch point started when it was first placed on the surface,
|
||
|
// even if the touch point has since moved outside the interactive area of that element.
|
||
|
// also, when the target doesnt exist anymore, we update it
|
||
|
if (ev.type == 'mousedown' || !eventTarget || (eventTarget && !eventTarget.dispatchEvent)) {
|
||
|
eventTarget = ev.target;
|
||
|
}
|
||
|
|
||
|
// shiftKey has been lost, so trigger a touchend
|
||
|
if (isMultiTouch && !ev.shiftKey) {
|
||
|
triggerTouch('touchend', ev);
|
||
|
isMultiTouch = false;
|
||
|
}
|
||
|
|
||
|
triggerTouch(touchType, ev);
|
||
|
|
||
|
// we're entering the multi-touch mode!
|
||
|
if (!isMultiTouch && ev.shiftKey) {
|
||
|
isMultiTouch = true;
|
||
|
multiTouchStartPos = {
|
||
|
pageX: ev.pageX,
|
||
|
pageY: ev.pageY,
|
||
|
clientX: ev.clientX,
|
||
|
clientY: ev.clientY,
|
||
|
screenX: ev.screenX,
|
||
|
screenY: ev.screenY
|
||
|
};
|
||
|
triggerTouch('touchstart', ev);
|
||
|
}
|
||
|
|
||
|
// reset
|
||
|
if (ev.type == 'mouseup') {
|
||
|
multiTouchStartPos = null;
|
||
|
isMultiTouch = false;
|
||
|
eventTarget = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* trigger a touch event
|
||
|
* @param eventName
|
||
|
* @param mouseEv
|
||
|
*/
|
||
|
function triggerTouch(eventName, mouseEv) {
|
||
|
var touchEvent = document.createEvent('Event');
|
||
|
touchEvent.initEvent(eventName, true, true);
|
||
|
|
||
|
touchEvent.altKey = mouseEv.altKey;
|
||
|
touchEvent.ctrlKey = mouseEv.ctrlKey;
|
||
|
touchEvent.metaKey = mouseEv.metaKey;
|
||
|
touchEvent.shiftKey = mouseEv.shiftKey;
|
||
|
|
||
|
touchEvent.touches = getActiveTouches(mouseEv, eventName);
|
||
|
touchEvent.targetTouches = getActiveTouches(mouseEv, eventName);
|
||
|
touchEvent.changedTouches = getChangedTouches(mouseEv, eventName);
|
||
|
|
||
|
eventTarget.dispatchEvent(touchEvent);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* create a touchList based on the mouse event
|
||
|
* @param mouseEv
|
||
|
* @returns {TouchList}
|
||
|
*/
|
||
|
function createTouchList(mouseEv) {
|
||
|
var touchList = new TouchList();
|
||
|
|
||
|
if (isMultiTouch) {
|
||
|
var f = TouchEmulator.multiTouchOffset;
|
||
|
var deltaX = multiTouchStartPos.pageX - mouseEv.pageX;
|
||
|
var deltaY = multiTouchStartPos.pageY - mouseEv.pageY;
|
||
|
|
||
|
touchList.push(new Touch(eventTarget, 1, multiTouchStartPos, (deltaX*-1) - f, (deltaY*-1) + f));
|
||
|
touchList.push(new Touch(eventTarget, 2, multiTouchStartPos, deltaX+f, deltaY-f));
|
||
|
} else {
|
||
|
touchList.push(new Touch(eventTarget, 1, mouseEv, 0, 0));
|
||
|
}
|
||
|
|
||
|
return touchList;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* receive all active touches
|
||
|
* @param mouseEv
|
||
|
* @returns {TouchList}
|
||
|
*/
|
||
|
function getActiveTouches(mouseEv, eventName) {
|
||
|
// empty list
|
||
|
if (mouseEv.type == 'mouseup') {
|
||
|
return new TouchList();
|
||
|
}
|
||
|
|
||
|
var touchList = createTouchList(mouseEv);
|
||
|
if(isMultiTouch && mouseEv.type != 'mouseup' && eventName == 'touchend') {
|
||
|
touchList.splice(1, 1);
|
||
|
}
|
||
|
return touchList;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* receive a filtered set of touches with only the changed pointers
|
||
|
* @param mouseEv
|
||
|
* @param eventName
|
||
|
* @returns {TouchList}
|
||
|
*/
|
||
|
function getChangedTouches(mouseEv, eventName) {
|
||
|
var touchList = createTouchList(mouseEv);
|
||
|
|
||
|
// we only want to return the added/removed item on multitouch
|
||
|
// which is the second pointer, so remove the first pointer from the touchList
|
||
|
//
|
||
|
// but when the mouseEv.type is mouseup, we want to send all touches because then
|
||
|
// no new input will be possible
|
||
|
if(isMultiTouch && mouseEv.type != 'mouseup' &&
|
||
|
(eventName == 'touchstart' || eventName == 'touchend')) {
|
||
|
touchList.splice(0, 1);
|
||
|
}
|
||
|
|
||
|
return touchList;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* show the touchpoints on the screen
|
||
|
*/
|
||
|
function showTouches(ev) {
|
||
|
var touch, i, el, styles;
|
||
|
|
||
|
// first all visible touches
|
||
|
for(i = 0; i < ev.touches.length; i++) {
|
||
|
touch = ev.touches[i];
|
||
|
el = touchElements[touch.identifier];
|
||
|
if(!el) {
|
||
|
el = touchElements[touch.identifier] = document.createElement("div");
|
||
|
document.body.appendChild(el);
|
||
|
}
|
||
|
|
||
|
styles = TouchEmulator.template(touch);
|
||
|
for(var prop in styles) {
|
||
|
el.style[prop] = styles[prop];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// remove all ended touches
|
||
|
if(ev.type == 'touchend' || ev.type == 'touchcancel') {
|
||
|
for(i = 0; i < ev.changedTouches.length; i++) {
|
||
|
touch = ev.changedTouches[i];
|
||
|
el = touchElements[touch.identifier];
|
||
|
if(el) {
|
||
|
el.parentNode.removeChild(el);
|
||
|
delete touchElements[touch.identifier];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* TouchEmulator initializer
|
||
|
*/
|
||
|
function TouchEmulator() {
|
||
|
if (hasTouchSupport()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
fakeTouchSupport();
|
||
|
|
||
|
window.addEventListener("mousedown", onMouse('touchstart'), true);
|
||
|
window.addEventListener("mousemove", onMouse('touchmove'), true);
|
||
|
window.addEventListener("mouseup", onMouse('touchend'), true);
|
||
|
|
||
|
window.addEventListener("mouseenter", preventMouseEvents, true);
|
||
|
window.addEventListener("mouseleave", preventMouseEvents, true);
|
||
|
window.addEventListener("mouseout", preventMouseEvents, true);
|
||
|
window.addEventListener("mouseover", preventMouseEvents, true);
|
||
|
|
||
|
// it uses itself!
|
||
|
window.addEventListener("touchstart", showTouches, true);
|
||
|
window.addEventListener("touchmove", showTouches, true);
|
||
|
window.addEventListener("touchend", showTouches, true);
|
||
|
window.addEventListener("touchcancel", showTouches, true);
|
||
|
}
|
||
|
|
||
|
// start distance when entering the multitouch mode
|
||
|
TouchEmulator.multiTouchOffset = 75;
|
||
|
|
||
|
/**
|
||
|
* css template for the touch rendering
|
||
|
* @param touch
|
||
|
* @returns object
|
||
|
*/
|
||
|
TouchEmulator.template = function(touch) {
|
||
|
var size = 0;
|
||
|
var transform = 'translate('+ (touch.clientX-(size/2)) +'px, '+ (touch.clientY-(size/2)) +'px)';
|
||
|
return {
|
||
|
position: 'fixed',
|
||
|
left: 0,
|
||
|
top: 0,
|
||
|
background: '#fff',
|
||
|
border: 'solid 1px #999',
|
||
|
opacity: .6,
|
||
|
borderRadius: '100%',
|
||
|
height: size + 'px',
|
||
|
width: size + 'px',
|
||
|
padding: 0,
|
||
|
margin: 0,
|
||
|
display: 'block',
|
||
|
overflow: 'hidden',
|
||
|
pointerEvents: 'none',
|
||
|
webkitUserSelect: 'none',
|
||
|
mozUserSelect: 'none',
|
||
|
userSelect: 'none',
|
||
|
webkitTransform: transform,
|
||
|
mozTransform: transform,
|
||
|
transform: transform,
|
||
|
zIndex: 100
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// export
|
||
|
if (typeof define == "function" && define.amd) {
|
||
|
define(function() {
|
||
|
return TouchEmulator;
|
||
|
});
|
||
|
} else if (typeof module != "undefined" && module.exports) {
|
||
|
module.exports = TouchEmulator;
|
||
|
} else {
|
||
|
window[exportName] = TouchEmulator;
|
||
|
}
|
||
|
})(window, document, "TouchEmulator");
|