Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Q
qualityTesting_studentHome
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
tobacco
qualityTesting_studentHome
Commits
5b99ba3f
提交
5b99ba3f
authored
4月 04, 2023
作者:
zfl
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
图片缩放
上级
2af8bdb5
显示空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
2885 行增加
和
5 行删除
+2885
-5
index.html
public/index.html
+1
-0
hammer.js
public/js/hammer.js
+2644
-0
ImageZoom.vue
src/components/Image/ImageZoom.vue
+230
-0
index.vue
src/views/analysis/index.vue
+10
-5
没有找到文件。
public/index.html
浏览文件 @
5b99ba3f
...
...
@@ -13,6 +13,7 @@
<meta
name=
"renderer"
content=
"webkit"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
>
<link
rel=
"icon"
href=
"<%= BASE_URL %>favicon.ico"
>
<script
src=
"/js/hammer.js"
></script>
<title>
国家烟草识别培训系统
</title>
...
...
public/js/hammer.js
0 → 100644
浏览文件 @
5b99ba3f
/*! Hammer.JS - v2.0.8 - 2016-04-23
* http://hammerjs.github.io/
*
* Copyright (c) 2016 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
);
}
}
}
/**
* wrap a method with a deprecation warning and stack trace
* @param {Function} method
* @param {String} name
* @param {String} message
* @returns {Function} A new function wrapping the supplied method.
*/
function
deprecate
(
method
,
name
,
message
)
{
var
deprecationMessage
=
'DEPRECATED METHOD: '
+
name
+
'
\
n'
+
message
+
' AT
\
n'
;
return
function
()
{
var
e
=
new
Error
(
'get-stack-trace'
);
var
stack
=
e
&&
e
.
stack
?
e
.
stack
.
replace
(
/^
[^\(]
+
?[\n
$
]
/gm
,
''
)
.
replace
(
/^
\s
+at
\s
+/gm
,
''
)
.
replace
(
/^Object.<anonymous>
\s
*
\(
/gm
,
'{anonymous}()@'
)
:
'Unknown Stack Trace'
;
var
log
=
window
.
console
&&
(
window
.
console
.
warn
||
window
.
console
.
log
);
if
(
log
)
{
log
.
call
(
window
.
console
,
deprecationMessage
,
stack
);
}
return
method
.
apply
(
this
,
arguments
);
};
}
/**
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} target
* @param {...Object} objects_to_assign
* @returns {Object} target
*/
var
assign
;
if
(
typeof
Object
.
assign
!==
'function'
)
{
assign
=
function
assign
(
target
)
{
if
(
target
===
undefined
||
target
===
null
)
{
throw
new
TypeError
(
'Cannot convert undefined or null to object'
);
}
var
output
=
Object
(
target
);
for
(
var
index
=
1
;
index
<
arguments
.
length
;
index
++
)
{
var
source
=
arguments
[
index
];
if
(
source
!==
undefined
&&
source
!==
null
)
{
for
(
var
nextKey
in
source
)
{
if
(
source
.
hasOwnProperty
(
nextKey
))
{
output
[
nextKey
]
=
source
[
nextKey
];
}
}
}
}
return
output
;
};
}
else
{
assign
=
Object
.
assign
;
}
/**
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} dest
* @param {Object} src
* @param {Boolean} [merge=false]
* @returns {Object} dest
*/
var
extend
=
deprecate
(
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
;
},
'extend'
,
'Use `assign`.'
);
/**
* 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
*/
var
merge
=
deprecate
(
function
merge
(
dest
,
src
)
{
return
extend
(
dest
,
src
,
true
);
},
'merge'
,
'Use `assign`.'
);
/**
* 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
)
{
assign
(
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
||
element
;
return
(
doc
.
defaultView
||
doc
.
parentWindow
||
window
);
}
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
);
var
overallVelocity
=
getVelocity
(
input
.
deltaTime
,
input
.
deltaX
,
input
.
deltaY
);
input
.
overallVelocityX
=
overallVelocity
.
x
;
input
.
overallVelocityY
=
overallVelocity
.
y
;
input
.
overallVelocity
=
(
abs
(
overallVelocity
.
x
)
>
abs
(
overallVelocity
.
y
))
?
overallVelocity
.
x
:
overallVelocity
.
y
;
input
.
scale
=
firstMultiple
?
getScale
(
firstMultiple
.
pointers
,
pointers
)
:
1
;
input
.
rotation
=
firstMultiple
?
getRotation
(
firstMultiple
.
pointers
,
pointers
)
:
0
;
input
.
maxPointers
=
!
session
.
prevInput
?
input
.
pointers
.
length
:
((
input
.
pointers
.
length
>
session
.
prevInput
.
maxPointers
)
?
input
.
pointers
.
length
:
session
.
prevInput
.
maxPointers
);
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
=
input
.
deltaX
-
last
.
deltaX
;
var
deltaY
=
input
.
deltaY
-
last
.
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
.
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
if
(
!
this
.
pressed
)
{
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 enum
var
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-sensitive
if
(
window
.
MSPointerEvent
&&
!
window
.
PointerEvent
)
{
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
*/
var
DEDUP_TIMEOUT
=
2500
;
var
DEDUP_DISTANCE
=
25
;
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
);
this
.
primaryTouch
=
null
;
this
.
lastTouches
=
[];
}
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
);
if
(
isMouse
&&
inputData
.
sourceCapabilities
&&
inputData
.
sourceCapabilities
.
firesTouchEvents
)
{
return
;
}
// when we're in a touch event, record touches to de-dupe synthetic mouse event
if
(
isTouch
)
{
recordTouches
.
call
(
this
,
inputEvent
,
inputData
);
}
else
if
(
isMouse
&&
isSyntheticEvent
.
call
(
this
,
inputData
))
{
return
;
}
this
.
callback
(
manager
,
inputEvent
,
inputData
);
},
/**
* remove the event listeners
*/
destroy
:
function
destroy
()
{
this
.
touch
.
destroy
();
this
.
mouse
.
destroy
();
}
});
function
recordTouches
(
eventType
,
eventData
)
{
if
(
eventType
&
INPUT_START
)
{
this
.
primaryTouch
=
eventData
.
changedPointers
[
0
].
identifier
;
setLastTouch
.
call
(
this
,
eventData
);
}
else
if
(
eventType
&
(
INPUT_END
|
INPUT_CANCEL
))
{
setLastTouch
.
call
(
this
,
eventData
);
}
}
function
setLastTouch
(
eventData
)
{
var
touch
=
eventData
.
changedPointers
[
0
];
if
(
touch
.
identifier
===
this
.
primaryTouch
)
{
var
lastTouch
=
{
x
:
touch
.
clientX
,
y
:
touch
.
clientY
};
this
.
lastTouches
.
push
(
lastTouch
);
var
lts
=
this
.
lastTouches
;
var
removeLastTouch
=
function
()
{
var
i
=
lts
.
indexOf
(
lastTouch
);
if
(
i
>
-
1
)
{
lts
.
splice
(
i
,
1
);
}
};
setTimeout
(
removeLastTouch
,
DEDUP_TIMEOUT
);
}
}
function
isSyntheticEvent
(
eventData
)
{
var
x
=
eventData
.
srcEvent
.
clientX
,
y
=
eventData
.
srcEvent
.
clientY
;
for
(
var
i
=
0
;
i
<
this
.
lastTouches
.
length
;
i
++
)
{
var
t
=
this
.
lastTouches
[
i
];
var
dx
=
Math
.
abs
(
x
-
t
.
x
),
dy
=
Math
.
abs
(
y
-
t
.
y
);
if
(
dx
<=
DEDUP_DISTANCE
&&
dy
<=
DEDUP_DISTANCE
)
{
return
true
;
}
}
return
false
;
}
var
PREFIXED_TOUCH_ACTION
=
prefixed
(
TEST_ELEMENT
.
style
,
'touchAction'
);
var
NATIVE_TOUCH_ACTION
=
PREFIXED_TOUCH_ACTION
!==
undefined
;
// magical touchAction value
var
TOUCH_ACTION_COMPUTE
=
'compute'
;
var
TOUCH_ACTION_AUTO
=
'auto'
;
var
TOUCH_ACTION_MANIPULATION
=
'manipulation'
;
// not implemented
var
TOUCH_ACTION_NONE
=
'none'
;
var
TOUCH_ACTION_PAN_X
=
'pan-x'
;
var
TOUCH_ACTION_PAN_Y
=
'pan-y'
;
var
TOUCH_ACTION_MAP
=
getTouchActionProps
();
/**
* 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
&&
TOUCH_ACTION_MAP
[
value
])
{
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
)
{
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
)
&&
!
TOUCH_ACTION_MAP
[
TOUCH_ACTION_NONE
];
var
hasPanY
=
inStr
(
actions
,
TOUCH_ACTION_PAN_Y
)
&&
!
TOUCH_ACTION_MAP
[
TOUCH_ACTION_PAN_Y
];
var
hasPanX
=
inStr
(
actions
,
TOUCH_ACTION_PAN_X
)
&&
!
TOUCH_ACTION_MAP
[
TOUCH_ACTION_PAN_X
];
if
(
hasNone
)
{
//do not prevent defaults if this is a tap gesture
var
isTapPointer
=
input
.
pointers
.
length
===
1
;
var
isTapMovement
=
input
.
distance
<
2
;
var
isTapTouchTime
=
input
.
deltaTime
<
250
;
if
(
isTapPointer
&&
isTapMovement
&&
isTapTouchTime
)
{
return
;
}
}
if
(
hasPanX
&&
hasPanY
)
{
// `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
return
;
}
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
);
// if both pan-x and pan-y are set (different recognizers
// for different directions, e.g. horizontal pan but vertical swipe?)
// we need none (as otherwise with pan-x pan-y combined none of these
// recognizers will work, since the browser would handle all panning
if
(
hasPanX
&&
hasPanY
)
{
return
TOUCH_ACTION_NONE
;
}
// 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
;
}
function
getTouchActionProps
()
{
if
(
!
NATIVE_TOUCH_ACTION
)
{
return
false
;
}
var
touchMap
=
{};
var
cssSupports
=
window
.
CSS
&&
window
.
CSS
.
supports
;
[
'auto'
,
'manipulation'
,
'pan-y'
,
'pan-x'
,
'pan-x pan-y'
,
'none'
].
forEach
(
function
(
val
)
{
// If css.supports is not supported but there is native touch-action assume it supports
// all values. This is the case for IE 10 and 11.
touchMap
[
val
]
=
cssSupports
?
window
.
CSS
.
supports
(
'touch-action'
,
val
)
:
true
;
});
return
touchMap
;
}
/**
* 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
.
options
=
assign
({},
this
.
defaults
,
options
||
{});
this
.
id
=
uniqueId
();
this
.
manager
=
null
;
// 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
)
{
assign
(
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
(
event
)
{
self
.
manager
.
emit
(
event
,
input
);
}
// 'panstart' and 'panmove'
if
(
state
<
STATE_ENDED
)
{
emit
(
self
.
options
.
event
+
stateStr
(
state
));
}
emit
(
self
.
options
.
event
);
// simple 'eventName' events
if
(
input
.
additionalEvent
)
{
// additional event(panleft, panright, pinchin, pinchout...)
emit
(
input
.
additionalEvent
);
}
// panend and pancancel
if
(
state
>=
STATE_ENDED
)
{
emit
(
self
.
options
.
event
+
stateStr
(
state
));
}
},
/**
* 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
=
assign
({},
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
)
{
input
.
additionalEvent
=
this
.
options
.
event
+
direction
;
}
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
)
{
if
(
input
.
scale
!==
1
)
{
var
inOut
=
input
.
scale
<
1
?
'in'
:
'out'
;
input
.
additionalEvent
=
this
.
options
.
event
+
inOut
;
}
this
.
_super
.
emit
.
call
(
this
,
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
:
251
,
// minimal time of the pointer to be pressed
threshold
:
9
// 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.3
,
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
.
overallVelocity
;
}
else
if
(
direction
&
DIRECTION_HORIZONTAL
)
{
velocity
=
input
.
overallVelocityX
;
}
else
if
(
direction
&
DIRECTION_VERTICAL
)
{
velocity
=
input
.
overallVelocityY
;
}
return
this
.
_super
.
attrTest
.
call
(
this
,
input
)
&&
direction
&
input
.
offsetDirection
&&
input
.
distance
>
this
.
options
.
threshold
&&
input
.
maxPointers
==
this
.
options
.
pointers
&&
abs
(
velocity
)
>
this
.
options
.
velocity
&&
input
.
eventType
&
INPUT_END
;
},
emit
:
function
(
input
)
{
var
direction
=
directionStr
(
input
.
offsetDirection
);
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
:
9
,
// 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 a 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.8'
;
/**
* 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
)
{
this
.
options
=
assign
({},
Hammer
.
defaults
,
options
||
{});
this
.
options
.
inputTarget
=
this
.
options
.
inputTarget
||
element
;
this
.
handlers
=
{};
this
.
session
=
{};
this
.
recognizers
=
[];
this
.
oldCssProps
=
{};
this
.
element
=
element
;
this
.
input
=
createInputInstance
(
this
);
this
.
touchAction
=
new
TouchAction
(
this
,
this
.
options
.
touchAction
);
toggleCssProps
(
this
,
true
);
each
(
this
.
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
)
{
assign
(
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
;
}
recognizer
=
this
.
get
(
recognizer
);
// let's make sure this recognizer exists
if
(
recognizer
)
{
var
recognizers
=
this
.
recognizers
;
var
index
=
inArray
(
recognizers
,
recognizer
);
if
(
index
!==
-
1
)
{
recognizers
.
splice
(
index
,
1
);
this
.
touchAction
.
update
();
}
}
return
this
;
},
/**
* bind event
* @param {String} events
* @param {Function} handler
* @returns {EventEmitter} this
*/
on
:
function
(
events
,
handler
)
{
if
(
events
===
undefined
)
{
return
;
}
if
(
handler
===
undefined
)
{
return
;
}
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
)
{
if
(
events
===
undefined
)
{
return
;
}
var
handlers
=
this
.
handlers
;
each
(
splitStr
(
events
),
function
(
event
)
{
if
(
!
handler
)
{
delete
handlers
[
event
];
}
else
{
handlers
[
event
]
&&
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
;
if
(
!
element
.
style
)
{
return
;
}
var
prop
;
each
(
manager
.
options
.
cssProps
,
function
(
value
,
name
)
{
prop
=
prefixed
(
element
.
style
,
name
);
if
(
add
)
{
manager
.
oldCssProps
[
prop
]
=
element
.
style
[
prop
];
element
.
style
[
prop
]
=
value
;
}
else
{
element
.
style
[
prop
]
=
manager
.
oldCssProps
[
prop
]
||
''
;
}
});
if
(
!
add
)
{
manager
.
oldCssProps
=
{};
}
}
/**
* 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
);
}
assign
(
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
,
assign
:
assign
,
inherit
:
inherit
,
bindFn
:
bindFn
,
prefixed
:
prefixed
});
// this prevents errors when Hammer is loaded in the presence of an AMD
// style loader but by script tag, not by the loader.
var
freeGlobal
=
(
typeof
window
!==
'undefined'
?
window
:
(
typeof
self
!==
'undefined'
?
self
:
{}));
// jshint ignore:line
freeGlobal
.
Hammer
=
Hammer
;
if
(
typeof
define
===
'function'
&&
define
.
amd
)
{
define
(
function
()
{
return
Hammer
;
});
}
else
if
(
typeof
module
!=
'undefined'
&&
module
.
exports
)
{
module
.
exports
=
Hammer
;
}
else
{
window
[
exportName
]
=
Hammer
;
}
})(
window
,
document
,
'Hammer'
);
\ No newline at end of file
src/components/Image/ImageZoom.vue
0 → 100644
浏览文件 @
5b99ba3f
<
template
>
<div
class=
"pinch-zoom-container"
>
<img
id=
"pinch-zoom-image-id"
class=
"pinch-zoom-image"
:src=
"src"
>
</div>
</
template
>
<
script
>
export
default
{
name
:
'ImageZoom'
,
props
:
[
'src'
],
data
()
{
return
{
MIN_SCALE
:
1
,
// 1=scaling when first loade,
MAX_SCALE
:
64
,
imgWidth
:
null
,
imgHeight
:
null
,
viewportWidth
:
null
,
viewportHeight
:
null
,
scale
:
null
,
lastScale
:
null
,
container
:
null
,
img
:
null
,
x
:
0
,
lastX
:
0
,
y
:
0
,
lastY
:
0
,
curWidth
:
null
,
curHeight
:
null
,
pinchCenter
:
null
,
}
},
computed
:
{
},
created
(){
},
methods
:
{
disableImgEventHandlers
()
{
var
_that
=
this
var
events
=
[
'onclick'
,
'onmousedown'
,
'onmousemove'
,
'onmouseout'
,
'onmouseover'
,
'onmouseup'
,
'ondblclick'
,
'onfocus'
,
'onblur'
];
events
.
forEach
(
function
(
event
)
{
_that
.
img
[
event
]
=
function
()
{
return
false
;
};
});
},
absolutePosition
(
el
){
var
x
=
0
,
y
=
0
;
while
(
el
!==
null
)
{
x
+=
el
.
offsetLeft
;
y
+=
el
.
offsetTop
;
el
=
el
.
offsetParent
;
}
return
{
x
:
x
,
y
:
y
};
},
restrictScale
(
scale
)
{
if
(
scale
<
this
.
MIN_SCALE
)
{
scale
=
this
.
MIN_SCALE
;
}
else
if
(
scale
>
this
.
MAX_SCALE
)
{
scale
=
this
.
MAX_SCALE
;
}
return
scale
;
},
restrictRawPos
(
pos
,
viewportDim
,
imgDim
)
{
if
(
pos
<
viewportDim
/
this
.
scale
-
imgDim
)
{
// too far left/up?
pos
=
viewportDim
/
this
.
scale
-
imgDim
;
}
else
if
(
pos
>
0
)
{
// too far right/down?
pos
=
0
;
}
return
pos
;
},
updateLastPos
(
deltaX
,
deltaY
)
{
this
.
lastX
=
this
.
x
;
this
.
lastY
=
this
.
y
;
},
translate
(
deltaX
,
deltaY
)
{
// We restrict to the min of the viewport width/height or current width/height as the
// current width/height may be smaller than the viewport width/height
var
newX
=
this
.
restrictRawPos
(
this
.
lastX
+
deltaX
/
this
.
scale
,
Math
.
min
(
this
.
viewportWidth
,
this
.
curWidth
),
this
.
imgWidth
);
this
.
x
=
newX
;
this
.
img
.
style
.
marginLeft
=
Math
.
ceil
(
newX
*
this
.
scale
)
+
'px'
;
var
newY
=
this
.
restrictRawPos
(
this
.
lastY
+
deltaY
/
this
.
scale
,
Math
.
min
(
this
.
viewportHeight
,
this
.
curHeight
),
this
.
imgHeight
);
this
.
y
=
newY
;
this
.
img
.
style
.
marginTop
=
Math
.
ceil
(
newY
*
this
.
scale
)
+
'px'
;
},
zoom
(
scaleBy
)
{
this
.
scale
=
this
.
restrictScale
(
this
.
lastScale
*
scaleBy
);
this
.
curWidth
=
this
.
imgWidth
*
this
.
scale
;
this
.
curHeight
=
this
.
imgHeight
*
this
.
scale
;
this
.
img
.
style
.
width
=
Math
.
ceil
(
this
.
curWidth
)
+
'px'
;
this
.
img
.
style
.
height
=
Math
.
ceil
(
this
.
curHeight
)
+
'px'
;
// Adjust margins to make sure that we aren't out of bounds
this
.
translate
(
0
,
0
);
},
rawCenter
(
e
)
{
var
pos
=
this
.
absolutePosition
(
this
.
container
);
// We need to account for the scroll position
var
scrollLeft
=
window
.
pageXOffset
?
window
.
pageXOffset
:
document
.
body
.
scrollLeft
;
var
scrollTop
=
window
.
pageYOffset
?
window
.
pageYOffset
:
document
.
body
.
scrollTop
;
var
zoomX
=
-
this
.
x
+
(
e
.
center
.
x
-
pos
.
x
+
scrollLeft
)
/
this
.
scale
;
var
zoomY
=
-
this
.
y
+
(
e
.
center
.
y
-
pos
.
y
+
scrollTop
)
/
this
.
scale
;
return
{
x
:
zoomX
,
y
:
zoomY
};
},
updateLastScale
()
{
this
.
lastScale
=
this
.
scale
;
},
zoomAround
(
scaleBy
,
rawZoomX
,
rawZoomY
,
doNotUpdateLast
)
{
this
.
zoom
(
scaleBy
);
// New raw center of viewport
var
rawCenterX
=
-
this
.
x
+
Math
.
min
(
this
.
viewportWidth
,
this
.
curWidth
)
/
2
/
this
.
scale
;
var
rawCenterY
=
-
this
.
y
+
Math
.
min
(
this
.
viewportHeight
,
this
.
curHeight
)
/
2
/
this
.
scale
;
// Delta
var
deltaX
=
(
rawCenterX
-
rawZoomX
)
*
this
.
scale
;
var
deltaY
=
(
rawCenterY
-
rawZoomY
)
*
this
.
scale
;
// Translate back to zoom center
this
.
translate
(
deltaX
,
deltaY
);
if
(
!
doNotUpdateLast
)
{
this
.
updateLastScale
();
this
.
updateLastPos
();
}
},
zoomCenter
(
scaleBy
)
{
// Center of viewport
var
zoomX
=
-
this
.
x
+
Math
.
min
(
this
.
viewportWidth
,
this
.
curWidth
)
/
2
/
this
.
scale
;
var
zoomY
=
-
this
.
y
+
Math
.
min
(
this
.
viewportHeight
,
this
.
curHeight
)
/
2
/
this
.
scale
;
this
.
zoomAround
(
scaleBy
,
zoomX
,
zoomY
);
},
onLoad
()
{
this
.
img
=
document
.
getElementById
(
'pinch-zoom-image-id'
);
this
.
container
=
this
.
img
.
parentElement
;
this
.
disableImgEventHandlers
();
this
.
imgWidth
=
this
.
img
.
width
;
this
.
imgHeight
=
this
.
img
.
height
;
this
.
viewportWidth
=
this
.
img
.
offsetWidth
;
this
.
scale
=
this
.
viewportWidth
/
this
.
imgWidth
;
this
.
lastScale
=
this
.
scale
;
this
.
viewportHeight
=
this
.
img
.
parentElement
.
offsetHeight
;
this
.
curWidth
=
this
.
imgWidth
*
this
.
scale
;
this
.
curHeight
=
this
.
imgHeight
*
this
.
scale
;
var
hammer
=
new
Hammer
(
this
.
container
,
{
domEvents
:
true
});
var
_that
=
this
hammer
.
get
(
'pinch'
).
set
({
enable
:
true
});
hammer
.
on
(
'pan'
,
function
(
e
)
{
_that
.
translate
(
e
.
deltaX
,
e
.
deltaY
);
});
hammer
.
on
(
'panend'
,
function
(
e
)
{
_that
.
updateLastPos
();
});
hammer
.
on
(
'pinch'
,
function
(
e
)
{
// We only calculate the pinch center on the first pinch event as we want the center to
// stay consistent during the entire pinch
if
(
_that
.
pinchCenter
===
null
)
{
_that
.
pinchCenter
=
_that
.
rawCenter
(
e
);
var
offsetX
=
_that
.
pinchCenter
.
x
*
_that
.
scale
-
(
-
_that
.
x
*
_that
.
scale
+
Math
.
min
(
_that
.
viewportWidth
,
_that
.
curWidth
)
/
2
);
var
offsetY
=
_that
.
pinchCenter
.
y
*
_that
.
scale
-
(
-
_that
.
y
*
_that
.
scale
+
Math
.
min
(
_that
.
viewportHeight
,
_that
.
curHeight
)
/
2
);
_that
.
pinchCenterOffset
=
{
x
:
offsetX
,
y
:
offsetY
};
}
var
newScale
=
_that
.
restrictScale
(
_that
.
scale
*
e
.
scale
);
var
zoomX
=
_that
.
pinchCenter
.
x
*
newScale
-
_that
.
pinchCenterOffset
.
x
;
var
zoomY
=
_that
.
pinchCenter
.
y
*
newScale
-
_that
.
pinchCenterOffset
.
y
;
var
zoomCenter
=
{
x
:
zoomX
/
newScale
,
y
:
zoomY
/
newScale
};
_that
.
zoomAround
(
e
.
scale
,
zoomCenter
.
x
,
zoomCenter
.
y
,
true
);
});
hammer
.
on
(
'pinchend'
,
function
(
e
)
{
_that
.
updateLastScale
();
_that
.
updateLastPos
();
_that
.
pinchCenter
=
null
;
});
hammer
.
on
(
'doubletap'
,
function
(
e
)
{
var
c
=
_that
.
rawCenter
(
e
);
_that
.
zoomAround
(
2
,
c
.
x
,
c
.
y
);
});
}
},
mounted
(){
this
.
onLoad
()
},
}
</
script
>
<
style
scoped
>
.c-zoom__wrapper
{
display
:
none
;
position
:
absolute
;
top
:
0
;
left
:
0
;
right
:
0
;
bottom
:
0
;
}
.pinch-zoom-container
{
overflow
:
hidden
;
height
:
80vh
;
}
.pinch-zoom-image
{
width
:
100%
;
}
</
style
>
\ No newline at end of file
src/views/analysis/index.vue
浏览文件 @
5b99ba3f
...
...
@@ -116,10 +116,11 @@
v-if=
"e.image && e.image != '/'"
class=
"image_box"
>
<!-- :preview-src-list="[IP + e.image]" -->
<el-image
style=
"width: 200px"
:src=
"IP + e.image"
:preview-src-list=
"[IP + e.image]
"
@
click=
"ImageZoomUrl=IP + e.image;is_ImageZoom=true
"
>
</el-image>
<!--
<img
:src=
"IP + e.image"
alt=
""
/>
-->
...
...
@@ -187,20 +188,22 @@
</el-card>
</div>
<el-dialog
title=
"
提示"
:visible
.
sync=
"is_dialogVisible"
width=
"6
0%"
>
<el-dialog
title=
"
"
:visible
.
sync=
"is_ImageZoom"
top=
"0"
style=
"height:100vh"
width=
"10
0%"
>
<div
class=
"table_box"
>
<!-- -->
<ImageZoom
:src=
"ImageZoomUrl"
></ImageZoom>
</div>
<span
slot=
"footer"
class=
"dialog-footer"
>
<
!--
<
span
slot=
"footer"
class=
"dialog-footer"
>
<el-button>
取 消
</el-button>
<el-button
type=
"primary"
>
确 定
</el-button>
</span>
</span>
-->
</el-dialog>
</div>
</
template
>
<
script
>
import
TestInfo
from
'@/components/TestInfo/index.vue'
;
import
ImageZoom
from
'@/components/Image/ImageZoom.vue'
;
import
{
viewExamResults
}
from
'@/api/user'
;
import
{
array_conversion
}
from
'@/utils/tool'
;
import
Summary
from
'./components/summary-view'
;
...
...
@@ -209,9 +212,11 @@ import OverallAppraisal from './components/overall_appraisal_view.vue';
export
default
{
name
:
''
,
components
:
{
TestInfo
,
Summary
,
StudentComments
,
OverallAppraisal
},
components
:
{
ImageZoom
,
TestInfo
,
Summary
,
StudentComments
,
OverallAppraisal
},
data
()
{
return
{
ImageZoomUrl
:
null
,
is_ImageZoom
:
false
,
IP
:
process
.
env
.
VUE_APP_BASE_API
,
//初始化请求参数
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论