jQuery移动设备图片裁剪代码

版权:原创 更新时间:1年以上
[该文章底部包含文件资源,可根据自己情况,决定是否下载资源使用,时间>金钱,如有需要,立即查看资源]

以下是 jQuery移动设备图片裁剪代码 的示例演示效果:

当前平台(PC电脑)
  • 平台:

部分效果截图:

jQuery移动设备图片裁剪代码

HTML代码(index.html):

<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="format-detection" content="telephone=no, email=no" />
<title>jQuery移动设备图片裁剪代码</title>
<link rel="stylesheet" type="text/css" href="css/default.css">
<style>
#clipArea {
	margin: 20px;
	height: 300px;
}
#file,
#clipBtn {
	margin: 20px;
}
#view {
	margin: 0 auto;
	width: 200px;
	height: 200px;
}
</style>

</head>
<body ontouchstart="">

<article class="zzsc-container">
	<div id="clipArea"></div>
	<input type="file" id="file">
	<button id="clipBtn">截取</button>
	<div id="view"></div>
</article>

<script src="js/jquery-2.1.1.min.js" type="text/javascript"></script>
<script src="js/iscroll-zoom.js"></script>
<script src="js/hammer.js"></script>
<script src="js/jquery.photoClip.js"></script>
<script>
//document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
$("#clipArea").photoClip({
	width: 200,
	height: 200,
	file: "#file",
	view: "#view",
	ok: "#clipBtn",
	loadStart: function() {
		console.log("照片读取中");
	},
	loadComplete: function() {
		console.log("照片读取完成");
	},
	clipFinish: function(dataURL) {
		console.log(dataURL);
	}
});
</script>
</body>
</html>

JS代码(hammer.js):

/*! Hammer.JS - v2.0.4 - 2014-09-28 * http://hammerjs.github.io/ * * Copyright (c) 2014 Jorik Tangelder;
	* Licensed under the MIT license */
