diff --git a/src/js/anot.shim.js b/src/js/anot.shim.js
index d4d0c72..3e1334e 100644
--- a/src/js/anot.shim.js
+++ b/src/js/anot.shim.js
@@ -5725,7 +5725,9 @@
time: function(str) {
str = str >> 0
var s = str % 60
- ;(m = Math.floor(str / 60)), (h = Math.floor(m / 60)), (m = m % 60)
+ var m = Math.floor(str / 60)
+ var h = Math.floor(m / 60)
+ m = m % 60
m = m < 10 ? '0' + m : m
s = s < 10 ? '0' + s : s
diff --git a/src/js/layer/base.js b/src/js/layer/base.js
index 824fb1f..a79c1b7 100644
--- a/src/js/layer/base.js
+++ b/src/js/layer/base.js
@@ -61,7 +61,7 @@ const close = function(id) {
}
}
-const reapeat = function(str, num) {
+const repeat = function(str, num) {
var idx = 0,
result = ''
while (idx < num) {
@@ -77,15 +77,15 @@ const fixOffset = function(val) {
return val
}
}
-const __layer__ = function(conf) {
- if (conf) {
- let { yes, no, success } = conf
- delete conf.yes
- delete conf.no
- delete conf.success
+const __layer__ = function(opt) {
+ if (opt) {
+ let { yes, no, success } = opt
+ delete opt.yes
+ delete opt.no
+ delete opt.success
this.construct({
- state: { ...conf },
+ state: { ...opt },
props: { yes, no, success }
})
.append()
@@ -130,27 +130,35 @@ const _layer = {
}
return _layer.open(opt)
},
- msg: function(msg, conf) {
- if (typeof conf !== 'object') {
- var tmp = conf
- conf = { timeout: 2500 }
- if (typeof tmp === 'number') {
- conf.icon = tmp
- }
+ toast: function(content, type, timeout, cb) {
+ // if (typeof opt !== 'object') {
+ // var tmp = opt
+ // opt = { timeout: 2500 }
+ // if (typeof tmp === 'number') {
+ // opt.icon = tmp
+ // }
+ // }
+
+ if (typeof type === 'number') {
+ timeout = type
+ type = 'info'
}
- if (!conf.hasOwnProperty('timeout')) {
- conf.timeout = 2500
+ // if (!opt.hasOwnProperty('timeout')) {
+ // opt.timeout = 2500
+ // }
+
+ let opt = {
+ content: `${content}`,
+ menubar: false,
+ mask: false,
+ type: 7,
+ fixed: true
}
- conf.specialMode = true //特殊模式
- conf.content = '
' + msg + '
'
- conf.type = 7
- conf.fixed = true
- conf.shade = false
- conf.menubar = false
- conf.radius = '5px'
- return _layer.open(conf)
+ opt.toast = true // toast模式
+
+ return _layer.open(opt)
},
loading: function(style, time, cb) {
style = style >>> 0
@@ -174,34 +182,34 @@ const _layer = {
fixed: true
})
},
- tips: function(content, container, conf) {
+ tips: function(content, container, opt) {
if (!(container instanceof HTMLElement)) {
return Anot.error('tips类型必须指定一个目标容器')
}
- if (typeof conf !== 'object') {
- var tmp = conf
- conf = { timeout: 2500 }
+ if (typeof opt !== 'object') {
+ var tmp = opt
+ opt = { timeout: 2500 }
if (typeof tmp === 'number') {
- conf.icon = tmp
+ opt.icon = tmp
}
}
- if (!conf.hasOwnProperty('timeout')) {
- conf.timeout = 2500
+ if (!opt.hasOwnProperty('timeout')) {
+ opt.timeout = 2500
}
- if (!conf.background) {
- conf.background = 'rgba(0,0,0,.5)'
+ if (!opt.background) {
+ opt.background = 'rgba(0,0,0,.5)'
}
- if (!conf.color) {
- conf.color = '#fff'
+ if (!opt.color) {
+ opt.color = '#fff'
}
- conf.container = container
- conf.content = content
- conf.type = 5
- conf.icon = 0
- conf.fixed = true
- conf.shade = false
- conf.menubar = false
- return _layer.open(conf)
+ opt.container = container
+ opt.content = content
+ opt.type = 5
+ opt.icon = 0
+ opt.fixed = true
+ opt.shade = false
+ opt.menubar = false
+ return _layer.open(opt)
},
prompt: function(title, yescb) {
if (typeof yescb !== 'function') {
@@ -226,33 +234,33 @@ const _layer = {
require(['css!./skin/' + skin], callback)
},
close: close,
- open: function(conf) {
- if (typeof conf === 'string') {
- /*conf = '$wrap-' + conf
- if (!layerObj[conf]) {
+ open: function(opt) {
+ if (typeof opt === 'string') {
+ /*opt = '$wrap-' + opt
+ if (!layerObj[opt]) {
throw new Error('layer实例不存在')
} else {
//只能显示一个实例
- if (layerObj[conf].show) {
- return conf
+ if (layerObj[opt].show) {
+ return opt
}
- layerObj[conf].show = true
+ layerObj[opt].show = true
- if (!Anot.vmodels[conf]) {
- Anot(layerObj[conf].obj.init)
+ if (!Anot.vmodels[opt]) {
+ Anot(layerObj[opt].obj.init)
}
- layerObj[conf].parentElem.appendChild(layerDom[conf][1])
- layerDom[conf][1]
+ layerObj[opt].parentElem.appendChild(layerDom[opt][1])
+ layerDom[opt][1]
.querySelector('.detail')
- .appendChild(layerObj[conf].wrap)
- layerObj[conf].wrap.style.display = ''
- // Anot.scan(layerDom[conf][1])
- layerObj[conf].obj.show()
- return conf
+ .appendChild(layerObj[opt].wrap)
+ layerObj[opt].wrap.style.display = ''
+ // Anot.scan(layerDom[opt][1])
+ layerObj[opt].obj.show()
+ return opt
}*/
} else {
- return new __layer__(conf).init.$id
+ return new __layer__(opt).init.$id
}
},
version: Anot.ui.layer
@@ -277,16 +285,24 @@ __layer__.prototype = {
5: 9
},
timeout: null,
- construct: function(conf) {
- let _id = conf.$id || uuid()
+ construct: function(opt) {
+ let _id = opt.$id || uuid()
this.init = {
$id: _id,
state: {
...defconf,
- ...conf.state
+ ...opt.state
},
- props: conf.props,
- skip: ['area', 'shift', 'skin', 'mask', 'maskClose', 'container'],
+ props: opt.props,
+ skip: [
+ 'area',
+ 'shift',
+ 'skin',
+ 'mask',
+ 'maskClose',
+ 'container',
+ 'follow'
+ ],
methods: {
onMaskClick: function() {
if (this.type < 4 && !this.maskClose) {
@@ -358,8 +374,8 @@ __layer__.prototype = {
if (state.type === 5) {
layBox.classList.add('active')
}
- if (state.specialMode && state.type === 7) {
- layBox.classList.add('type-unspecial')
+ if (state.toast) {
+ layBox.classList.add('type-toast')
} else {
layBox.classList.add('type-' + state.type)
}
@@ -396,9 +412,10 @@ __layer__.prototype = {
${this.getMenubar()}
+ style="${boxcss}"
+ ${!state.wrap && state.type !== 6 ? ':html="content"' : ''}>
- ${this.getCont()}
+ ${state.type === 6 ? this.getLoading(state.load) : ''}
${this.getCtrl()}
${arrow}
@@ -407,17 +424,14 @@ __layer__.prototype = {
outerBox.appendChild(layBox)
return [outerBox, layBox]
},
- getCont: function() {
- let { state, $id } = this.init
- if (state.type === 6) {
- return this.getLoading(state.load)
- } else {
- return `
- ${this.getIcon()}
-
- `
- }
- },
+ // getCont: function() {
+ // let { state, $id } = this.init
+ // if (state.type === 6) {
+ // return this.getLoading(state.load)
+ // } else {
+ // return !state.wrap ? '{{content | html}}' : ''
+ // }
+ // },
getLoading: function(style) {
return `
@@ -481,7 +495,7 @@ __layer__.prototype = {
}
},
append: function() {
- let { state, $id } = this.init
+ let { state, $id, container } = this.init
//如果有已经打开的弹窗,则关闭
if (unique) {
_layer.close(unique)
@@ -491,14 +505,17 @@ __layer__.prototype = {
}
layerDom[$id] = this.create()
- delete state.specialMode
+ delete state.toast
+ if (!container) {
+ container = document.body
+ }
- document.body.appendChild(layerDom[$id][0])
+ container.appendChild(layerDom[$id][0])
this.vm = Anot(this.init)
return this
},
show: function() {
- let { state, $id, container } = this.init
+ let { state, $id, follow } = this.init
setTimeout(function() {
var style = { visibility: '', background: state.background }
@@ -509,10 +526,10 @@ __layer__.prototype = {
// only type[tips] can define `color`
style.color = state.color
- let $container = Anot(container)
- let ew = $container.innerWidth()
- let ol = $container.offset().left - document.body.scrollLeft
- let ot = $container.offset().top - document.body.scrollTop
+ let $follow = Anot(follow)
+ let ew = $follow.innerWidth()
+ let ol = $follow.offset().left - document.body.scrollLeft
+ let ot = $follow.offset().top - document.body.scrollTop
style.left = ol + ew * 0.7
style.top = ot - parseInt(css.height) - 8
@@ -557,7 +574,7 @@ __layer__.prototype = {
_this.vm.yes($id)
}
}, state.timeout)
- } else if (statetype === 6) {
+ } else if (state.type === 6) {
// loading类型, 非自动关闭时, 主动触发回调
this.vm.yes($id)
}
diff --git a/src/js/layer/skin/def.scss b/src/js/layer/skin/def.scss
index fa38230..a05028e 100644
--- a/src/js/layer/skin/def.scss
+++ b/src/js/layer/skin/def.scss
@@ -13,15 +13,9 @@
.layer-box {position:absolute;}
-
- &.mask {position:fixed;z-index:65534;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,.3);
-
- .layer-box {position:absolute;}
- }
-
-
a {text-decoration:none;}
+
.layer-box {left:50%;top:50%;z-index:65535;
&.shift {transition: all .3s ease-in-out;}
@@ -47,20 +41,19 @@
.layer-title {width:100%;height:43px;padding:0 10px;line-height:43px;font-size:16px;color:nth($cgr, 1);}
/* 弹层主体内容 */
- .layer-content {position:relative;width:100%;height:auto;min-height:50px;padding:10px;
+ .layer-content {position:relative;width:100%;height:auto;min-height:50px;padding:10px;word-break:break-all;word-wrap: break-word;
- .msg-icon {position:absolute;left:10px;top:10px;width:50px;height:auto;line-height:40px;font-size:35px;text-align:center;}
- .detail {width:auto;height:100%;margin:auto auto auto 50px;padding:5px 15px;word-break:break-all;word-wrap: break-word;
+ // .msg-icon {position:absolute;left:10px;top:10px;width:50px;height:auto;line-height:40px;font-size:35px;text-align:center;}
+
+ .prompt-value {width: 100%;height: 30px;padding: 0 8px;border: 1px solid #ddd;border-radius: 3px;
- .prompt-value {width: 230px;height: 30px;padding: 0 8px;border: 1px solid #ddd;border-radius: 3px;
+ &.alert {border-color:nth($cr, 1)}
+ &:focus {border-color:nth($ct, 1)}
+ }
+ .msg-box {line-height:30px;}
+
- &.alert {border-color:nth($cr, 1)}
- &:focus {border-color:nth($ct, 1)}
- }
- .msg-box {line-height:30px;}
- }
-
- &.none-icon .detail {margin:0 auto;}
+ &.none-icon .detail {margin:0 auto;}
}
@@ -92,7 +85,7 @@
&.active {visibility:visible;opacity:1;}
i.arrow {position:absolute;left:5px;bottom:-14px;width:0;height:0;border:6px solid transparent;border-top:8px solid rgba(0,0,0,.5);content: ""}
- .layer-content .detail {margin:0;padding:0}
+ .layer-content {margin:0;padding:0}
}
@@ -100,11 +93,15 @@
&.type-6 {box-shadow:none;background:transparent;}
- /* 特殊类弹层(msg弹层) */
- &.type-unspecial {min-width:10px;background:transparent;
+ /* 特殊类弹层(toast弹层) */
+ &.type-toast {min-width:10px;padding:0;background:transparent;
- .layer-content {min-height:60px;padding:0}
- .layer-content .detail {margin:0;padding:0}
+ .layer-content {min-height:40px;height:40px;padding:0}
+
+ .toast-box {display:inline-block;position:relative;min-height:40px;padding:5px 8px 5px 50px;line-height:28px;border:1px solid nth($co, 1);border-radius:3px;background:#fffbed;color:nth($co, 3);word-break: break-all;
+
+ &::before {position:absolute;left:15px;top:5px;font:20px/28px "ui font";color:nth($cr, 1);content:"\e6f6";}
+ }
}
@@ -182,6 +179,11 @@
}
}
+
+ &.mask {position:fixed;z-index:65534;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,.3);
+
+ .layer-box {position:absolute;}
+ }
&:active {z-index:65536;}
}
diff --git a/src/js/router/main.js b/src/js/router/main.js
index a85ebf5..15626a8 100644
--- a/src/js/router/main.js
+++ b/src/js/router/main.js
@@ -1,172 +1,180 @@
-define(['yua'], function(){
+/**
+ *
+ * @authors yutent (yutent@doui.cc)
+ * @date 2017-04-14 21:04:50
+ *
+ */
- function Router(){
- this.table = {get: []};
- this.errorFn = null;
- this.history = null;
- this.hash = '';
- this.started = false;
- this.init = {};
- }
+'use strict'
+//储存版本信息
+Anot.ui.router = '0.0.1'
- var defaultOptions = {
- prefix: /^(#!|#)[\/]?/, //hash前缀正则
- historyOpen: true, //是否开启hash历史
- allowReload: true //连续点击同一个链接是否重新加载
- };
- var isMouseUp = true;
+function Router() {
+ this.table = { get: [] }
+ this.errorFn = null
+ this.history = null
+ this.hash = ''
+ this.started = false
+ this.init = {}
+}
- var ruleRegExp = /(:id)|(\{id\})|(\{id:([A-z\d\,\[\]\{\}\-\+\*\?\!:\^\$]*)\})/g;
+var defaultOptions = {
+ prefix: /^(#!|#)[\/]?/, //hash前缀正则
+ historyOpen: true, //是否开启hash历史
+ allowReload: true //连续点击同一个链接是否重新加载
+}
+var isMouseUp = true
- Router.prototype = {
- error: function(callback){
- this.errorFn = callback;
- },
- config: function(opts){
- if(this.started)
- return console.error('Router config has been set');
+var ruleRegExp = /(:id)|(\{id\})|(\{id:([A-z\d\,\[\]\{\}\-\+\*\?\!:\^\$]*)\})/g
- this.started = true;
- if(!opts.allowReload)
- opts.historyOpen = true;
- this.init = yua.mix({}, defaultOptions, opts);
- },
- _getRegExp: function(rule, opts){
- var re = rule.replace(ruleRegExp, function(m, p1, p2, p3, p4){
- var w = '([\\w.-]';
- if(p1 || p2){
- return w + '+)';
- }else{
- if(!/^\{[\d\,]+\}$/.test(p4)){
- w = '(';
- }
- return w + p4 + ')';
- }
- })
- re = re.replace(/(([^\\])([\/]+))/g, '$2\\/').replace(/(([^\\])([\.]+))/g, '$2\\.').replace(/(([^\\])([\-]+))/g, '$2\\-').replace(/(\(.*)(\\[\-]+)(.*\))/g, '$1-$3');
- re = '^' + re + '$';
- opts.regexp = new RegExp(re)
- return opts;
- },
- _add: function(method, rule, callback){
- if(!this.started)
- this.config({});
+Router.prototype = {
+ error: function(callback) {
+ this.errorFn = callback
+ },
+ config: function(opts) {
+ if (this.started) return console.error('Router config has been set')
- var table = this.table[method.toLowerCase()];
- if (rule.charAt(0) !== "/") {
- console.error('char "/" must be in front of router rule');
- return;
- }
- rule = rule.replace(/^[\/]+|[\/]+$|\s+/g, '');
- var opts = {};
- opts.rule = rule;
- opts.callback = callback;
- yua.Array.ensure(table, this._getRegExp(rule, opts));
- },
- _route: function(method, hash){
- var hash = hash.trim();
- var table = this.table[method];
- var init = this.init;
-
- if(!init.allowReload && hash === this.history)
- return;
-
- if(init.historyOpen){
- this.history = hash;
- if(yua.ls)
- yua.ls('lastHash', hash);
- }
-
- for(var i = 0, obj; obj = table[i++];){
- var args = hash.match(obj.regexp);
- if(args){
- args.shift();
- return obj.callback.apply(obj, args)
- }
- }
- this.errorFn && this.errorFn(hash);
- },
- on: function(rule, callback){
- var _this = this
- if(Array.isArray(rule)){
- rule.forEach(function(it){
- _this._add('get', it, callback);
- })
- }else{
- this._add('get', rule, callback);
- }
- }
- }
-
- yua.bind(window, 'load', function(){
- if(!yua.router.started)
- return;
- var prefix = yua.router.init.prefix;
- var hash = location.hash;
- hash = hash.replace(prefix, "").trim();
- yua.router._route('get', hash);
- });
-
-
- if('onhashchange' in window){
- window.addEventListener('hashchange', function(event){
- if(!isMouseUp)
- return;
- var prefix = yua.router.init.prefix;
- var hash = location.hash.replace(prefix, "").trim();
- yua.router._route('get', hash)
- })
- }
-
- //劫持页面上所有点击事件,如果事件源来自链接或其内部,
- //并且它不会跳出本页,并且以"#/"或"#!/"开头,那么触发updateLocation方法
- yua.bind(document, "mousedown", function(event){
- var defaultPrevented = "defaultPrevented" in event ? event['defaultPrevented'] : event.returnValue === false
- if (defaultPrevented || event.ctrlKey || event.metaKey || event.which === 2)
- return
- var target = event.target
- while (target.nodeName !== "A") {
- target = target.parentNode
- if (!target || target.tagName === "BODY") {
- return
- }
- }
-
- if (targetIsThisWindow(target.target)){
- if(!yua.router.started)
- return;
- var href = target.getAttribute("href") || target.getAttribute("xlink:href"),
- prefix = yua.router.init.prefix;
-
- if (href === null || !prefix.test(href))
- return
-
- yua.router.hash = href.replace(prefix, "").trim();
- event.preventDefault();
- location.hash = href;
- isMouseUp = false;
+ this.started = true
+ if (!opts.allowReload) opts.historyOpen = true
+ this.init = Anot.mix({}, defaultOptions, opts)
+ },
+ _getRegExp: function(rule, opts) {
+ var re = rule.replace(ruleRegExp, function(m, p1, p2, p3, p4) {
+ var w = '([\\w.-]'
+ if (p1 || p2) {
+ return w + '+)'
+ } else {
+ if (!/^\{[\d\,]+\}$/.test(p4)) {
+ w = '('
}
+ return w + p4 + ')'
+ }
})
+ re = re
+ .replace(/(([^\\])([\/]+))/g, '$2\\/')
+ .replace(/(([^\\])([\.]+))/g, '$2\\.')
+ .replace(/(([^\\])([\-]+))/g, '$2\\-')
+ .replace(/(\(.*)(\\[\-]+)(.*\))/g, '$1-$3')
+ re = '^' + re + '$'
+ opts.regexp = new RegExp(re)
+ return opts
+ },
+ _add: function(method, rule, callback) {
+ if (!this.started) this.config({})
- yua.bind(document, "mouseup", function(){
- if(!isMouseUp){
- yua.router._route('get', yua.router.hash);
- isMouseUp = true;
- }
-
- })
-
+ var table = this.table[method.toLowerCase()]
+ if (rule.charAt(0) !== '/') {
+ console.error('char "/" must be in front of router rule')
+ return
+ }
+ rule = rule.replace(/^[\/]+|[\/]+$|\s+/g, '')
+ var opts = {}
+ opts.rule = rule
+ opts.callback = callback
+ Anot.Array.ensure(table, this._getRegExp(rule, opts))
+ },
+ _route: function(method, hash) {
+ var hash = hash.trim()
+ var table = this.table[method]
+ var init = this.init
- //判定A标签的target属性是否指向自身
- //thanks https://github.com/quirkey/sammy/blob/master/lib/sammy.js#L219
- function targetIsThisWindow(targetWindow) {
- if (!targetWindow || targetWindow === window.name || targetWindow === '_self' || (targetWindow === 'top' && window == window.top)) {
- return true
- }
- return false
+ if (!init.allowReload && hash === this.history) return
+
+ if (init.historyOpen) {
+ this.history = hash
+ if (Anot.ls) {
+ Anot.ls('lastHash', hash)
+ }
}
- yua.ui.router = '0.0.1'
+ for (var i = 0, obj; (obj = table[i++]); ) {
+ var args = hash.match(obj.regexp)
+ if (args) {
+ args.shift()
+ return obj.callback.apply(obj, args)
+ }
+ }
+ this.errorFn && this.errorFn(hash)
+ },
+ on: function(rule, callback) {
+ var _this = this
+ if (Array.isArray(rule)) {
+ rule.forEach(function(it) {
+ _this._add('get', it, callback)
+ })
+ } else {
+ this._add('get', rule, callback)
+ }
+ }
+}
- return yua.router = new Router;
-})
\ No newline at end of file
+Anot.bind(window, 'load', function() {
+ if (!Anot.router.started) return
+ var prefix = Anot.router.init.prefix
+ var hash = location.hash
+ hash = hash.replace(prefix, '').trim()
+ Anot.router._route('get', hash)
+})
+
+if ('onhashchange' in window) {
+ window.addEventListener('hashchange', function(event) {
+ if (!isMouseUp) return
+ var prefix = Anot.router.init.prefix
+ var hash = location.hash.replace(prefix, '').trim()
+ Anot.router._route('get', hash)
+ })
+}
+
+//劫持页面上所有点击事件,如果事件源来自链接或其内部,
+//并且它不会跳出本页,并且以"#/"或"#!/"开头,那么触发updateLocation方法
+Anot.bind(document, 'mousedown', function(event) {
+ var defaultPrevented =
+ 'defaultPrevented' in event
+ ? event['defaultPrevented']
+ : event.returnValue === false
+ if (defaultPrevented || event.ctrlKey || event.metaKey || event.which === 2)
+ return
+ var target = event.target
+ while (target.nodeName !== 'A') {
+ target = target.parentNode
+ if (!target || target.tagName === 'BODY') {
+ return
+ }
+ }
+
+ if (targetIsThisWindow(target.target)) {
+ if (!Anot.router.started) return
+ var href = target.getAttribute('href') || target.getAttribute('xlink:href'),
+ prefix = Anot.router.init.prefix
+
+ if (href === null || !prefix.test(href)) return
+
+ Anot.router.hash = href.replace(prefix, '').trim()
+ event.preventDefault()
+ location.hash = href
+ isMouseUp = false
+ }
+})
+
+Anot.bind(document, 'mouseup', function() {
+ if (!isMouseUp) {
+ Anot.router._route('get', Anot.router.hash)
+ isMouseUp = true
+ }
+})
+
+//判定A标签的target属性是否指向自身
+//thanks https://github.com/quirkey/sammy/blob/master/lib/sammy.js#L219
+function targetIsThisWindow(targetWindow) {
+ if (
+ !targetWindow ||
+ targetWindow === window.name ||
+ targetWindow === '_self' ||
+ (targetWindow === 'top' && window == window.top)
+ ) {
+ return true
+ }
+ return false
+}
+
+export default (Anot.router = new Router())
diff --git a/src/js/touch.patch.js b/src/js/touch.patch.js
index 443fd92..a444471 100644
--- a/src/js/touch.patch.js
+++ b/src/js/touch.patch.js
@@ -1,594 +1,583 @@
var ua = navigator.userAgent.toLowerCase()
//http://stackoverflow.com/questions/9038625/detect-if-device-is-ios
function iOSversion() {
- //https://developer.apple.com/library/prerelease/mac/releasenotes/General/WhatsNewInSafari/Articles/Safari_9.html
- //http://mp.weixin.qq.com/s?__biz=MzA3MDQ4MzQzMg==&mid=256900619&idx=1&sn=b29f84cff0b8d7b9742e5d8b3cd8f218&scene=1&srcid=1009F9l4gh9nZ7rcQJEhmf7Q#rd
- if (/iPad|iPhone|iPod/i.test(ua) && !window.MSStream) {
- if ('backdropFilter' in document.documentElement.style) {
- return 9
- }
- if (!!window.indexedDB) {
- return 8
- }
- if (!!window.SpeechSynthesisUtterance) {
- return 7
- }
- if (!!window.webkitAudioContext) {
- return 6
- }
- if (!!window.matchMedia) {
- return 5
- }
- if (!!window.history && 'pushState' in window.history) {
- return 4
- }
- return 3
+ //https://developer.apple.com/library/prerelease/mac/releasenotes/General/WhatsNewInSafari/Articles/Safari_9.html
+ //http://mp.weixin.qq.com/s?__biz=MzA3MDQ4MzQzMg==&mid=256900619&idx=1&sn=b29f84cff0b8d7b9742e5d8b3cd8f218&scene=1&srcid=1009F9l4gh9nZ7rcQJEhmf7Q#rd
+ if (/ipad|iphone|ipod/.test(ua) && !window.MSStream) {
+ if ('backdropFilter' in document.documentElement.style) {
+ return 9
}
- return NaN
+ if (!!window.indexedDB) {
+ return 8
+ }
+ if (!!window.SpeechSynthesisUtterance) {
+ return 7
+ }
+ if (!!window.webkitAudioContext) {
+ return 6
+ }
+ if (!!window.matchMedia) {
+ return 5
+ }
+ if (!!window.history && 'pushState' in window.history) {
+ return 4
+ }
+ return 3
+ }
+ return NaN
}
var deviceIsAndroid = ua.indexOf('android') > 0
var deviceIsIOS = iOSversion()
-var Recognizer = (yua.gestureHooks = {
- pointers: {},
- //以AOP切入touchstart, touchmove, touchend, touchcancel回调
- start: function(event, callback) {
- //touches是当前屏幕上所有触摸点的列表;
- //targetTouches是当前对象上所有触摸点的列表;
- //changedTouches是涉及当前事件的触摸点的列表。
- for (var i = 0; i < event.changedTouches.length; i++) {
- var touch = event.changedTouches[i],
- id = touch.identifier,
- pointer = {
- startTouch: mixLocations({}, touch),
- startTime: Date.now(),
- status: 'tapping',
- element: event.target,
- pressingHandler:
- Recognizer.pointers[id] &&
- Recognizer.pointers[id].pressingHandler
- }
- Recognizer.pointers[id] = pointer
- callback(pointer, touch)
+var Recognizer = (Anot.gestureHooks = {
+ pointers: {},
+ //以AOP切入touchstart, touchmove, touchend, touchcancel回调
+ start: function(event, callback) {
+ //touches是当前屏幕上所有触摸点的列表;
+ //targetTouches是当前对象上所有触摸点的列表;
+ //changedTouches是涉及当前事件的触摸点的列表。
+ for (var i = 0; i < event.changedTouches.length; i++) {
+ var touch = event.changedTouches[i],
+ id = touch.identifier,
+ pointer = {
+ startTouch: mixLocations({}, touch),
+ startTime: Date.now(),
+ status: 'tapping',
+ element: event.target,
+ pressingHandler:
+ Recognizer.pointers[id] && Recognizer.pointers[id].pressingHandler
}
- },
- move: function(event, callback) {
- for (var i = 0; i < event.changedTouches.length; i++) {
- var touch = event.changedTouches[i]
- var pointer = Recognizer.pointers[touch.identifier]
- if (!pointer) {
- return
- }
-
- if (!('lastTouch' in pointer)) {
- pointer.lastTouch = pointer.startTouch
- pointer.lastTime = pointer.startTime
- pointer.deltaX = pointer.deltaY = pointer.duration = pointer.distance = 0
- }
-
- var time = Date.now() - pointer.lastTime
-
- if (time > 0) {
- var RECORD_DURATION = 70
- if (time > RECORD_DURATION) {
- time = RECORD_DURATION
- }
- if (pointer.duration + time > RECORD_DURATION) {
- pointer.duration = RECORD_DURATION - time
- }
-
- pointer.duration += time
- pointer.lastTouch = mixLocations({}, touch)
-
- pointer.lastTime = Date.now()
-
- pointer.deltaX = touch.clientX - pointer.startTouch.clientX
- pointer.deltaY = touch.clientY - pointer.startTouch.clientY
- var x = pointer.deltaX * pointer.deltaX
- var y = pointer.deltaY * pointer.deltaY
- pointer.distance = Math.sqrt(x + y)
- pointer.isVertical = x < y
-
- callback(pointer, touch)
- }
- }
- },
- end: function(event, callback) {
- for (var i = 0; i < event.changedTouches.length; i++) {
- var touch = event.changedTouches[i],
- id = touch.identifier,
- pointer = Recognizer.pointers[id]
-
- if (!pointer) continue
-
- callback(pointer, touch)
-
- delete Recognizer.pointers[id]
- }
- },
- //人工触发合成事件
- fire: function(elem, type, props) {
- if (elem) {
- var event = document.createEvent('Events')
- event.initEvent(type, true, true)
- yua.mix(event, props)
- elem.dispatchEvent(event)
- }
- },
- //添加各种识别器
- add: function(name, recognizer) {
- function move(event) {
- recognizer.touchmove(event)
- }
-
- function end(event) {
- recognizer.touchend(event)
-
- document.removeEventListener('touchmove', move)
-
- document.removeEventListener('touchend', end)
-
- document.removeEventListener('touchcancel', cancel)
- }
-
- function cancel(event) {
- recognizer.touchcancel(event)
-
- document.removeEventListener('touchmove', move)
-
- document.removeEventListener('touchend', end)
-
- document.removeEventListener('touchcancel', cancel)
- }
-
- recognizer.events.forEach(function(eventName) {
- yua.eventHooks[eventName] = {
- fix: function(el, fn) {
- if (!el['touch-' + name]) {
- el['touch-' + name] = '1'
- el.addEventListener('touchstart', function(event) {
- recognizer.touchstart(event)
-
- document.addEventListener('touchmove', move)
-
- document.addEventListener('touchend', end)
-
- document.addEventListener('touchcancel', cancel)
- })
- }
- return fn
- }
- }
- })
+ Recognizer.pointers[id] = pointer
+ callback(pointer, touch)
}
+ },
+ move: function(event, callback) {
+ for (var i = 0; i < event.changedTouches.length; i++) {
+ var touch = event.changedTouches[i]
+ var pointer = Recognizer.pointers[touch.identifier]
+ if (!pointer) {
+ return
+ }
+
+ if (!('lastTouch' in pointer)) {
+ pointer.lastTouch = pointer.startTouch
+ pointer.lastTime = pointer.startTime
+ pointer.deltaX = pointer.deltaY = pointer.duration = pointer.distance = 0
+ }
+
+ var time = Date.now() - pointer.lastTime
+
+ if (time > 0) {
+ var RECORD_DURATION = 70
+ if (time > RECORD_DURATION) {
+ time = RECORD_DURATION
+ }
+ if (pointer.duration + time > RECORD_DURATION) {
+ pointer.duration = RECORD_DURATION - time
+ }
+
+ pointer.duration += time
+ pointer.lastTouch = mixLocations({}, touch)
+
+ pointer.lastTime = Date.now()
+
+ pointer.deltaX = touch.clientX - pointer.startTouch.clientX
+ pointer.deltaY = touch.clientY - pointer.startTouch.clientY
+ var x = pointer.deltaX * pointer.deltaX
+ var y = pointer.deltaY * pointer.deltaY
+ pointer.distance = Math.sqrt(x + y)
+ pointer.isVertical = x < y
+
+ callback(pointer, touch)
+ }
+ }
+ },
+ end: function(event, callback) {
+ for (var i = 0; i < event.changedTouches.length; i++) {
+ var touch = event.changedTouches[i],
+ id = touch.identifier,
+ pointer = Recognizer.pointers[id]
+
+ if (!pointer) continue
+
+ callback(pointer, touch)
+
+ delete Recognizer.pointers[id]
+ }
+ },
+ //人工触发合成事件
+ fire: function(elem, type, props) {
+ if (elem) {
+ var event = document.createEvent('Events')
+ event.initEvent(type, true, true)
+ Anot.mix(event, props)
+ elem.dispatchEvent(event)
+ }
+ },
+ //添加各种识别器
+ add: function(name, recognizer) {
+ function move(event) {
+ recognizer.touchmove(event)
+ }
+
+ function end(event) {
+ recognizer.touchend(event)
+
+ document.removeEventListener('touchmove', move)
+
+ document.removeEventListener('touchend', end)
+
+ document.removeEventListener('touchcancel', cancel)
+ }
+
+ function cancel(event) {
+ recognizer.touchcancel(event)
+
+ document.removeEventListener('touchmove', move)
+
+ document.removeEventListener('touchend', end)
+
+ document.removeEventListener('touchcancel', cancel)
+ }
+
+ recognizer.events.forEach(function(eventName) {
+ Anot.eventHooks[eventName] = {
+ fix: function(el, fn) {
+ if (!el['touch-' + name]) {
+ el['touch-' + name] = '1'
+ el.addEventListener('touchstart', function(event) {
+ recognizer.touchstart(event)
+
+ document.addEventListener('touchmove', move)
+
+ document.addEventListener('touchend', end)
+
+ document.addEventListener('touchcancel', cancel)
+ })
+ }
+ return fn
+ }
+ }
+ })
+ }
})
var locations = ['screenX', 'screenY', 'clientX', 'clientY', 'pageX', 'pageY']
// 复制 touch 对象上的有用属性到固定对象上
function mixLocations(target, source) {
- if (source) {
- locations.forEach(function(key) {
- target[key] = source[key]
- })
- }
- return target
+ if (source) {
+ locations.forEach(function(key) {
+ target[key] = source[key]
+ })
+ }
+ return target
}
var supportPointer = !!navigator.pointerEnabled || !!navigator.msPointerEnabled
if (supportPointer) {
- // 支持pointer的设备可用样式来取消click事件的300毫秒延迟
- root.style.msTouchAction = root.style.touchAction = 'none'
+ // 支持pointer的设备可用样式来取消click事件的300毫秒延迟
+ root.style.msTouchAction = root.style.touchAction = 'none'
}
var tapRecognizer = {
- events: ['tap'],
- touchBoundary: 10,
- tapDelay: 200,
- needClick: function(target) {
- //判定是否使用原生的点击事件, 否则使用sendClick方法手动触发一个人工的点击事件
- switch (target.nodeName.toLowerCase()) {
- case 'button':
- case 'select':
- case 'textarea':
- if (target.disabled) {
- return true
- }
-
- break
- case 'input':
- // IOS6 pad 上选择文件,如果不是原生的click,弹出的选择界面尺寸错误
- if (
- (deviceIsIOS && target.type === 'file') ||
- target.disabled
- ) {
- return true
- }
-
- break
- case 'label':
- case 'iframe':
- case 'video':
- return true
+ events: ['tap'],
+ touchBoundary: 10,
+ tapDelay: 200,
+ needClick: function(target) {
+ //判定是否使用原生的点击事件, 否则使用sendClick方法手动触发一个人工的点击事件
+ switch (target.nodeName.toLowerCase()) {
+ case 'button':
+ case 'select':
+ case 'textarea':
+ if (target.disabled) {
+ return true
}
- return false
- },
- needFocus: function(target) {
- switch (target.nodeName.toLowerCase()) {
- case 'textarea':
- case 'select': //实测android下select也需要
- return true
- case 'input':
- switch (target.type) {
- case 'button':
- case 'checkbox':
- case 'file':
- case 'image':
- case 'radio':
- case 'submit':
- return false
- }
- //如果是只读或disabled状态,就无须获得焦点了
- return !target.disabled && !target.readOnly
- default:
- return false
- }
- },
- focus: function(targetElement) {
- var length
- //在iOS7下, 对一些新表单元素(如date, datetime, time, month)调用focus方法会抛错,
- //幸好的是,我们可以改用setSelectionRange获取焦点, 将光标挪到文字的最后
- var type = targetElement.type
- if (
- deviceIsIOS &&
- targetElement.setSelectionRange &&
- type.indexOf('date') !== 0 &&
- type !== 'time' &&
- type !== 'month'
- ) {
- length = targetElement.value.length
- targetElement.setSelectionRange(length, length)
- } else {
- targetElement.focus()
- }
- },
- findControl: function(labelElement) {
- // 获取label元素所对应的表单元素
- // 可以能过control属性, getElementById, 或用querySelector直接找其内部第一表单元素实现
- if (labelElement.control !== undefined) {
- return labelElement.control
+ break
+ case 'input':
+ // IOS6 pad 上选择文件,如果不是原生的click,弹出的选择界面尺寸错误
+ if ((deviceIsIOS && target.type === 'file') || target.disabled) {
+ return true
}
- if (labelElement.htmlFor) {
- return document.getElementById(labelElement.htmlFor)
- }
-
- return labelElement.querySelector(
- 'button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea'
- )
- },
- fixTarget: function(target) {
- if (target.nodeType === 3) {
- return target.parentNode
- }
- if (window.SVGElementInstance && target instanceof SVGElementInstance) {
- return target.correspondingUseElement
- }
-
- return target
- },
- updateScrollParent: function(targetElement) {
- //如果事件源元素位于某一个有滚动条的祖父元素中,那么保持其scrollParent与scrollTop值
- var scrollParent = targetElement.tapScrollParent
-
- if (!scrollParent || !scrollParent.contains(targetElement)) {
- var parentElement = targetElement
- do {
- if (parentElement.scrollHeight > parentElement.offsetHeight) {
- scrollParent = parentElement
- targetElement.tapScrollParent = parentElement
- break
- }
-
- parentElement = parentElement.parentElement
- } while (parentElement)
- }
-
- if (scrollParent) {
- scrollParent.lastScrollTop = scrollParent.scrollTop
- }
- },
- touchHasMoved: function(event) {
- //判定是否发生移动,其阀值是10px
- var touch = event.changedTouches[0],
- boundary = tapRecognizer.touchBoundary
- return (
- Math.abs(touch.pageX - tapRecognizer.pageX) > boundary ||
- Math.abs(touch.pageY - tapRecognizer.pageY) > boundary
- )
- },
-
- findType: function(targetElement) {
- // 安卓chrome浏览器上,模拟的 click 事件不能让 select 打开,故使用 mousedown 事件
- return deviceIsAndroid &&
- targetElement.tagName.toLowerCase() === 'select'
- ? 'mousedown'
- : 'click'
- },
- sendClick: function(targetElement, event) {
- // 在click之前触发tap事件
- Recognizer.fire(targetElement, 'tap', {
- touchEvent: event
- })
- var clickEvent, touch
- //某些安卓设备必须先移除焦点,之后模拟的click事件才能让新元素获取焦点
- if (
- document.activeElement &&
- document.activeElement !== targetElement
- ) {
- document.activeElement.blur()
- }
-
- touch = event.changedTouches[0]
- // 手动触发点击事件,此时必须使用document.createEvent('MouseEvents')来创建事件
- // 及使用initMouseEvent来初始化它
- clickEvent = document.createEvent('MouseEvents')
- clickEvent.initMouseEvent(
- tapRecognizer.findType(targetElement),
- true,
- true,
- window,
- 1,
- touch.screenX,
- touch.screenY,
- touch.clientX,
- touch.clientY,
- false,
- false,
- false,
- false,
- 0,
- null
- )
- clickEvent.touchEvent = event
- targetElement.dispatchEvent(clickEvent)
- },
- touchstart: function(event) {
- //忽略多点触摸
- if (event.targetTouches.length !== 1) {
- return true
- }
- //修正事件源对象
- var targetElement = tapRecognizer.fixTarget(event.target)
- var touch = event.targetTouches[0]
- if (deviceIsIOS) {
- // 判断是否是点击文字,进行选择等操作,如果是,不需要模拟click
- var selection = window.getSelection()
- if (selection.rangeCount && !selection.isCollapsed) {
- return true
- }
- var id = touch.identifier
- //当 alert 或 confirm 时,点击其他地方,会触发touch事件,identifier相同,此事件应该被忽略
- if (
- id &&
- isFinite(tapRecognizer.lastTouchIdentifier) &&
- tapRecognizer.lastTouchIdentifier === id
- ) {
- event.preventDefault()
- return false
- }
-
- tapRecognizer.lastTouchIdentifier = id
-
- tapRecognizer.updateScrollParent(targetElement)
- }
- //收集触摸点的信息
- tapRecognizer.status = 'tapping'
- tapRecognizer.startTime = Date.now()
- tapRecognizer.element = targetElement
- tapRecognizer.pageX = touch.pageX
- tapRecognizer.pageY = touch.pageY
- // 如果点击太快,阻止双击带来的放大收缩行为
- if (
- tapRecognizer.startTime - tapRecognizer.lastTime <
- tapRecognizer.tapDelay
- ) {
- event.preventDefault()
- }
- },
- touchmove: function(event) {
- if (tapRecognizer.status !== 'tapping') {
- return true
- }
- // 如果事件源元素发生改变,或者发生了移动,那么就取消触发点击事件
- if (
- tapRecognizer.element !== tapRecognizer.fixTarget(event.target) ||
- tapRecognizer.touchHasMoved(event)
- ) {
- tapRecognizer.status = tapRecognizer.element = 0
- }
- },
- touchend: function(event) {
- var targetElement = tapRecognizer.element
- var now = Date.now()
- //如果是touchstart与touchend相隔太久,可以认为是长按,那么就直接返回
- //或者是在touchstart, touchmove阶段,判定其不该触发点击事件,也直接返回
- if (
- !targetElement ||
- now - tapRecognizer.startTime > tapRecognizer.tapDelay
- ) {
- return true
- }
-
- tapRecognizer.lastTime = now
-
- var startTime = tapRecognizer.startTime
- tapRecognizer.status = tapRecognizer.startTime = 0
-
- targetTagName = targetElement.tagName.toLowerCase()
- if (targetTagName === 'label') {
- //尝试触发label上可能绑定的tap事件
- Recognizer.fire(targetElement, 'tap', {
- touchEvent: event
- })
- var forElement = tapRecognizer.findControl(targetElement)
- if (forElement) {
- tapRecognizer.focus(targetElement)
- targetElement = forElement
- }
- } else if (tapRecognizer.needFocus(targetElement)) {
- // 如果元素从touchstart到touchend经历时间过长,那么不应该触发点击事
- // 或者此元素是iframe中的input元素,那么它也无法获点焦点
- if (
- now - startTime > 100 ||
- (deviceIsIOS &&
- window.top !== window &&
- targetTagName === 'input')
- ) {
- tapRecognizer.element = 0
- return false
- }
-
- tapRecognizer.focus(targetElement)
- deviceIsAndroid && tapRecognizer.sendClick(targetElement, event)
+ break
+ case 'label':
+ case 'iframe':
+ case 'video':
+ return true
+ }
+ return false
+ },
+ needFocus: function(target) {
+ switch (target.nodeName.toLowerCase()) {
+ case 'textarea':
+ case 'select': //实测android下select也需要
+ return true
+ case 'input':
+ switch (target.type) {
+ case 'button':
+ case 'checkbox':
+ case 'file':
+ case 'image':
+ case 'radio':
+ case 'submit':
return false
}
-
- if (deviceIsIOS) {
- //如果它的父容器的滚动条发生改变,那么应该识别为划动或拖动事件,不应该触发点击事件
- var scrollParent = targetElement.tapScrollParent
- if (
- scrollParent &&
- scrollParent.lastScrollTop !== scrollParent.scrollTop
- ) {
- return true
- }
- }
- //如果这不是一个需要使用原生click的元素,则屏蔽原生事件,避免触发两次click
- if (!tapRecognizer.needClick(targetElement)) {
- event.preventDefault()
- // 触发一次模拟的click
- tapRecognizer.sendClick(targetElement, event)
- }
- },
- touchcancel: function() {
- tapRecognizer.startTime = tapRecognizer.element = 0
+ //如果是只读或disabled状态,就无须获得焦点了
+ return !target.disabled && !target.readOnly
+ default:
+ return false
}
+ },
+ focus: function(targetElement) {
+ var length
+ //在iOS7下, 对一些新表单元素(如date, datetime, time, month)调用focus方法会抛错,
+ //幸好的是,我们可以改用setSelectionRange获取焦点, 将光标挪到文字的最后
+ var type = targetElement.type
+ if (
+ deviceIsIOS &&
+ targetElement.setSelectionRange &&
+ type.indexOf('date') !== 0 &&
+ type !== 'time' &&
+ type !== 'month'
+ ) {
+ length = targetElement.value.length
+ targetElement.setSelectionRange(length, length)
+ } else {
+ targetElement.focus()
+ }
+ },
+ findControl: function(labelElement) {
+ // 获取label元素所对应的表单元素
+ // 可以能过control属性, getElementById, 或用querySelector直接找其内部第一表单元素实现
+ if (labelElement.control !== undefined) {
+ return labelElement.control
+ }
+
+ if (labelElement.htmlFor) {
+ return document.getElementById(labelElement.htmlFor)
+ }
+
+ return labelElement.querySelector(
+ 'button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea'
+ )
+ },
+ fixTarget: function(target) {
+ if (target.nodeType === 3) {
+ return target.parentNode
+ }
+ if (window.SVGElementInstance && target instanceof SVGElementInstance) {
+ return target.correspondingUseElement
+ }
+
+ return target
+ },
+ updateScrollParent: function(targetElement) {
+ //如果事件源元素位于某一个有滚动条的祖父元素中,那么保持其scrollParent与scrollTop值
+ var scrollParent = targetElement.tapScrollParent
+
+ if (!scrollParent || !scrollParent.contains(targetElement)) {
+ var parentElement = targetElement
+ do {
+ if (parentElement.scrollHeight > parentElement.offsetHeight) {
+ scrollParent = parentElement
+ targetElement.tapScrollParent = parentElement
+ break
+ }
+
+ parentElement = parentElement.parentElement
+ } while (parentElement)
+ }
+
+ if (scrollParent) {
+ scrollParent.lastScrollTop = scrollParent.scrollTop
+ }
+ },
+ touchHasMoved: function(event) {
+ //判定是否发生移动,其阀值是10px
+ var touch = event.changedTouches[0],
+ boundary = tapRecognizer.touchBoundary
+ return (
+ Math.abs(touch.pageX - tapRecognizer.pageX) > boundary ||
+ Math.abs(touch.pageY - tapRecognizer.pageY) > boundary
+ )
+ },
+
+ findType: function(targetElement) {
+ // 安卓chrome浏览器上,模拟的 click 事件不能让 select 打开,故使用 mousedown 事件
+ return deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select'
+ ? 'mousedown'
+ : 'click'
+ },
+ sendClick: function(targetElement, event) {
+ // 在click之前触发tap事件
+ Recognizer.fire(targetElement, 'tap', {
+ touchEvent: event
+ })
+ var clickEvent, touch
+ //某些安卓设备必须先移除焦点,之后模拟的click事件才能让新元素获取焦点
+ if (document.activeElement && document.activeElement !== targetElement) {
+ document.activeElement.blur()
+ }
+
+ touch = event.changedTouches[0]
+ // 手动触发点击事件,此时必须使用document.createEvent('MouseEvents')来创建事件
+ // 及使用initMouseEvent来初始化它
+ clickEvent = document.createEvent('MouseEvents')
+ clickEvent.initMouseEvent(
+ tapRecognizer.findType(targetElement),
+ true,
+ true,
+ window,
+ 1,
+ touch.screenX,
+ touch.screenY,
+ touch.clientX,
+ touch.clientY,
+ false,
+ false,
+ false,
+ false,
+ 0,
+ null
+ )
+ clickEvent.touchEvent = event
+ targetElement.dispatchEvent(clickEvent)
+ },
+ touchstart: function(event) {
+ //忽略多点触摸
+ if (event.targetTouches.length !== 1) {
+ return true
+ }
+ //修正事件源对象
+ var targetElement = tapRecognizer.fixTarget(event.target)
+ var touch = event.targetTouches[0]
+ if (deviceIsIOS) {
+ // 判断是否是点击文字,进行选择等操作,如果是,不需要模拟click
+ var selection = window.getSelection()
+ if (selection.rangeCount && !selection.isCollapsed) {
+ return true
+ }
+ var id = touch.identifier
+ //当 alert 或 confirm 时,点击其他地方,会触发touch事件,identifier相同,此事件应该被忽略
+ if (
+ id &&
+ isFinite(tapRecognizer.lastTouchIdentifier) &&
+ tapRecognizer.lastTouchIdentifier === id
+ ) {
+ event.preventDefault()
+ return false
+ }
+
+ tapRecognizer.lastTouchIdentifier = id
+
+ tapRecognizer.updateScrollParent(targetElement)
+ }
+ //收集触摸点的信息
+ tapRecognizer.status = 'tapping'
+ tapRecognizer.startTime = Date.now()
+ tapRecognizer.element = targetElement
+ tapRecognizer.pageX = touch.pageX
+ tapRecognizer.pageY = touch.pageY
+ // 如果点击太快,阻止双击带来的放大收缩行为
+ if (
+ tapRecognizer.startTime - tapRecognizer.lastTime <
+ tapRecognizer.tapDelay
+ ) {
+ event.preventDefault()
+ }
+ },
+ touchmove: function(event) {
+ if (tapRecognizer.status !== 'tapping') {
+ return true
+ }
+ // 如果事件源元素发生改变,或者发生了移动,那么就取消触发点击事件
+ if (
+ tapRecognizer.element !== tapRecognizer.fixTarget(event.target) ||
+ tapRecognizer.touchHasMoved(event)
+ ) {
+ tapRecognizer.status = tapRecognizer.element = 0
+ }
+ },
+ touchend: function(event) {
+ var targetElement = tapRecognizer.element
+ var now = Date.now()
+ //如果是touchstart与touchend相隔太久,可以认为是长按,那么就直接返回
+ //或者是在touchstart, touchmove阶段,判定其不该触发点击事件,也直接返回
+ if (
+ !targetElement ||
+ now - tapRecognizer.startTime > tapRecognizer.tapDelay
+ ) {
+ return true
+ }
+
+ tapRecognizer.lastTime = now
+
+ var startTime = tapRecognizer.startTime
+ tapRecognizer.status = tapRecognizer.startTime = 0
+
+ targetTagName = targetElement.tagName.toLowerCase()
+ if (targetTagName === 'label') {
+ //尝试触发label上可能绑定的tap事件
+ Recognizer.fire(targetElement, 'tap', {
+ touchEvent: event
+ })
+ var forElement = tapRecognizer.findControl(targetElement)
+ if (forElement) {
+ tapRecognizer.focus(targetElement)
+ targetElement = forElement
+ }
+ } else if (tapRecognizer.needFocus(targetElement)) {
+ // 如果元素从touchstart到touchend经历时间过长,那么不应该触发点击事
+ // 或者此元素是iframe中的input元素,那么它也无法获点焦点
+ if (
+ now - startTime > 100 ||
+ (deviceIsIOS && window.top !== window && targetTagName === 'input')
+ ) {
+ tapRecognizer.element = 0
+ return false
+ }
+
+ tapRecognizer.focus(targetElement)
+ deviceIsAndroid && tapRecognizer.sendClick(targetElement, event)
+
+ return false
+ }
+
+ if (deviceIsIOS) {
+ //如果它的父容器的滚动条发生改变,那么应该识别为划动或拖动事件,不应该触发点击事件
+ var scrollParent = targetElement.tapScrollParent
+ if (
+ scrollParent &&
+ scrollParent.lastScrollTop !== scrollParent.scrollTop
+ ) {
+ return true
+ }
+ }
+ //如果这不是一个需要使用原生click的元素,则屏蔽原生事件,避免触发两次click
+ if (!tapRecognizer.needClick(targetElement)) {
+ event.preventDefault()
+ // 触发一次模拟的click
+ tapRecognizer.sendClick(targetElement, event)
+ }
+ },
+ touchcancel: function() {
+ tapRecognizer.startTime = tapRecognizer.element = 0
+ }
}
Recognizer.add('tap', tapRecognizer)
var pressRecognizer = {
- events: ['longtap', 'doubletap'],
- cancelPress: function(pointer) {
- clearTimeout(pointer.pressingHandler)
- pointer.pressingHandler = null
- },
- touchstart: function(event) {
- Recognizer.start(event, function(pointer, touch) {
- pointer.pressingHandler = setTimeout(function() {
- if (pointer.status === 'tapping') {
- Recognizer.fire(event.target, 'longtap', {
- touch: touch,
- touchEvent: event
- })
- }
- pressRecognizer.cancelPress(pointer)
- }, 800)
- if (event.changedTouches.length !== 1) {
- pointer.status = 0
- }
- })
- },
- touchmove: function(event) {
- Recognizer.move(event, function(pointer) {
- if (pointer.distance > 10 && pointer.pressingHandler) {
- pressRecognizer.cancelPress(pointer)
- if (pointer.status === 'tapping') {
- pointer.status = 'panning'
- }
- }
- })
- },
- touchend: function(event) {
- Recognizer.end(event, function(pointer, touch) {
- pressRecognizer.cancelPress(pointer)
- if (pointer.status === 'tapping') {
- pointer.lastTime = Date.now()
- if (
- pressRecognizer.lastTap &&
- pointer.lastTime - pressRecognizer.lastTap.lastTime < 300
- ) {
- Recognizer.fire(pointer.element, 'doubletap', {
- touch: touch,
- touchEvent: event
- })
- }
+ events: ['longtap', 'doubletap'],
+ cancelPress: function(pointer) {
+ clearTimeout(pointer.pressingHandler)
+ pointer.pressingHandler = null
+ },
+ touchstart: function(event) {
+ Recognizer.start(event, function(pointer, touch) {
+ pointer.pressingHandler = setTimeout(function() {
+ if (pointer.status === 'tapping') {
+ Recognizer.fire(event.target, 'longtap', {
+ touch: touch,
+ touchEvent: event
+ })
+ }
+ pressRecognizer.cancelPress(pointer)
+ }, 800)
+ if (event.changedTouches.length !== 1) {
+ pointer.status = 0
+ }
+ })
+ },
+ touchmove: function(event) {
+ Recognizer.move(event, function(pointer) {
+ if (pointer.distance > 10 && pointer.pressingHandler) {
+ pressRecognizer.cancelPress(pointer)
+ if (pointer.status === 'tapping') {
+ pointer.status = 'panning'
+ }
+ }
+ })
+ },
+ touchend: function(event) {
+ Recognizer.end(event, function(pointer, touch) {
+ pressRecognizer.cancelPress(pointer)
+ if (pointer.status === 'tapping') {
+ pointer.lastTime = Date.now()
+ if (
+ pressRecognizer.lastTap &&
+ pointer.lastTime - pressRecognizer.lastTap.lastTime < 300
+ ) {
+ Recognizer.fire(pointer.element, 'doubletap', {
+ touch: touch,
+ touchEvent: event
+ })
+ }
- pressRecognizer.lastTap = pointer
- }
- })
- },
- touchcancel: function(event) {
- Recognizer.end(event, function(pointer) {
- pressRecognizer.cancelPress(pointer)
- })
- }
+ pressRecognizer.lastTap = pointer
+ }
+ })
+ },
+ touchcancel: function(event) {
+ Recognizer.end(event, function(pointer) {
+ pressRecognizer.cancelPress(pointer)
+ })
+ }
}
Recognizer.add('press', pressRecognizer)
var swipeRecognizer = {
- events: ['swipe', 'swipeleft', 'swiperight', 'swipeup', 'swipedown'],
- getAngle: function(x, y) {
- return Math.atan2(y, x) * 180 / Math.PI
- },
- getDirection: function(x, y) {
- var angle = swipeRecognizer.getAngle(x, y)
- if (angle < -45 && angle > -135) {
- return 'up'
- } else if (angle >= 45 && angle < 315) {
- return 'down'
- } else if (angle > -45 && angle <= 45) {
- return 'right'
- } else {
- return 'left'
- }
- },
- touchstart: function(event) {
- Recognizer.start(event, noop)
- },
- touchmove: function(event) {
- Recognizer.move(event, noop)
- },
- touchend: function(event) {
- if (event.changedTouches.length !== 1) {
- return
- }
- Recognizer.end(event, function(pointer, touch) {
- var isflick =
- pointer.distance > 30 &&
- pointer.distance / pointer.duration > 0.65
- if (isflick) {
- var extra = {
- deltaX: pointer.deltaX,
- deltaY: pointer.deltaY,
- touch: touch,
- touchEvent: event,
- direction: swipeRecognizer.getDirection(
- pointer.deltaX,
- pointer.deltaY
- ),
- isVertical: pointer.isVertical
- }
- var target = pointer.element
- Recognizer.fire(target, 'swipe', extra)
- Recognizer.fire(target, 'swipe' + extra.direction, extra)
- }
- })
+ events: ['swipe', 'swipeleft', 'swiperight', 'swipeup', 'swipedown'],
+ getAngle: function(x, y) {
+ return Math.atan2(y, x) * 180 / Math.PI
+ },
+ getDirection: function(x, y) {
+ var angle = swipeRecognizer.getAngle(x, y)
+ if (angle < -45 && angle > -135) {
+ return 'up'
+ } else if (angle >= 45 && angle < 315) {
+ return 'down'
+ } else if (angle > -45 && angle <= 45) {
+ return 'right'
+ } else {
+ return 'left'
}
+ },
+ touchstart: function(event) {
+ Recognizer.start(event, noop)
+ },
+ touchmove: function(event) {
+ Recognizer.move(event, noop)
+ },
+ touchend: function(event) {
+ if (event.changedTouches.length !== 1) {
+ return
+ }
+ Recognizer.end(event, function(pointer, touch) {
+ var isflick =
+ pointer.distance > 30 && pointer.distance / pointer.duration > 0.65
+ if (isflick) {
+ var extra = {
+ deltaX: pointer.deltaX,
+ deltaY: pointer.deltaY,
+ touch: touch,
+ touchEvent: event,
+ direction: swipeRecognizer.getDirection(
+ pointer.deltaX,
+ pointer.deltaY
+ ),
+ isVertical: pointer.isVertical
+ }
+ var target = pointer.element
+ Recognizer.fire(target, 'swipe', extra)
+ Recognizer.fire(target, 'swipe' + extra.direction, extra)
+ }
+ })
+ }
}
swipeRecognizer.touchcancel = swipeRecognizer.touchend