<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery仿Android锁屏图案应用插件DEMO演示</title> <link href="css/patternLock.css" rel="stylesheet" type="text/css" /> <script src="js/jquery.min.js"></script> <script src="js/patternLock.js"></script> <style type="text/css"> #warp{width:600px; margin:auto;} #warp div{margin-bottom:50px;} </style> </head> <body> <div id="warp"> <h1>图案锁实例</h1> <p>1、基础初始化</p> <div id="patternContainer"></div> <p>2、两点间到达目标点才画线</p> <div id="patternContainer1"></div> <p>3、当图案连线完成后才显示连线</p> <div id="patternContainer2"></div> <p>4、改变两个点之间的距</p> <div id="patternContainer3"></div> <p>5、自定义距阵</p> <div id="patternContainer4"></div> <p>6、使用映射</p> <div id="patternContainer5"></div> <p>7、作为验证码使用,这个在服务端需用到 patternCaptha,有兴趣的朋友可以研究下,这里就不多说了。</p> <div id="patternContainer6"></div> </div> </body> </html> <script> $(function(){ var lock = new PatternLock("#patternContainer"); var lock1 = new PatternLock("#patternContainer1",{lineOnMove:false}); var lock2 = new PatternLock("#patternContainer2",{patternVisible:false}); var lock3 = new PatternLock("#patternContainer3",{radius:30,margin:20}); var lock4 = new PatternLock("#patternContainer4",{matrix:[4,4]}); var lock5 = new PatternLock("#patternContainer5", { mapper: function(idx){ return (idx%9) + 1; } }); }) </script>
CSS
/* patternLock.js v 0.2.0 Author: Sudhanshu Yadav Copyright (c) 2013 Sudhanshu Yadav - ignitersworld.com , released under the MIT license. Demo on: ignitersworld.com/lab/patternLock.html */ .patt-holder{background:#3382c0;} .patt-wrap{position:relative; cursor:pointer;} .patt-wrap ul, .patt-wrap li{ list-style: none; margin:0; padding: 0; } .patt-circ{ position:relative; float: left; box-sizing: border-box; -moz-box-sizing: border-box; } .patt-circ.hovered{ border:3px solid #009900; } .patt-error .patt-circ.hovered{ border:3px solid #BA1B26; } .patt-hidden .patt-circ.hovered{border:0;} .patt-dots{ background: #FFF; width: 10px;height: 10px; border-radius:5px; position:absolute; top:50%; left:50%; margin-top:-5px; margin-left:-5px; } .patt-lines{ border-radius:5px; height:10px; background:rgba(255,255,255,.7); position:absolute; transform-origin:5px 5px; -ms-transform-origin:5px 5px; /* IE 9 */ -webkit-transform-origin:5px 5px; } .patt-hidden .patt-lines{ display:none; }
JS
/* patternLock.js v 0.2.0 Author: Sudhanshu Yadav Copyright (c) 2013 Sudhanshu Yadav - ignitersworld.com , released under the MIT license. Demo on: ignitersworld.com/lab/patternLock.html */ (function ($, window, document, undefined) { "use strict"; var isTouchSupported = !!('ontouchstart' in window), touchStart = isTouchSupported ? "touchstart" : "mousedown", touchEnd = isTouchSupported ? "touchend" : "mouseup", touchMove = isTouchSupported ? "touchmove" : "mousemove", nullFunc = function () {}, objectHolder = {}; //internal functions function readyDom(iObj) { var holder = iObj.holder, option = iObj.option, matrix = option.matrix, margin = option.margin, radius = option.radius, html = ['<ul class="patt-wrap" style="padding:' + margin + 'px">']; for (var i = 0, ln = matrix[0] * matrix[1]; i < ln; i++) { html.push('<li class="patt-circ" style="margin:' + margin + 'px; width : ' + (radius * 2) + 'px; height : ' + (radius * 2) + 'px; -webkit-border-radius: ' + radius + 'px; -moz-border-radius: ' + radius + 'px; border-radius: ' + radius + 'px; "><div class="patt-dots"></div></li>'); } html.push('</ul>'); holder.html(html.join('')).css({ 'width': (matrix[1] * (radius * 2 + margin * 2) + margin * 2) + 'px', 'height': (matrix[0] * (radius * 2 + margin * 2) + margin * 2) + 'px' }); //select pattern circle iObj.pattCircle = iObj.holder.find('.patt-circ'); } //return height and angle for lines function getLengthAngle(x1, x2, y1, y2) { var xDiff = x2 - x1, yDiff = y2 - y1; return { length: Math.ceil(Math.sqrt(xDiff * xDiff + yDiff * yDiff)), angle: Math.round((Math.atan2(yDiff, xDiff) * 180) / Math.PI) }; } var startHandler = function (e, obj) { e.preventDefault(); var iObj = objectHolder[obj.token]; //check if pattern is visible or not if (!iObj.option.patternVisible) { iObj.holder.addClass('patt-hidden'); } //assign events $(this).on(touchMove + '.pattern-move', function (e) { moveHandler.call(this, e, obj); }); $(document).one(touchEnd, function () { endHandler.call(this, e, obj); }); //set pattern offset var wrap = iObj.holder.find('.patt-wrap'), offset = wrap.offset(); iObj.wrapTop = offset.top; iObj.wrapLeft = offset.left; //reset pattern obj.reset(); }, moveHandler = function (e, obj) { e.preventDefault(); var x = e.pageX || e.originalEvent.touches[0].pageX, y = e.pageY || e.originalEvent.touches[0].pageY, iObj = objectHolder[obj.token], li = iObj.pattCircle, patternAry = iObj.patternAry, lineOnMove = iObj.option.lineOnMove, posObj = iObj.getIdxFromPoint(x, y), idx = posObj.idx, pattId = iObj.mapperFunc(idx) || idx; if (patternAry.length > 0) { var laMove = getLengthAngle(iObj.lineX1, posObj.x, iObj.lineY1, posObj.y); iObj.line.css({ 'width': (laMove.length + 10) + 'px', 'transform': 'rotate(' + laMove.angle + 'deg)' }); } if (idx) { if (patternAry.indexOf(pattId) == -1) { var elm = $(li[idx - 1]); elm.addClass('hovered'); //push pattern on array patternAry.push(pattId); //add start point for line var margin = iObj.option.margin, radius = iObj.option.radius, newX = (posObj.i - 1) * (2 * margin + 2 * radius) + 2 * margin + radius, newY = (posObj.j - 1) * (2 * margin + 2 * radius) + 2 * margin + radius; if (patternAry.length != 1) { //to fix line var lA = getLengthAngle(iObj.lineX1, newX, iObj.lineY1, newY); iObj.line.css({ 'width': (lA.length + 10) + 'px', 'transform': 'rotate(' + lA.angle + 'deg)' }); if (!lineOnMove) iObj.line.show(); } //to create new line var line = $('<div class="patt-lines" style="top:' + (newY - 5) + 'px; left:' + (newX - 5) + 'px"></div>'); iObj.line = line; iObj.lineX1 = newX; iObj.lineY1 = newY; //add on dom iObj.holder.append(line); if (!lineOnMove) iObj.line.hide(); } } }, endHandler = function (e, obj) { e.preventDefault(); var iObj = objectHolder[obj.token], pattern = iObj.patternAry.join(''); //remove hidden pattern class and remove event iObj.holder.off('.pattern-move').removeClass('patt-hidden'); if (!pattern) return; iObj.option.onDraw(pattern); //to remove last line iObj.line.remove(); if (iObj.rightPattern) { if (pattern == iObj.rightPattern) { iObj.onSuccess(); } else { iObj.onError(); obj.error(); } } }; function InternalMethods() {} InternalMethods.prototype = { constructor: InternalMethods, getIdxFromPoint: function (x, y) { var option = this.option, matrix = option.matrix, xi = x - this.wrapLeft, yi = y - this.wrapTop, idx = null, margin = option.margin, plotLn = option.radius * 2 + margin * 2, qsntX = Math.ceil(xi / plotLn), qsntY = Math.ceil(yi / plotLn), remX = xi % plotLn, remY = yi % plotLn; if (qsntX <= matrix[1] && qsntY <= matrix[0] && remX > margin * 2 && remY > margin * 2) { idx = (qsntY - 1) * matrix[1] + qsntX; } return { idx: idx, i: qsntX, j: qsntY, x: xi, y: yi }; } }; function PatternLock(selector, option) { var self = this, token = self.token = Math.random(), iObj = objectHolder[token] = new InternalMethods(), holder = iObj.holder = $(selector); //if holder is not present return if (holder.length == 0) return; iObj.object = self; option = iObj.option = $.extend({}, PatternLock.defaults, option); readyDom(iObj); //add class on holder holder.addClass('patt-holder'); //change offset property of holder if it does not have any property if (holder.css('position') == "static") holder.css('position', 'relative'); //assign event holder.on(touchStart, function (e) { startHandler.call(this, e, self); }); //handeling callback iObj.option.onDraw = option.onDraw || nullFunc; //adding a mapper function var mapper = option.mapper; if (typeof mapper == "object") { iObj.mapperFunc = function (idx) { return mapper[idx]; }; } else if (typeof mapper == "function") { iObj.mapperFunc = mapper; } else { iObj.mapperFunc = nullFunc; } //to delete from option object iObj.option.mapper = null; } PatternLock.prototype = { constructor: PatternLock, option: function (key, val) { var iObj = objectHolder[this.token], option = iObj.option; //for set methods if (!val) { return option[key]; } //for setter else { option[key] = val; if (key == "margin" || key == "matrix" || key == "radius") { readyDom(iObj); } } }, getPattern: function () { return objectHolder[this.token].patternAry.join(''); }, reset: function () { var iObj = objectHolder[this.token]; //to remove lines iObj.pattCircle.removeClass('hovered'); iObj.holder.find('.patt-lines').remove(); //add/reset a array which capture pattern iObj.patternAry = []; //remove error class if added iObj.holder.removeClass('patt-error'); }, error: function () { objectHolder[this.token].holder.addClass('patt-error'); }, checkForPattern: function (pattern, success, error) { var iObj = objectHolder[this.token]; iObj.rightPattern = pattern; iObj.onSuccess = success || nullFunc; iObj.onError = error || nullFunc; } }; PatternLock.defaults = { matrix: [3, 3], margin: 20, radius: 25, patternVisible: true, lineOnMove: true }; window.PatternLock = PatternLock; }(jQuery, window, document));