|
|
/** |
|
|
* 手势锁屏插件 |
|
|
* varstion 1.0.5 |
|
|
* by Houfeng |
|
|
* Houfeng@DCloud.io |
|
|
*/ |
|
|
|
|
|
(function($, doc) { |
|
|
|
|
|
var touchSupport = ('ontouchstart' in document); |
|
|
var startEventName = touchSupport ? $.EVENT_START : 'mousedown'; |
|
|
var moveEventName = touchSupport ? $.EVENT_MOVE : 'mousemove'; |
|
|
var endEventName = touchSupport ? $.EVENT_END : 'mouseup'; |
|
|
var lockerHolderClassName = $.className('locker-holder'); |
|
|
var lockerClassName = $.className('locker'); |
|
|
|
|
|
var styleHolder = doc.querySelector('head') || doc.querySelector('body'); |
|
|
styleHolder.innerHTML += "<style>.mui-locker-holder{overflow:hidden;position:relative;padding:0px;}.mui-locker-holder canvas{width:100%;height:100%;}</style>"; |
|
|
|
|
|
var times = 2; |
|
|
|
|
|
function getElementLeft(element) { |
|
|
var actualLeft = element.offsetLeft; |
|
|
var current = element.offsetParent; |
|
|
while (current !== null) { |
|
|
actualLeft += current.offsetLeft; |
|
|
current = current.offsetParent; |
|
|
} |
|
|
return actualLeft; |
|
|
} |
|
|
|
|
|
function getElementTop(element) { |
|
|
var actualTop = element.offsetTop; |
|
|
var current = element.offsetParent; |
|
|
while (current !== null) { |
|
|
actualTop += current.offsetTop; |
|
|
current = current.offsetParent; |
|
|
} |
|
|
return actualTop; |
|
|
} |
|
|
|
|
|
//定义 Locker 类 |
|
|
var Locker = $.Locker = $.Class.extend({ |
|
|
R: 26, |
|
|
CW: 400, |
|
|
CH: 320, |
|
|
OffsetX: 30, |
|
|
OffsetY: 30, |
|
|
|
|
|
/** |
|
|
* 构造函数 |
|
|
* */ |
|
|
init: function(holder, options) { |
|
|
var self = this; |
|
|
if (!holder) { |
|
|
throw "构造 Locker 时缺少容器元素"; |
|
|
} |
|
|
self.holder = holder; |
|
|
// |
|
|
options = options || {}; |
|
|
options.callback = options.callback || options.done || $.noop; |
|
|
options.times = options.times || times; |
|
|
self.options = options; |
|
|
self.holder.innerHTML = '<canvas></canvas>'; |
|
|
// |
|
|
self.holder.classList.add(lockerHolderClassName); |
|
|
//初始化 |
|
|
var canvas = self.canvas = $.qsa('canvas', self.holder)[0]; |
|
|
canvas.on = canvas.addEventListener || function(name, handler, capture) { |
|
|
canvas.attachEvent('on' + name, handler, capture); |
|
|
}; |
|
|
canvas.off = canvas.removeEventListener || function(name, handler, capture) { |
|
|
canvas.detachEvent('on' + name, handler, capture); |
|
|
}; |
|
|
// |
|
|
if (self.options.width) self.holder.style.width = self.options.width + 'px'; |
|
|
if (self.options.height) self.holder.style.height = self.options.height + 'px'; |
|
|
self.CW = self.options.width || self.holder.offsetWidth || self.CW; |
|
|
self.CH = self.options.height || self.holder.offsetHeight || self.CH; |
|
|
//处理 “宽、高” 等数值, 全部扩大 times 倍 |
|
|
self.R *= self.options.times; |
|
|
self.CW *= self.options.times; |
|
|
self.CH *= self.options.times; |
|
|
self.OffsetX *= self.options.times; |
|
|
self.OffsetY *= self.options.times; |
|
|
// |
|
|
canvas.width = self.CW; |
|
|
canvas.height = self.CH; |
|
|
var cxt = self.cxt = canvas.getContext("2d"); |
|
|
//两个圆之间的外距离 就是说两个圆心的距离去除两个半径 |
|
|
var X = (self.CW - 2 * self.OffsetX - self.R * 2 * 3) / 2; |
|
|
var Y = (self.CH - 2 * self.OffsetY - self.R * 2 * 3) / 2; |
|
|
self.pointLocationArr = self.caculateNinePointLotion(X, Y); |
|
|
self.initEvent(canvas, cxt, self.holder); |
|
|
//console.log(X); |
|
|
self.draw(cxt, self.pointLocationArr, [], null); |
|
|
setTimeout(function() { |
|
|
self.draw(cxt, self.pointLocationArr, [], null); |
|
|
}, 0); |
|
|
}, |
|
|
|
|
|
/** |
|
|
* 计算 |
|
|
*/ |
|
|
caculateNinePointLotion: function(diffX, diffY) { |
|
|
var self = this; |
|
|
var Re = []; |
|
|
for (var row = 0; row < 3; row++) { |
|
|
for (var col = 0; col < 3; col++) { |
|
|
var Point = { |
|
|
X: (self.OffsetX + col * diffX + (col * 2 + 1) * self.R), |
|
|
Y: (self.OffsetY + row * diffY + (row * 2 + 1) * self.R) |
|
|
}; |
|
|
Re.push(Point); |
|
|
} |
|
|
} |
|
|
return Re; |
|
|
}, |
|
|
|
|
|
/** |
|
|
* 绘制 |
|
|
*/ |
|
|
draw: function(cxt, _PointLocationArr, _LinePointArr, touchPoint) { |
|
|
var self = this; |
|
|
var R = self.R; |
|
|
if (_LinePointArr.length > 0) { |
|
|
cxt.beginPath(); |
|
|
for (var i = 0; i < _LinePointArr.length; i++) { |
|
|
var pointIndex = _LinePointArr[i]; |
|
|
cxt.lineTo(_PointLocationArr[pointIndex].X, _PointLocationArr[pointIndex].Y); |
|
|
} |
|
|
cxt.lineWidth = (self.options.lindeWidth || 2) * self.options.times; |
|
|
cxt.strokeStyle = "#606fef"; //连结线颜色self.options.lineColor || |
|
|
cxt.stroke(); |
|
|
cxt.closePath(); |
|
|
if (touchPoint != null) { |
|
|
var lastPointIndex = _LinePointArr[_LinePointArr.length - 1]; |
|
|
var lastPoint = _PointLocationArr[lastPointIndex]; |
|
|
cxt.beginPath(); |
|
|
cxt.moveTo(lastPoint.X, lastPoint.Y); |
|
|
cxt.lineTo(touchPoint.X, touchPoint.Y); |
|
|
cxt.stroke(); |
|
|
cxt.closePath(); |
|
|
} |
|
|
} |
|
|
for (var i = 0; i < _PointLocationArr.length; i++) { |
|
|
var Point = _PointLocationArr[i]; |
|
|
cxt.fillStyle = "#303344"; //圆圈边框颜色self.options.ringColor || |
|
|
cxt.beginPath(); |
|
|
cxt.arc(Point.X, Point.Y, R, 0, Math.PI * 2, true); |
|
|
cxt.closePath(); |
|
|
cxt.fill(); |
|
|
cxt.fillStyle = "#303344"; //圆圈填充颜色self.options.fillColor || |
|
|
cxt.beginPath(); |
|
|
cxt.arc(Point.X, Point.Y, R - ((self.options.ringWidth || 2) * self.options.times), 0, Math.PI * 2, true); |
|
|
cxt.closePath(); |
|
|
cxt.fill(); |
|
|
if (_LinePointArr.indexOf(i) >= 0) { |
|
|
cxt.fillStyle ="#606fef"; //圆圈中心点颜色 self.options.pointColor || |
|
|
cxt.beginPath(); |
|
|
cxt.arc(Point.X, Point.Y, R - ((self.options.pointWidth || 16) * self.options.times), 0, Math.PI * 2, true); |
|
|
cxt.closePath(); |
|
|
cxt.fill(); |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
isPointSelect: function(touches, linePoint) { |
|
|
var self = this; |
|
|
for (var i = 0; i < self.pointLocationArr.length; i++) { |
|
|
var currentPoint = self.pointLocationArr[i]; |
|
|
var xdiff = Math.abs(currentPoint.X - touches.elementX); |
|
|
var ydiff = Math.abs(currentPoint.Y - touches.elementY); |
|
|
var dir = Math.pow((xdiff * xdiff + ydiff * ydiff), 0.5); |
|
|
if (dir < self.R) { |
|
|
if (linePoint.indexOf(i) < 0) { |
|
|
linePoint.push(i); |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
initEvent: function(canvas, cxt, holder) { |
|
|
var self = this; |
|
|
var linePoint = []; |
|
|
var isDown = false; //针对鼠标事件 |
|
|
//start |
|
|
self._startHandler = function(e) { |
|
|
e.point = event.changedTouches ? event.changedTouches[0] : event; |
|
|
e.point.elementX = (e.point.pageX - getElementLeft(holder)) * self.options.times; |
|
|
e.point.elementY = (e.point.pageY - getElementTop(holder)) * self.options.times; |
|
|
self.isPointSelect(e.point, linePoint); |
|
|
isDown = true; |
|
|
}; |
|
|
canvas.on(startEventName, self._startHandler, false); |
|
|
//move |
|
|
self._moveHanlder = function(e) { |
|
|
if (!isDown) return; |
|
|
e.preventDefault(); |
|
|
e.point = event.changedTouches ? event.changedTouches[0] : event; |
|
|
e.point.elementX = (e.point.pageX - getElementLeft(holder)) * self.options.times; |
|
|
e.point.elementY = (e.point.pageY - getElementTop(holder)) * self.options.times; |
|
|
var touches = e.point; |
|
|
self.isPointSelect(touches, linePoint); |
|
|
cxt.clearRect(0, 0, self.CW, self.CH); |
|
|
self.draw(cxt, self.pointLocationArr, linePoint, { |
|
|
X: touches.elementX, |
|
|
Y: touches.elementY |
|
|
}); |
|
|
}; |
|
|
canvas.on(moveEventName, self._moveHanlder, false); |
|
|
//end |
|
|
self._endHandler = function(e) { |
|
|
e.point = event.changedTouches ? event.changedTouches[0] : event; |
|
|
e.point.elementX = (e.point.pageX - getElementLeft(holder)) * self.options.times; |
|
|
e.point.elementY = (e.point.pageY - getElementTop(holder)) * self.options.times; |
|
|
cxt.clearRect(0, 0, self.CW, self.CH); |
|
|
self.draw(cxt, self.pointLocationArr, linePoint, null); |
|
|
//事件数据 |
|
|
var eventData = { |
|
|
sender: self, |
|
|
points: linePoint |
|
|
}; |
|
|
/* |
|
|
* 回调完成事件 |
|
|
* |
|
|
* 备注: |
|
|
* 比较理想的做法是为 Locker 的实例启用事件机制,比如 locker.on('done',handler); |
|
|
* 在 mui 没有完整的公共事件模块前,此版本中 locker 实例暂通过 options.callback 处理 |
|
|
*/ |
|
|
self.options.callback(eventData); |
|
|
//触发声明的DOM的自定义事件(暂定 done 为事件名,可以考虑更有针对的事件名 ) |
|
|
$.trigger(self.holder, 'done', eventData); |
|
|
//- |
|
|
linePoint = []; |
|
|
isDown = false; |
|
|
}; |
|
|
canvas.on(endEventName, self._endHandler, false); |
|
|
}, |
|
|
|
|
|
pointLocationArr: [], |
|
|
|
|
|
/** |
|
|
* 清除图形 |
|
|
* */ |
|
|
clear: function() { |
|
|
var self = this; |
|
|
//self.pointLocationArr = []; |
|
|
if (self.cxt) { |
|
|
self.cxt.clearRect(0, 0, self.CW, self.CH); |
|
|
self.draw(self.cxt, self.pointLocationArr, [], { |
|
|
X: 0, |
|
|
Y: 0 |
|
|
}); |
|
|
} |
|
|
}, |
|
|
|
|
|
/** |
|
|
* 释放资源 |
|
|
* */ |
|
|
dispose: function() { |
|
|
var self = this; |
|
|
self.cxt = null; |
|
|
self.canvas.off(startEventName, self._startHandler); |
|
|
self.canvas.off(moveEventName, self._moveHandler); |
|
|
self.canvas.off(endEventName, self._endHandler); |
|
|
self.holder.innerHTML = ''; |
|
|
self.canvas = null; |
|
|
} |
|
|
}); |
|
|
|
|
|
//添加 locker 插件 |
|
|
$.fn.locker = function(options) { |
|
|
//遍历选择的元素 |
|
|
this.each(function(i, element) { |
|
|
if (element.locker) return; |
|
|
if (options) { |
|
|
element.locker = new Locker(element, options); |
|
|
} else { |
|
|
var optionsText = element.getAttribute('data-locker-options'); |
|
|
var _options = optionsText ? JSON.parse(optionsText) : {}; |
|
|
_options.lineColor = element.getAttribute('data-locker-line-color') || _options.lineColor; |
|
|
_options.ringColor = element.getAttribute('data-locker-ring-color') || _options.ringColor; |
|
|
_options.fillColor = element.getAttribute('data-locker-fill-color') || _options.fillColor; |
|
|
_options.pointColor = element.getAttribute('data-locker-point-color') || _options.pointColor; |
|
|
_options.width = element.getAttribute('data-locker-width') || _options.width; |
|
|
_options.height = element.getAttribute('data-locker-height') || _options.height; |
|
|
element.locker = new Locker(element, _options); |
|
|
} |
|
|
}); |
|
|
return this[0] ? this[0].locker : null; |
|
|
}; |
|
|
|
|
|
//自动处理 class='mui-locker' 的 dom |
|
|
try { |
|
|
$('.' + lockerClassName).locker(); |
|
|
} catch (ex) {} |
|
|
$.ready(function() { |
|
|
$('.' + lockerClassName).locker(); |
|
|
}); |
|
|
|
|
|
}(mui, document)); |