(function(window,document,exportName,undefined){
	'use strict';
	var VENDOR_PREFIXES = ['','webkit','moz','MS','ms','o'];
	var TEST_ELEMENT = document.createElement('div');
	var TYPE_FUNCTION = 'function';
	var round = Math.round;
	var abs = Math.abs;
	var now = Date.now;
	/** * set a timeout with a given scope * @param{
	Function}
fn * @param{
	Number}
timeout * @param{
	Object}
context * @returns{
	number}
*/
function setTimeoutContext(fn,timeout,context){
	return setTimeout(bindFn(fn,context),timeout);
}
/** * if the argument is an array,we want to execute the fn on each entry * if it aint an array we don't want to do a thing. * this is used by all the methods that accept a single and array argument. * @param{
	*|Array}
arg * @param{
	String}
fn * @param{
	Object}
[context] * @returns{
	Boolean}
*/
function invokeArrayArg(arg,fn,context){
	if (Array.isArray(arg)){
	each(arg,context[fn],context);
	return true;
}
return false;
}
/** * walk objects and arrays * @param{
	Object}
obj * @param{
	Function}
iterator * @param{
	Object}
context */
function each(obj,iterator,context){
	var i;
	if (!obj){
	return;
}
if (obj.forEach){
	obj.forEach(iterator,context);
}
else if (obj.length !== undefined){
	i = 0;
	while (i < obj.length){
	iterator.call(context,obj[i],i,obj);
	i++;
}
}
else{
	for (i in obj){
	obj.hasOwnProperty(i) && iterator.call(context,obj[i],i,obj);
}
}
}
/** * extend object. * means that properties in dest will be overwritten by the ones in src. * @param{
	Object}
dest * @param{
	Object}
src * @param{
	Boolean}
[merge] * @returns{
	Object}
dest */
function extend(dest,src,merge){
	var keys = Object.keys(src);
	var i = 0;
	while (i < keys.length){
	if (!merge || (merge && dest[keys[i]] === undefined)){
	dest[keys[i]] = src[keys[i]];
}
i++;
}
return dest;
}
/** * merge the values from src in the dest. * means that properties that exist in dest will not be overwritten by src * @param{
	Object}
dest * @param{
	Object}
src * @returns{
	Object}
dest */
function merge(dest,src){
	return extend(dest,src,true);
}
/** * simple class inheritance * @param{
	Function}
child * @param{
	Function}
base * @param{
	Object}
[properties] */
function inherit(child,base,properties){
	var baseP = base.prototype,childP;
	childP = child.prototype = Object.create(baseP);
	childP.constructor = child;
	childP._super = baseP;
	if (properties){
	extend(childP,properties);
}
}
/** * simple function bind * @param{
	Function}
fn * @param{
	Object}
context * @returns{
	Function}
*/
function bindFn(fn,context){
	return function boundFn(){
	return fn.apply(context,arguments);
}
;
}
/** * let a boolean value also be a function that must return a boolean * this first item in args will be used as the context * @param{
	Boolean|Function}
val * @param{
	Array}
[args] * @returns{
	Boolean}
*/
function boolOrFn(val,args){
	if (typeof val == TYPE_FUNCTION){
	return val.apply(args ? args[0] || undefined:undefined,args);
}
return val;
}
/** * use the val2 when val1 is undefined * @param{
	*}
val1 * @param{
	*}
val2 * @returns{
	*}
*/
function ifUndefined(val1,val2){
	return (val1 === undefined) ? val2:val1;
}
/** * addEventListener with multiple events at once * @param{
	EventTarget}
target * @param{
	String}
types * @param{
	Function}
handler */
function addEventListeners(target,types,handler){
	each(splitStr(types),function(type){
	target.addEventListener(type,handler,false);
}
);
}
/** * removeEventListener with multiple events at once * @param{
	EventTarget}
target * @param{
	String}
types * @param{
	Function}
handler */
function removeEventListeners(target,types,handler){
	each(splitStr(types),function(type){
	target.removeEventListener(type,handler,false);
}
);
}
/** * find if a node is in the given parent * @method hasParent * @param{
	HTMLElement}
node * @param{
	HTMLElement}
parent * @return{
	Boolean}
found */
function hasParent(node,parent){
	while (node){
	if (node == parent){
	return true;
}
node = node.parentNode;
}
return false;
}
/** * small indexOf wrapper * @param{
	String}
str * @param{
	String}
find * @returns{
	Boolean}
found */
function inStr(str,find){
	return str.indexOf(find) > -1;
}
/** * split string on whitespace * @param{
	String}
str * @returns{
	Array}
words */
function splitStr(str){
	return str.trim().split(/\s+/g);
}
/** * find if a array contains the object using indexOf or a simple polyFill * @param{
	Array}
src * @param{
	String}
find * @param{
	String}
[findByKey] * @return{
	Boolean|Number}
false when not found,or the index */
function inArray(src,find,findByKey){
	if (src.indexOf && !findByKey){
	return src.indexOf(find);
}
else{
	var i = 0;
	while (i < src.length){
	if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)){
	return i;
}
i++;
}
return -1;
}
}
/** * convert array-like objects to real arrays * @param{
	Object}
obj * @returns{
	Array}
*/
function toArray(obj){
	return Array.prototype.slice.call(obj,0);
}
/** * unique array with objects based on a key (like 'id') or just by the array's value * @param{
	Array}
src [{
	id:1}
,{
	id:2}
,{
	id:1}
] * @param{
	String}
[key] * @param{
	Boolean}
[sort=False] * @returns{
	Array}
[{
	id:1}
,{
	id:2}
] */
function uniqueArray(src,key,sort){
	var results = [];
	var values = [];
	var i = 0;
	while (i < src.length){
	var val = key ? src[i][key]:src[i];
	if (inArray(values,val) < 0){
	results.push(src[i]);
}
values[i] = val;
	i++;
}
if (sort){
	if (!key){
	results = results.sort();
}
else{
	results = results.sort(function sortUniqueArray(a,b){
	return a[key] > b[key];
}
);
}
}
return results;
}
/** * get the prefixed property * @param{
	Object}
obj * @param{
	String}
property * @returns{
	String|Undefined}
prefixed */
function prefixed(obj,property){
	var prefix,prop;
	var camelProp = property[0].toUpperCase() + property.slice(1);
	var i = 0;
	while (i < VENDOR_PREFIXES.length){
	prefix = VENDOR_PREFIXES[i];
	prop = (prefix) ? prefix + camelProp:property;
	if (prop in obj){
	return prop;
}
i++;
}
return undefined;
}
/** * get a unique id * @returns{
	number}
uniqueId */
var _uniqueId = 1;
	function uniqueId(){
	return _uniqueId++;
}
/** * get the window object of an element * @param{
	HTMLElement}
element * @returns{
	DocumentView|Window}
*/
function getWindowForElement(element){
	var doc = element.ownerDocument;
	return (doc.defaultView || doc.parentWindow);
}
var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
	var SUPPORT_TOUCH = ('ontouchstart' in window);
	var SUPPORT_POINTER_EVENTS = prefixed(window,'PointerEvent') !== undefined;
	var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
	var INPUT_TYPE_TOUCH = 'touch';
	var INPUT_TYPE_PEN = 'pen';
	var INPUT_TYPE_MOUSE = 'mouse';
	var INPUT_TYPE_KINECT = 'kinect';
	var COMPUTE_INTERVAL = 25;
	var INPUT_START = 1;
	var INPUT_MOVE = 2;
	var INPUT_END = 4;
	var INPUT_CANCEL = 8;
	var DIRECTION_NONE = 1;
	var DIRECTION_LEFT = 2;
	var DIRECTION_RIGHT = 4;
	var DIRECTION_UP = 8;
	var DIRECTION_DOWN = 16;
	var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
	var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
	var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
	var PROPS_XY = ['x','y'];
	var PROPS_CLIENT_XY = ['clientX','clientY'];
	/** * create new input type manager * @param{
	Manager}
manager * @param{
	Function}
callback * @returns{
	Input}
* @constructor */
function Input(manager,callback){
	var self = this;
	this.manager = manager;
	this.callback = callback;
	this.element = manager.element;
	this.target = manager.options.inputTarget;
	// smaller wrapper around the handler,for the scope and the enabled state of the manager,// so when disabled the input events are completely bypassed. this.domHandler = function(ev){
	if (boolOrFn(manager.options.enable,[manager])){
	self.handler(ev);
}
}
;
	this.init();
}
Input.prototype ={
	/** * should handle the inputEvent data and trigger the callback * @virtual */
 handler:function(){
}
,/** * bind the events */
 init:function(){
	this.evEl && addEventListeners(this.element,this.evEl,this.domHandler);
	this.evTarget && addEventListeners(this.target,this.evTarget,this.domHandler);
	this.evWin && addEventListeners(getWindowForElement(this.element),this.evWin,this.domHandler);
}
,/** * unbind the events */
 destroy:function(){
	this.evEl && removeEventListeners(this.element,this.evEl,this.domHandler);
	this.evTarget && removeEventListeners(this.target,this.evTarget,this.domHandler);
	this.evWin && removeEventListeners(getWindowForElement(this.element),this.evWin,this.domHandler);
}
}
;
	/** * create new input type manager * called by the Manager constructor * @param{
	Hammer}
manager * @returns{
	Input}
*/
function createInputInstance(manager){
	var Type;
	var inputClass = manager.options.inputClass;
	if (inputClass){
	Type = inputClass;
}
else if (SUPPORT_POINTER_EVENTS){
	Type = PointerEventInput;
}
else if (SUPPORT_ONLY_TOUCH){
	Type = TouchInput;
}
else if (!SUPPORT_TOUCH){
	Type = MouseInput;
}
else{
	Type = TouchMouseInput;
}
return new (Type)(manager,inputHandler);
}
/** * handle input events * @param{
	Manager}
manager * @param{
	String}
eventType * @param{
	Object}
input */
function inputHandler(manager,eventType,input){
	var pointersLen = input.pointers.length;
	var changedPointersLen = input.changedPointers.length;
	var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
	var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
	input.isFirst = !!isFirst;
	input.isFinal = !!isFinal;
	if (isFirst){
	manager.session ={
}
;
}
// source event is the normalized value of the domEvents // like 'touchstart,mouseup,pointerdown' input.eventType = eventType;
	// compute scale,rotation etc computeInputData(manager,input);
	// emit secret event manager.emit('hammer.input',input);
	manager.recognize(input);
	manager.session.prevInput = input;
}
/** * extend the data with some usable properties like scale,rotate,velocity etc * @param{
	Object}
manager * @param{
	Object}
input */
function computeInputData(manager,input){
	var session = manager.session;
	var pointers = input.pointers;
	var pointersLength = pointers.length;
	// store the first input to calculate the distance and direction if (!session.firstInput){
	session.firstInput = simpleCloneInputData(input);
}
// to compute scale and rotation we need to store the multiple touches if (pointersLength > 1 && !session.firstMultiple){
	session.firstMultiple = simpleCloneInputData(input);
}
else if (pointersLength === 1){
	session.firstMultiple = false;
}
var firstInput = session.firstInput;
	var firstMultiple = session.firstMultiple;
	var offsetCenter = firstMultiple ? firstMultiple.center:firstInput.center;
	var center = input.center = getCenter(pointers);
	input.timeStamp = now();
	input.deltaTime = input.timeStamp - firstInput.timeStamp;
	input.angle = getAngle(offsetCenter,center);
	input.distance = getDistance(offsetCenter,center);
	computeDeltaXY(session,input);
	input.offsetDirection = getDirection(input.deltaX,input.deltaY);
	input.scale = firstMultiple ? getScale(firstMultiple.pointers,pointers):1;
	input.rotation = firstMultiple ? getRotation(firstMultiple.pointers,pointers):0;
	computeIntervalInputData(session,input);
	// find the correct target var target = manager.element;
	if (hasParent(input.srcEvent.target,target)){
	target = input.srcEvent.target;
}
input.target = target;
}
function computeDeltaXY(session,input){
	var center = input.center;
	var offset = session.offsetDelta ||{
}
;
	var prevDelta = session.prevDelta ||{
}
;
	var prevInput = session.prevInput ||{
}
;
	if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END){
	prevDelta = session.prevDelta ={
	x:prevInput.deltaX || 0,y:prevInput.deltaY || 0}
;
	offset = session.offsetDelta ={
	x:center.x,y:center.y}
;
}
input.deltaX = prevDelta.x + (center.x - offset.x);
	input.deltaY = prevDelta.y + (center.y - offset.y);
}
/** * velocity is calculated every x ms * @param{
	Object}
session * @param{
	Object}
input */
function computeIntervalInputData(session,input){
	var last = session.lastInterval || input,deltaTime = input.timeStamp - last.timeStamp,velocity,velocityX,velocityY,direction;
	if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)){
	var deltaX = last.deltaX - input.deltaX;
	var deltaY = last.deltaY - input.deltaY;
	var v = getVelocity(deltaTime,deltaX,deltaY);
	velocityX = v.x;
	velocityY = v.y;
	velocity = (abs(v.x) > abs(v.y)) ? v.x:v.y;
	direction = getDirection(deltaX,deltaY);
	session.lastInterval = input;
}
else{
	// use latest velocity info if it doesn't overtake a minimum period velocity = last.velocity;
	velocityX = last.velocityX;
	velocityY = last.velocityY;
	direction = last.direction;
}
input.velocity = velocity;
	input.velocityX = velocityX;
	input.velocityY = velocityY;
	input.direction = direction;
}
/** * create a simple clone from the input used for storage of firstInput and firstMultiple * @param{
	Object}
input * @returns{
	Object}
clonedInputData */
function simpleCloneInputData(input){
	// make a simple copy of the pointers because we will get a reference if we don't // we only need clientXY for the calculations var pointers = [];
	var i = 0;
	while (i < input.pointers.length){
	pointers[i] ={
	clientX:round(input.pointers[i].clientX),clientY:round(input.pointers[i].clientY)}
;
	i++;
}
return{
	timeStamp:now(),pointers:pointers,center:getCenter(pointers),deltaX:input.deltaX,deltaY:input.deltaY}
;
}
/** * get the center of all the pointers * @param{
	Array}
pointers * @return{
	Object}
center contains `x` and `y` properties */
function getCenter(pointers){
	var pointersLength = pointers.length;
	// no need to loop when only one touch if (pointersLength === 1){
	return{
	x:round(pointers[0].clientX),y:round(pointers[0].clientY)}
;
}
var x = 0,y = 0,i = 0;
	while (i < pointersLength){
	x += pointers[i].clientX;
	y += pointers[i].clientY;
	i++;
}
return{
	x:round(x / pointersLength),y:round(y / pointersLength)}
