|
|
|
|
/**
|
|
|
|
|
* 手势锁屏插件
|
|
|
|
|
* 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));
|