;
}
/** * calculate the velocity between two points. unit is in px per ms. * @param{
	Number}
deltaTime * @param{
	Number}
x * @param{
	Number}
y * @return{
	Object}
velocity `x` and `y` */
function getVelocity(deltaTime,x,y){
	return{
	x:x / deltaTime || 0,y:y / deltaTime || 0}
;
}
/** * get the direction between two points * @param{
	Number}
x * @param{
	Number}
y * @return{
	Number}
direction */
function getDirection(x,y){
	if (x === y){
	return DIRECTION_NONE;
}
if (abs(x) >= abs(y)){
	return x > 0 ? DIRECTION_LEFT:DIRECTION_RIGHT;
}
return y > 0 ? DIRECTION_UP:DIRECTION_DOWN;
}
/** * calculate the absolute distance between two points * @param{
	Object}
p1{
	x,y}
* @param{
	Object}
p2{
	x,y}
* @param{
	Array}
[props] containing x and y keys * @return{
	Number}
distance */
function getDistance(p1,p2,props){
	if (!props){
	props = PROPS_XY;
}
var x = p2[props[0]] - p1[props[0]],y = p2[props[1]] - p1[props[1]];
	return Math.sqrt((x * x) + (y * y));
}
/** * calculate the angle between two coordinates * @param{
	Object}
p1 * @param{
	Object}
p2 * @param{
	Array}
[props] containing x and y keys * @return{
	Number}
angle */
function getAngle(p1,p2,props){
	if (!props){
	props = PROPS_XY;
}
var x = p2[props[0]] - p1[props[0]],y = p2[props[1]] - p1[props[1]];
	return Math.atan2(y,x) * 180 / Math.PI;
}
/** * calculate the rotation degrees between two pointersets * @param{
	Array}
start array of pointers * @param{
	Array}
end array of pointers * @return{
	Number}
rotation */
function getRotation(start,end){
	return getAngle(end[1],end[0],PROPS_CLIENT_XY) - getAngle(start[1],start[0],PROPS_CLIENT_XY);
}
/** * calculate the scale factor between two pointersets * no scale is 1,and goes down to 0 when pinched together,and bigger when pinched out * @param{
	Array}
start array of pointers * @param{
	Array}
end array of pointers * @return{
	Number}
scale */
function getScale(start,end){
	return getDistance(end[0],end[1],PROPS_CLIENT_XY) / getDistance(start[0],start[1],PROPS_CLIENT_XY);
}
var MOUSE_INPUT_MAP ={
	mousedown:INPUT_START,mousemove:INPUT_MOVE,mouseup:INPUT_END}
;
	var MOUSE_ELEMENT_EVENTS = 'mousedown';
	var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
	/** * Mouse events input * @constructor * @extends Input */
function MouseInput(){
	this.evEl = MOUSE_ELEMENT_EVENTS;
	this.evWin = MOUSE_WINDOW_EVENTS;
	this.allow = true;
	// used by Input.TouchMouse to disable mouse events this.pressed = false;
	// mousedown state Input.apply(this,arguments);
}
inherit(MouseInput,Input,{
	/** * handle mouse events * @param{
	Object}
ev */
 handler:function MEhandler(ev){
	var eventType = MOUSE_INPUT_MAP[ev.type];
	// on start we want to have the left mouse button down if (eventType & INPUT_START && ev.button === 0){
	this.pressed = true;
}
if (eventType & INPUT_MOVE && ev.which !== 1){
	eventType = INPUT_END;
}
// mouse must be down,and mouse events are allowed (see the TouchMouse input) if (!this.pressed || !this.allow){
	return;
}
if (eventType & INPUT_END){
	this.pressed = false;
}
this.callback(this.manager,eventType,{
	pointers:[ev],changedPointers:[ev],pointerType:INPUT_TYPE_MOUSE,srcEvent:ev}
);
}
}
);
	var POINTER_INPUT_MAP ={
	pointerdown:INPUT_START,pointermove:INPUT_MOVE,pointerup:INPUT_END,pointercancel:INPUT_CANCEL,pointerout:INPUT_CANCEL}
;
	// in IE10 the pointer types is defined as an enumvar IE10_POINTER_TYPE_ENUM ={
	2:INPUT_TYPE_TOUCH,3:INPUT_TYPE_PEN,4:INPUT_TYPE_MOUSE,5:INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816}
;
	var POINTER_ELEMENT_EVENTS = 'pointerdown';
	var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
	// IE10 has prefixed support,and case-sensitiveif (window.MSPointerEvent){
	POINTER_ELEMENT_EVENTS = 'MSPointerDown';
	POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
}
/** * Pointer events input * @constructor * @extends Input */
function PointerEventInput(){
	this.evEl = POINTER_ELEMENT_EVENTS;
	this.evWin = POINTER_WINDOW_EVENTS;
	Input.apply(this,arguments);
	this.store = (this.manager.session.pointerEvents = []);
}
inherit(PointerEventInput,Input,{
	/** * handle mouse events * @param{
	Object}
ev */
 handler:function PEhandler(ev){
	var store = this.store;
	var removePointer = false;
	var eventTypeNormalized = ev.type.toLowerCase().replace('ms','');
	var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
	var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
	var isTouch = (pointerType == INPUT_TYPE_TOUCH);
	// get index of the event in the store var storeIndex = inArray(store,ev.pointerId,'pointerId');
	// start and mouse must be down if (eventType & INPUT_START && (ev.button === 0 || isTouch)){
	if (storeIndex < 0){
	store.push(ev);
	storeIndex = store.length - 1;
}
}
else if (eventType & (INPUT_END | INPUT_CANCEL)){
	removePointer = true;
}
// it not found,so the pointer hasn't been down (so it's probably a hover) if (storeIndex < 0){
	return;
}
// update the event in the store store[storeIndex] = ev;
	this.callback(this.manager,eventType,{
	pointers:store,changedPointers:[ev],pointerType:pointerType,srcEvent:ev}
);
	if (removePointer){
	// remove from the store store.splice(storeIndex,1);
}
}
}
);
	var SINGLE_TOUCH_INPUT_MAP ={
	touchstart:INPUT_START,touchmove:INPUT_MOVE,touchend:INPUT_END,touchcancel:INPUT_CANCEL}
;
	var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
	var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
	/** * Touch events input * @constructor * @extends Input */
function SingleTouchInput(){
	this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
	this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
	this.started = false;
	Input.apply(this,arguments);
}
inherit(SingleTouchInput,Input,{
	handler:function TEhandler(ev){
	var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
	// should we handle the touch events? if (type === INPUT_START){
	this.started = true;
}
if (!this.started){
	return;
}
var touches = normalizeSingleTouches.call(this,ev,type);
	// when done,reset the started state if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0){
	this.started = false;
}
this.callback(this.manager,type,{
	pointers:touches[0],changedPointers:touches[1],pointerType:INPUT_TYPE_TOUCH,srcEvent:ev}
);
}
}
);
	/** * @this{
	TouchInput}
* @param{
	Object}
ev * @param{
	Number}
type flag * @returns{
	undefined|Array}
[all,changed] */
function normalizeSingleTouches(ev,type){
	var all = toArray(ev.touches);
	var changed = toArray(ev.changedTouches);
	if (type & (INPUT_END | INPUT_CANCEL)){
	all = uniqueArray(all.concat(changed),'identifier',true);
}
return [all,changed];
}
var TOUCH_INPUT_MAP ={
	touchstart:INPUT_START,touchmove:INPUT_MOVE,touchend:INPUT_END,touchcancel:INPUT_CANCEL}
;
	var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
	/** * Multi-user touch events input * @constructor * @extends Input */
function TouchInput(){
	this.evTarget = TOUCH_TARGET_EVENTS;
	this.targetIds ={
}
;
	Input.apply(this,arguments);
}
inherit(TouchInput,Input,{
	handler:function MTEhandler(ev){
	var type = TOUCH_INPUT_MAP[ev.type];
	var touches = getTouches.call(this,ev,type);
	if (!touches){
	return;
}
this.callback(this.manager,type,{
	pointers:touches[0],changedPointers:touches[1],pointerType:INPUT_TYPE_TOUCH,srcEvent:ev}
);
}
}
);
	/** * @this{
	TouchInput}
* @param{
	Object}
ev * @param{
	Number}
type flag * @returns{
	undefined|Array}
[all,changed] */
function getTouches(ev,type){
	var allTouches = toArray(ev.touches);
	var targetIds = this.targetIds;
	// when there is only one touch,the process can be simplified if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1){
	targetIds[allTouches[0].identifier] = true;
	return [allTouches,allTouches];
}
var i,targetTouches,changedTouches = toArray(ev.changedTouches),changedTargetTouches = [],target = this.target;
	// get target touches from touches targetTouches = allTouches.filter(function(touch){
	return hasParent(touch.target,target);
}
);
	// collect touches if (type === INPUT_START){
	i = 0;
	while (i < targetTouches.length){
	targetIds[targetTouches[i].identifier] = true;
	i++;
}
}
// filter changed touches to only contain touches that exist in the collected target ids i = 0;
	while (i < changedTouches.length){
	if (targetIds[changedTouches[i].identifier]){
	changedTargetTouches.push(changedTouches[i]);
}
// cleanup removed touches if (type & (INPUT_END | INPUT_CANCEL)){
	delete targetIds[changedTouches[i].identifier];
}
i++;
}
if (!changedTargetTouches.length){
	return;
}
return [ // merge targetTouches with changedTargetTouches so it contains ALL touches,including 'end' and 'cancel' uniqueArray(targetTouches.concat(changedTargetTouches),'identifier',true),changedTargetTouches ];
}
/** * Combined touch and mouse input * * Touch has a higher priority then mouse,and while touching no mouse events are allowed. * This because touch devices also emit mouse events while doing a touch. * * @constructor * @extends Input */
function TouchMouseInput(){
	Input.apply(this,arguments);
	var handler = bindFn(this.handler,this);
	this.touch = new TouchInput(this.manager,handler);
	this.mouse = new MouseInput(this.manager,handler);
}
inherit(TouchMouseInput,Input,{
	/** * handle mouse and touch events * @param{
	Hammer}
manager * @param{
	String}
inputEvent * @param{
	Object}
inputData */
 handler:function TMEhandler(manager,inputEvent,inputData){
	var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
	// when we're in a touch event,so block all upcoming mouse events // most mobile browser also emit mouseevents,right after touchstart if (isTouch){
	this.mouse.allow = false;
}
else if (isMouse && !this.mouse.allow){
	return;
}
// reset the allowMouse when we're done if (inputEvent & (INPUT_END | INPUT_CANCEL)){
	this.mouse.allow = true;
}
this.callback(manager,inputEvent,inputData);
}
,/** * remove the event listeners */
 destroy:function destroy(){
	this.touch.destroy();
	this.mouse.destroy();
}
}
);
	var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style,'touchAction');
	var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
	// magical touchAction valuevar TOUCH_ACTION_COMPUTE = 'compute';
	var TOUCH_ACTION_AUTO = 'auto';
	var TOUCH_ACTION_MANIPULATION = 'manipulation';
	// not implementedvar TOUCH_ACTION_NONE = 'none';
	var TOUCH_ACTION_PAN_X = 'pan-x';
	var TOUCH_ACTION_PAN_Y = 'pan-y';
	/** * Touch Action * sets the touchAction property or uses the js alternative * @param{
	Manager}
manager * @param{
	String}
value * @constructor */
function TouchAction(manager,value){
	this.manager = manager;
	this.set(value);
}
TouchAction.prototype ={
	/** * set the touchAction value on the element or enable the polyfill * @param{
	String}
value */
 set:function(value){
	// find out the touch-action by the event handlers if (value == TOUCH_ACTION_COMPUTE){
	value = this.compute();
}
if (NATIVE_TOUCH_ACTION){
	this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
}
this.actions = value.toLowerCase().trim();
}
,/** * just re-set the touchAction value */
 update:function(){
	this.set(this.manager.options.touchAction);
}
,/** * compute the value for the touchAction property based on the recognizer's settings * @returns{
	String}
value */
 compute:function(){
	var actions = [];
	each(this.manager.recognizers,function(recognizer){
	if (boolOrFn(recognizer.options.enable,[recognizer])){
	actions = actions.concat(recognizer.getTouchAction());
}
}
);
	return cleanTouchActions(actions.join(' '));
}
,/** * this method is called on each input cycle and provides the preventing of the browser behavior * @param{
	Object}
input */
 preventDefaults:function(input){
	// not needed with native support for the touchAction property if (NATIVE_TOUCH_ACTION){
	return;
}
var srcEvent = input.srcEvent;
	var direction = input.offsetDirection;
	// if the touch action did prevented once this session if (this.manager.session.prevented){
	srcEvent.preventDefault();
	return;
}
var actions = this.actions;
	var hasNone = inStr(actions,TOUCH_ACTION_NONE);
	var hasPanY = inStr(actions,TOUCH_ACTION_PAN_Y);
	var hasPanX = inStr(actions,TOUCH_ACTION_PAN_X);
	if (hasNone || (hasPanY && direction & DIRECTION_HORIZONTAL) || (hasPanX && direction & DIRECTION_VERTICAL)){
	return this.preventSrc(srcEvent);
}
}
,/** * call preventDefault to prevent the browser's default behavior (scrolling in most cases) * @param{
	Object}
srcEvent */
 preventSrc:function(srcEvent){
	this.manager.session.prevented = true;
	srcEvent.preventDefault();
}
}
;
	/** * when the touchActions are collected they are not a valid value,so we need to clean things up. * * @param{
	String}
actions * @returns{
	*}
*/
function cleanTouchActions(actions){
	// none if (inStr(actions,TOUCH_ACTION_NONE)){
	return TOUCH_ACTION_NONE;
}
var hasPanX = inStr(actions,TOUCH_ACTION_PAN_X);
	var hasPanY = inStr(actions,TOUCH_ACTION_PAN_Y);
	// pan-x and pan-y can be combined if (hasPanX && hasPanY){
	return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
}
// pan-x OR pan-y if (hasPanX || hasPanY){
	return hasPanX ? TOUCH_ACTION_PAN_X:TOUCH_ACTION_PAN_Y;
}
// manipulation if (inStr(actions,TOUCH_ACTION_MANIPULATION)){
	return TOUCH_ACTION_MANIPULATION;
}
return TOUCH_ACTION_AUTO;
}
/** * Recognizer flow explained;
	* * All recognizers have the initial state of POSSIBLE when a input session starts. * The definition of a input session is from the first input until the last input,with all it's movement in it. * * Example session for mouse-input:mousedown -> mousemove -> mouseup * * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed * which determines with state it should be. * * If the recognizer has the state FAILED,CANCELLED or RECOGNIZED (equals ENDED),it is reset to * POSSIBLE to give it another change on the next cycle. * * Possible * | * +-----+---------------+ * | | * +-----+-----+ | * | | | * Failed Cancelled | * +-------+------+ * | | * Recognized Began * | * Changed * | * Ended/Recognized */
var STATE_POSSIBLE = 1;
	var STATE_BEGAN = 2;
	var STATE_CHANGED = 4;
	var STATE_ENDED = 8;
	var STATE_RECOGNIZED = STATE_ENDED;
	var STATE_CANCELLED = 16;
	var STATE_FAILED = 32;
	/** * Recognizer * Every recognizer needs to extend from this class. * @constructor * @param{
	Object}
options */
function Recognizer(options){
	this.id = uniqueId();
	this.manager = null;
	this.options = merge(options ||{
}
,this.defaults);
	// default is enable true this.options.enable = ifUndefined(this.options.enable,true);
	this.state = STATE_POSSIBLE;
	this.simultaneous ={
}
;
	this.requireFail = [];
}
Recognizer.prototype ={
	/** * @virtual * @type{
	Object}
*/
 defaults:{
}
,/** * set options * @param{
	Object}
options * @return{
	Recognizer}
*/
 set:function(options){
	extend(this.options,options);
	// also update the touchAction,in case something changed about the directions/enabled state this.manager && this.manager.touchAction.update();
	return this;
}
,/** * recognize simultaneous with an other recognizer. * @param{
	Recognizer}
otherRecognizer * @returns{
	Recognizer}
this */
 recognizeWith:function(otherRecognizer){
	if (invokeArrayArg(otherRecognizer,'recognizeWith',this)){
	return this;
}
var simultaneous = this.simultaneous;
	otherRecognizer = getRecognizerByNameIfManager(otherRecognizer,this);
	if (!simultaneous[otherRecognizer.id]){
	simultaneous[otherRecognizer.id] = otherRecognizer;
	otherRecognizer.recognizeWith(this);
}
return this;
}
,/** * drop the simultaneous link. it doesnt remove the link on the other recognizer. * @param{
	Recognizer}
otherRecognizer * @returns{
	Recognizer}
this */
 dropRecognizeWith:function(otherRecognizer){
	if (invokeArrayArg(otherRecognizer,'dropRecognizeWith',this)){
	return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer,this);
	delete this.simultaneous[otherRecognizer.id];
	return this;
}
,/** * recognizer can only run when an other is failing * @param{
	Recognizer}
otherRecognizer * @returns{
	Recognizer}
this */
 requireFailure:function(otherRecognizer){
	if (invokeArrayArg(otherRecognizer,'requireFailure',this)){
	return this;
}
var requireFail = this.requireFail;
	otherRecognizer = getRecognizerByNameIfManager(otherRecognizer,this);
	if (inArray(requireFail,otherRecognizer) === -1){
	requireFail.push(otherRecognizer);
	otherRecognizer.requireFailure(this);
}
return this;
}
,/** * drop the requireFailure link. it does not remove the link on the other recognizer. * @param{
	Recognizer}
otherRecognizer * @returns{
	Recognizer}
this */
 dropRequireFailure:function(otherRecognizer){
	if (invokeArrayArg(otherRecognizer,'dropRequireFailure',this)){
	return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer,this);
	var index = inArray(this.requireFail,otherRecognizer);
	if (index > -1){
	this.requireFail.splice(index,1);
}
return this;
}
,/** * has require failures boolean * @returns{
	boolean}
*/
 hasRequireFailures:function(){
	return this.requireFail.length > 0;
}
,/** * if the recognizer can recognize simultaneous with an other recognizer * @param{
	Recognizer}
otherRecognizer * @returns{
	Boolean}
*/
 canRecognizeWith:function(otherRecognizer){
	return !!this.simultaneous[otherRecognizer.id];
}
,/** * You should use `tryEmit` instead of `emit` directly to check * that all the needed recognizers has failed before emitting. * @param{
	Object}
input */
 emit:function(input){
	var self = this;
	var state = this.state;
	function emit(withState){
	self.manager.emit(self.options.event + (withState ? stateStr(state):''),input);
}
// 'panstart' and 'panmove' if (state < STATE_ENDED){
	emit(true);
}
emit();
	// simple 'eventName' events // panend and pancancel if (state >= STATE_ENDED){
	emit(true);
}
}
,/** * Check that all the require failure recognizers has failed,* if true,it emits a gesture event,* otherwise,setup the state to FAILED. * @param{
	Object}
input */
 tryEmit:function(input){
	if (this.canEmit()){
	return this.emit(input);
}
// it's failing anyway this.state = STATE_FAILED;
}
,/** * can we emit? * @returns{
	boolean}
*/
 canEmit:function(){
	var i = 0;
	while (i < this.requireFail.length){
	if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))){
	return false;
}
i++;
}
return true;
}
,/** * update the recognizer * @param{
	Object}
inputData */
 recognize:function(inputData){
	// make a new copy of the inputData // so we can change the inputData without messing up the other recognizers var inputDataClone = extend({
}
,inputData);
	// is is enabled and allow recognizing? if (!boolOrFn(this.options.enable,[this,inputDataClone])){
	this.reset();
	this.state = STATE_FAILED;
	return;
}
// reset when we've reached the end if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)){
	this.state = STATE_POSSIBLE;
}
this.state = this.process(inputDataClone);
	// the recognizer has recognized a gesture // so trigger an event if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)){
	this.tryEmit(inputDataClone);
}
}
,/** * return the state of the recognizer * the actual recognizing happens in this method * @virtual * @param{
	Object}
inputData * @returns{
	Const}
STATE */
 process:function(inputData){
}
,// jshint ignore:line /** * return the preferred touch-action * @virtual * @returns{
	Array}
*/
 getTouchAction:function(){
}
,/** * called when the gesture isn't allowed to recognize * like when another is being recognized or it is disabled * @virtual */
 reset:function(){
}
}
;
	/** * get a usable string,used as event postfix * @param{
	Const}
state * @returns{
	String}
state */
function stateStr(state){
	if (state & STATE_CANCELLED){
	return 'cancel';
}
else if (state & STATE_ENDED){
	return 'end';
}
else if (state & STATE_CHANGED){
	return 'move';
}
else if (state & STATE_BEGAN){
	return 'start';
}
return '';
}
/** * direction cons to string * @param{
	Const}
direction * @returns{
	String}
*/
function directionStr(direction){
	if (direction == DIRECTION_DOWN){
	return 'down';
}
else if (direction == DIRECTION_UP){
	return 'up';
}
else if (direction == DIRECTION_LEFT){
	return 'left';
}
else if (direction == DIRECTION_RIGHT){
	return 'right';
}
return '';
}
/** * get a recognizer by name if it is bound to a manager * @param{
	Recognizer|String}
otherRecognizer * @param{
	Recognizer}
recognizer * @returns{
	Recognizer}
*/
function getRecognizerByNameIfManager(otherRecognizer,recognizer){
	var manager = recognizer.manager;
	if (manager){
	return manager.get(otherRecognizer);
}
return otherRecognizer;
}
/** * This recognizer is just used as a base for the simple attribute recognizers. * @constructor * @extends Recognizer */
function AttrRecognizer(){
	Recognizer.apply(this,arguments);
}
inherit(AttrRecognizer,Recognizer,{
	/** * @namespace * @memberof AttrRecognizer */
 defaults:{
	/** * @type{
	Number}
* @default 1 */
 pointers:1}
,/** * Used to check if it the recognizer receives valid input,like input.distance > 10. * @memberof AttrRecognizer * @param{
	Object}
input * @returns{
	Boolean}
recognized */
 attrTest:function(input){
	var optionPointers = this.options.pointers;
	return optionPointers === 0 || input.pointers.length === optionPointers;
}
,/** * Process the input and return the state for the recognizer * @memberof AttrRecognizer * @param{
	Object}
input * @returns{
	*}
State */
 process:function(input){
	var state = this.state;
	var eventType = input.eventType;
	var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
	var isValid = this.attrTest(input);
	// on cancel input and we've recognized before,return STATE_CANCELLED if (isRecognized && (eventType & INPUT_CANCEL || !isValid)){
	return state | STATE_CANCELLED;
}
else if (isRecognized || isValid){
	if (eventType & INPUT_END){
	return state | STATE_ENDED;
}
else if (!(state & STATE_BEGAN)){
	return STATE_BEGAN;
}
return state | STATE_CHANGED;
}
return STATE_FAILED;
}
}
);
	/** * Pan * Recognized when the pointer is down and moved in the allowed direction. * @constructor * @extends AttrRecognizer */
function PanRecognizer(){
	AttrRecognizer.apply(this,arguments);
	this.pX = null;
	this.pY = null;
}
inherit(PanRecognizer,AttrRecognizer,{
	/** * @namespace * @memberof PanRecognizer */
 defaults:{
	event:'pan',threshold:10,pointers:1,direction:DIRECTION_ALL}
,getTouchAction:function(){
	var direction = this.options.direction;
	var actions = [];
	if (direction & DIRECTION_HORIZONTAL){
	actions.push(TOUCH_ACTION_PAN_Y);
}
if (direction & DIRECTION_VERTICAL){
	actions.push(TOUCH_ACTION_PAN_X);
}
return actions;
}
,directionTest:function(input){
	var options = this.options;
	var hasMoved = true;
	var distance = input.distance;
	var direction = input.direction;
	var x = input.deltaX;
	var y = input.deltaY;
	// lock to axis? if (!(direction & options.direction)){
	if (options.direction & DIRECTION_HORIZONTAL){
	direction = (x === 0) ? DIRECTION_NONE:(x < 0) ? DIRECTION_LEFT:DIRECTION_RIGHT;
	hasMoved = x != this.pX;
	distance = Math.abs(input.deltaX);
}
else{
	direction = (y === 0) ? DIRECTION_NONE:(y < 0) ? DIRECTION_UP:DIRECTION_DOWN;
	hasMoved = y != this.pY;
	distance = Math.abs(input.deltaY);
}
}
input.direction = direction;
	return hasMoved && distance > options.threshold && direction & options.direction;
}
,attrTest:function(input){
	return AttrRecognizer.prototype.attrTest.call(this,input) && (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
}
,emit:function(input){
	this.pX = input.deltaX;
	this.pY = input.deltaY;
	var direction = directionStr(input.direction);
	if (direction){
	this.manager.emit(this.options.event + direction,input);
}
this._super.emit.call(this,input);
}
}
);
	/** * Pinch * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). * @constructor * @extends AttrRecognizer */
function PinchRecognizer(){
	AttrRecognizer.apply(this,arguments);
}
inherit(PinchRecognizer,AttrRecognizer,{
	/** * @namespace * @memberof PinchRecognizer */
 defaults:{
	event:'pinch',threshold:0,pointers:2}
,getTouchAction:function(){
	return [TOUCH_ACTION_NONE];
}
,attrTest:function(input){
	return this._super.attrTest.call(this,input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
}
,emit:function(input){
	this._super.emit.call(this,input);
	if (input.scale !== 1){
	var inOut = input.scale < 1 ? 'in':'out';
	this.manager.emit(this.options.event + inOut,input);
}
}
}
);
	/** * Press * Recognized when the pointer is down for x ms without any movement. * @constructor * @extends Recognizer */
function PressRecognizer(){
	Recognizer.apply(this,arguments);
	this._timer = null;
	this._input = null;
}
inherit(PressRecognizer,Recognizer,{
	/** * @namespace * @memberof PressRecognizer */
 defaults:{
	event:'press',pointers:1,time:500,// minimal time of the pointer to be pressed threshold:5 // a minimal movement is ok,but keep it low}
,getTouchAction:function(){
	return [TOUCH_ACTION_AUTO];
}
,process:function(input){
	var options = this.options;
	var validPointers = input.pointers.length === options.pointers;
	var validMovement = input.distance < options.threshold;
	var validTime = input.deltaTime > options.time;
	this._input = input;
	// we only allow little movement // and we've reached an end event,so a tap is possible if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)){
	this.reset();
}
else if (input.eventType & INPUT_START){
	this.reset();
	this._timer = setTimeoutContext(function(){
	this.state = STATE_RECOGNIZED;
	this.tryEmit();
}
,options.time,this);
}
else if (input.eventType & INPUT_END){
	return STATE_RECOGNIZED;
}
return STATE_FAILED;
}
,reset:function(){
	clearTimeout(this._timer);
}
,emit:function(input){
	if (this.state !== STATE_RECOGNIZED){
	return;
}
if (input && (input.eventType & INPUT_END)){
	this.manager.emit(this.options.event + 'up',input);
}
else{
	this._input.timeStamp = now();
	this.manager.emit(this.options.event,this._input);
}
}
}
);
	/** * Rotate * Recognized when two or more pointer are moving in a circular motion. * @constructor * @extends AttrRecognizer */
function RotateRecognizer(){
	AttrRecognizer.apply(this,arguments);
}
inherit(RotateRecognizer,AttrRecognizer,{
	/** * @namespace * @memberof RotateRecognizer */
 defaults:{
	event:'rotate',threshold:0,pointers:2}
,getTouchAction:function(){
	return [TOUCH_ACTION_NONE];
}
,attrTest:function(input){
	return this._super.attrTest.call(this,input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
}
}
);
	/** * Swipe * Recognized when the pointer is moving fast (velocity),with enough distance in the allowed direction. * @constructor * @extends AttrRecognizer */
function SwipeRecognizer(){
	AttrRecognizer.apply(this,arguments);
}
inherit(SwipeRecognizer,AttrRecognizer,{
	/** * @namespace * @memberof SwipeRecognizer */
 defaults:{
	event:'swipe',threshold:10,velocity:0.65,direction:DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,pointers:1}
,getTouchAction:function(){
	return PanRecognizer.prototype.getTouchAction.call(this);
}
,attrTest:function(input){
	var direction = this.options.direction;
	var velocity;
	if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)){
	velocity = input.velocity;
}
else if (direction & DIRECTION_HORIZONTAL){
	velocity = input.velocityX;
}
else if (direction & DIRECTION_VERTICAL){
	velocity = input.velocityY;
}
return this._super.attrTest.call(this,input) && direction & input.direction && input.distance > this.options.threshold && abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
}
,emit:function(input){
	var direction = directionStr(input.direction);
	if (direction){
	this.manager.emit(this.options.event + direction,input);
}
this.manager.emit(this.options.event,input);
}
}
);
	/** * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur * between the given interval and position. The delay option can be used to recognize multi-taps without firing * a single tap. * * The eventData from the emitted event contains the property `tapCount`,which contains the amount of * multi-taps being recognized. * @constructor * @extends Recognizer */
function TapRecognizer(){
	Recognizer.apply(this,arguments);
	// previous time and center,// used for tap counting this.pTime = false;
	this.pCenter = false;
	this._timer = null;
	this._input = null;
	this.count = 0;
}
inherit(TapRecognizer,Recognizer,{
	/** * @namespace * @memberof PinchRecognizer */
 defaults:{
	event:'tap',pointers:1,taps:1,interval:300,// max time between the multi-tap taps time:250,// max time of the pointer to be down (like finger on the screen) threshold:2,// a minimal movement is ok,but keep it low posThreshold:10 // a multi-tap can be a bit off the initial position}
,getTouchAction:function(){
	return [TOUCH_ACTION_MANIPULATION];
}
,process:function(input){
	var options = this.options;
	var validPointers = input.pointers.length === options.pointers;
	var validMovement = input.distance < options.threshold;
	var validTouchTime = input.deltaTime < options.time;
	this.reset();
	if ((input.eventType & INPUT_START) && (this.count === 0)){
	return this.failTimeout();
}
// we only allow little movement // and we've reached an end event,so a tap is possible if (validMovement && validTouchTime && validPointers){
	if (input.eventType != INPUT_END){
	return this.failTimeout();
}
var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval):true;
	var validMultiTap = !this.pCenter || getDistance(this.pCenter,input.center) < options.posThreshold;
	this.pTime = input.timeStamp;
	this.pCenter = input.center;
	if (!validMultiTap || !validInterval){
	this.count = 1;
}
else{
	this.count += 1;
}
this._input = input;
	// if tap count matches we have recognized it,// else it has began recognizing... var tapCount = this.count % options.taps;
	if (tapCount === 0){
	// no failing requirements,immediately trigger the tap event // or wait as long as the multitap interval to trigger if (!this.hasRequireFailures()){
	return STATE_RECOGNIZED;
}
else{
	this._timer = setTimeoutContext(function(){
	this.state = STATE_RECOGNIZED;
	this.tryEmit();
}
,options.interval,this);
	return STATE_BEGAN;
}
}
}
return STATE_FAILED;
}
,failTimeout:function(){
	this._timer = setTimeoutContext(function(){
	this.state = STATE_FAILED;
}
,this.options.interval,this);
	return STATE_FAILED;
}
,reset:function(){
	clearTimeout(this._timer);
}
,emit:function(){
	if (this.state == STATE_RECOGNIZED ){
	this._input.tapCount = this.count;
	this.manager.emit(this.options.event,this._input);
}
}
}
);
	/** * Simple way to create an manager with a default set of recognizers. * @param{
	HTMLElement}
element * @param{
	Object}
[options] * @constructor */
function Hammer(element,options){
	options = options ||{
}
;
	options.recognizers = ifUndefined(options.recognizers,Hammer.defaults.preset);
	return new Manager(element,options);
}
/** * @const{
	string}
*/
Hammer.VERSION = '2.0.4';
	/** * default settings * @namespace */
Hammer.defaults ={
	/** * set if DOM events are being triggered. * But this is slower and unused by simple implementations,so disabled by default. * @type{
	Boolean}
* @default false */
 domEvents:false,/** * The value for the touchAction property/fallback. * When set to `compute` it will magically set the correct value based on the added recognizers. * @type{
	String}
* @default compute */
 touchAction:TOUCH_ACTION_COMPUTE,/** * @type{
	Boolean}
* @default true */
 enable:true,/** * EXPERIMENTAL FEATURE -- can be removed/changed * Change the parent input target element. * If Null,then it is being set the to main element. * @type{
	Null|EventTarget}
* @default null */
 inputTarget:null,/** * force an input class * @type{
	Null|Function}
* @default null */
 inputClass:null,/** * Default recognizer setup when calling `Hammer()` * When creating a new Manager these will be skipped. * @type{
	Array}
*/
 preset:[ // RecognizerClass,options,[recognizeWith,...],[requireFailure,...] [RotateRecognizer,{
	enable:false}
],[PinchRecognizer,{
	enable:false}
,['rotate']],[SwipeRecognizer,{
	direction:DIRECTION_HORIZONTAL}
],[PanRecognizer,{
	direction:DIRECTION_HORIZONTAL}
,['swipe']],[TapRecognizer],[TapRecognizer,{
	event:'doubletap',taps:2}
,['tap']],[PressRecognizer] ],/** * Some CSS properties can be used to improve the working of Hammer. * Add them to this method and they will be set when creating a new Manager. * @namespace */
 cssProps:{
	/** * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. * @type{
	String}
* @default 'none' */
 userSelect:'none',/** * Disable the Windows Phone grippers when pressing an element. * @type{
	String}
* @default 'none' */
 touchSelect:'none',/** * Disables the default callout shown when you touch and hold a touch target. * On iOS,when you touch and hold a touch target such as a link,Safari displays * a callout containing information about the link. This property allows you to disable that callout. * @type{
	String}
* @default 'none' */
 touchCallout:'none',/** * Specifies whether zooming is enabled. Used by IE10> * @type{
	String}
* @default 'none' */
 contentZooming:'none',/** * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. * @type{
	String}
* @default 'none' */
 userDrag:'none',/** * Overrides the highlight color shown when the user taps a link or a JavaScript * clickable element in iOS. This property obeys the alpha value,if specified. * @type{
	String}
* @default 'rgba(0,0,0,0)' */
 tapHighlightColor:'rgba(0,0,0,0)'}
}
;
	var STOP = 1;
	var FORCED_STOP = 2;
	/** * Manager * @param{
	HTMLElement}
element * @param{
	Object}
[options] * @constructor */
function Manager(element,options){
	options = options ||{
}
;
	this.options = merge(options,Hammer.defaults);
	this.options.inputTarget = this.options.inputTarget || element;
	this.handlers ={
}
;
	this.session ={
}
;
	this.recognizers = [];
	this.element = element;
	this.input = createInputInstance(this);
	this.touchAction = new TouchAction(this,this.options.touchAction);
	toggleCssProps(this,true);
	each(options.recognizers,function(item){
	var recognizer = this.add(new (item[0])(item[1]));
	item[2] && recognizer.recognizeWith(item[2]);
	item[3] && recognizer.requireFailure(item[3]);
}
,this);
}
Manager.prototype ={
	/** * set options * @param{
	Object}
options * @returns{
	Manager}
*/
 set:function(options){
	extend(this.options,options);
	// Options that need a little more setup if (options.touchAction){
	this.touchAction.update();
}
if (options.inputTarget){
	// Clean up existing event listeners and reinitialize this.input.destroy();
	this.input.target = options.inputTarget;
	this.input.init();
}
return this;
}
,/** * stop recognizing for this session. * This session will be discarded,when a new [input]start event is fired. * When forced,the recognizer cycle is stopped immediately. * @param{
	Boolean}
[force] */
 stop:function(force){
	this.session.stopped = force ? FORCED_STOP:STOP;
}
,/** * run the recognizers! * called by the inputHandler function on every movement of the pointers (touches) * it walks through all the recognizers and tries to detect the gesture that is being made * @param{
	Object}
inputData */
 recognize:function(inputData){
	var session = this.session;
	if (session.stopped){
	return;
}
// run the touch-action polyfill this.touchAction.preventDefaults(inputData);
	var recognizer;
	var recognizers = this.recognizers;
	// this holds the recognizer that is being recognized. // so the recognizer's state needs to be BEGAN,CHANGED,ENDED or RECOGNIZED // if no recognizer is detecting a thing,it is set to `null` var curRecognizer = session.curRecognizer;
	// reset when the last recognizer is recognized // or when we're in a new session if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)){
	curRecognizer = session.curRecognizer = null;
}
var i = 0;
	while (i < recognizers.length){
	recognizer = recognizers[i];
	// find out if we are allowed try to recognize the input for this one. // 1. allow if the session is NOT forced stopped (see the .stop() method) // 2. allow if we still haven't recognized a gesture in this session,or the this recognizer is the one // that is being recognized. // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. // this can be setup with the `recognizeWith()` method on the recognizer. if (session.stopped !== FORCED_STOP && ( // 1 !curRecognizer || recognizer == curRecognizer || // 2 recognizer.canRecognizeWith(curRecognizer))){
	// 3 recognizer.recognize(inputData);
}
else{
	recognizer.reset();
}
// if the recognizer has been recognizing the input as a valid gesture,we want to store this one as the // current active recognizer. but only if we don't already have an active recognizer if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)){
	curRecognizer = session.curRecognizer = recognizer;
}
i++;
}
}
,/** * get a recognizer by its event name. * @param{
	Recognizer|String}
recognizer * @returns{
	Recognizer|Null}
*/
 get:function(recognizer){
	if (recognizer instanceof Recognizer){
	return recognizer;
}
var recognizers = this.recognizers;
	for (var i = 0;
	i < recognizers.length;
	i++){
	if (recognizers[i].options.event == recognizer){
	return recognizers[i];
}
}
return null;
}
,/** * add a recognizer to the manager * existing recognizers with the same event name will be removed * @param{
	Recognizer}
recognizer * @returns{
	Recognizer|Manager}
*/
 add:function(recognizer){
	if (invokeArrayArg(recognizer,'add',this)){
	return this;
}
// remove existing var existing = this.get(recognizer.options.event);
	if (existing){
	this.remove(existing);
}
this.recognizers.push(recognizer);
	recognizer.manager = this;
	this.touchAction.update();
	return recognizer;
}
,/** * remove a recognizer by name or instance * @param{
	Recognizer|String}
recognizer * @returns{
	Manager}
*/
 remove:function(recognizer){
	if (invokeArrayArg(recognizer,'remove',this)){
	return this;
}
var recognizers = this.recognizers;
	recognizer = this.get(recognizer);
	recognizers.splice(inArray(recognizers,recognizer),1);
	this.touchAction.update();
	return this;
}
,/** * bind event * @param{
	String}
events * @param{
	Function}
handler * @returns{
	EventEmitter}
this */
 on:function(events,handler){
	var handlers = this.handlers;
	each(splitStr(events),function(event){
	handlers[event] = handlers[event] || [];
	handlers[event].push(handler);
}
);
	return this;
}
,/** * unbind event,leave emit blank to remove all handlers * @param{
	String}
events * @param{
	Function}
[handler] * @returns{
	EventEmitter}
this */
 off:function(events,handler){
	var handlers = this.handlers;
	each(splitStr(events),function(event){
	if (!handler){
	delete handlers[event];
}
else{
	handlers[event].splice(inArray(handlers[event],handler),1);
}
}
);
	return this;
}
,/** * emit event to the listeners * @param{
	String}
event * @param{
	Object}
data */
 emit:function(event,data){
	// we also want to trigger dom events if (this.options.domEvents){
	triggerDomEvent(event,data);
}
// no handlers,so skip it all var handlers = this.handlers[event] && this.handlers[event].slice();
	if (!handlers || !handlers.length){
	return;
}
data.type = event;
	data.preventDefault = function(){
	data.srcEvent.preventDefault();
}
;
	var i = 0;
	while (i < handlers.length){
	handlers[i](data);
	i++;
}
}
,/** * destroy the manager and unbinds all events * it doesn't unbind dom events,that is the user own responsibility */
 destroy:function(){
	this.element && toggleCssProps(this,false);
	this.handlers ={
}
;
	this.session ={
}
;
	this.input.destroy();
	this.element = null;
}
}
;
	/** * add/remove the css properties as defined in manager.options.cssProps * @param{
	Manager}
manager * @param{
	Boolean}
add */
function toggleCssProps(manager,add){
	var element = manager.element;
	each(manager.options.cssProps,function(value,name){
	element.style[prefixed(element.style,name)] = add ? value:'';
}
);
}
/** * trigger dom event * @param{
	String}
event * @param{
	Object}
data */
function triggerDomEvent(event,data){
	var gestureEvent = document.createEvent('Event');
	gestureEvent.initEvent(event,true,true);
	gestureEvent.gesture = data;
	data.target.dispatchEvent(gestureEvent);
}
extend(Hammer,{
	INPUT_START:INPUT_START,INPUT_MOVE:INPUT_MOVE,INPUT_END:INPUT_END,INPUT_CANCEL:INPUT_CANCEL,STATE_POSSIBLE:STATE_POSSIBLE,STATE_BEGAN:STATE_BEGAN,STATE_CHANGED:STATE_CHANGED,STATE_ENDED:STATE_ENDED,STATE_RECOGNIZED:STATE_RECOGNIZED,STATE_CANCELLED:STATE_CANCELLED,STATE_FAILED:STATE_FAILED,DIRECTION_NONE:DIRECTION_NONE,DIRECTION_LEFT:DIRECTION_LEFT,DIRECTION_RIGHT:DIRECTION_RIGHT,DIRECTION_UP:DIRECTION_UP,DIRECTION_DOWN:DIRECTION_DOWN,DIRECTION_HORIZONTAL:DIRECTION_HORIZONTAL,DIRECTION_VERTICAL:DIRECTION_VERTICAL,DIRECTION_ALL:DIRECTION_ALL,Manager:Manager,Input:Input,TouchAction:TouchAction,TouchInput:TouchInput,MouseInput:MouseInput,PointerEventInput:PointerEventInput,TouchMouseInput:TouchMouseInput,SingleTouchInput:SingleTouchInput,Recognizer:Recognizer,AttrRecognizer:AttrRecognizer,Tap:TapRecognizer,Pan:PanRecognizer,Swipe:SwipeRecognizer,Pinch:PinchRecognizer,Rotate:RotateRecognizer,Press:PressRecognizer,on:addEventListeners,off:removeEventListeners,each:each,merge:merge,extend:extend,inherit:inherit,bindFn:bindFn,prefixed:prefixed}
);
	if (typeof define == TYPE_FUNCTION && define.amd){
	define(function(){
	return Hammer;
}
);
}
else if (typeof module != 'undefined' && module.exports){
	module.exports = Hammer;
}
else{
	window[exportName] = Hammer;
}
}
)(window,document,'Hammer');
	

CSS代码(default.css):

body{/*background:#f9f7f6;color:#404d5b;*/
background:#494A5F;color:#D5D6E2;font-weight:500;font-size:1.05em;font-family:"Microsoft YaHei","宋体","Segoe UI","Lucida Grande",Helvetica,Arial,sans-serif,FreeSans,Arimo;}
a{color:#2fa0ec;text-decoration:none;outline:none;}
a:hover,a:focus{color:#74777b;}
.zzsc-container{margin:0 auto;text-align:center;overflow:hidden;}
.zzsc-content{font-size:150%;padding:1em 0;}
附件:下载该文件资源,减少时间成本(增值服务)
留言
该资源可下载
File Source
.rar
66.75 KB
jquery特效6
最新结算
jquery虚拟键盘中文打字效果js代码
类型: .rar 金额: CNY 2.31¥ 状态: 待结算 详细>
jquery虚拟键盘中文打字效果js代码
类型: .rar 金额: CNY 0.29¥ 状态: 待结算 详细>
HTML5实现CSS滤镜图片切换特效代码
类型: .rar 金额: CNY 2.31¥ 状态: 待结算 详细>
jQuery头像裁剪插件cropbox js代码
类型: .rar 金额: CNY 0.29¥ 状态: 待结算 详细>
jQuery头像裁剪插件cropbox js代码
类型: .rar 金额: CNY 2.31¥ 状态: 待结算 详细>
CSS3制作3D图片立方体旋转特效
类型: .rar 金额: CNY 2.31¥ 状态: 待结算 详细>
CSS3制作3D图片立方体旋转特效
类型: .rar 金额: CNY 0.29¥ 状态: 待结算 详细>
CSS3制作3D图片立方体旋转特效
类型: .rar 金额: CNY 2.31¥ 状态: 待结算 详细>
CSS3制作3D图片立方体旋转特效
类型: .rar 金额: CNY 0.29¥ 状态: 待结算 详细>
jQuery+css3实现信封效果
类型: .rar 金额: CNY 0.29¥ 状态: 待结算 详细>
我们力求给您提供有用的文章,再此基础上,会附加营收资源,不做任何广告,让平台可以更好发展 若您发现您的权利被侵害,或使用了您的版权,请发邮件联系 sunlifel@foxmail.com ggbig觉得 : 不提供源码的文章不是好文章
合作伙伴
联系我们
  • QQ:21499807
  • 邮箱:sunlifel@foxmail.com
  • QQ扫一扫加QQ
    QQ扫一扫
Copyright 2023-2024 ggbig.com·皖ICP备2023004211号-1
打赏文章