mui.locker.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /**
  2. * 手势锁屏插件
  3. * varstion 1.0.5
  4. * by Houfeng
  5. * Houfeng@DCloud.io
  6. */
  7. (function($, doc) {
  8. var touchSupport = ('ontouchstart' in document);
  9. var startEventName = touchSupport ? $.EVENT_START : 'mousedown';
  10. var moveEventName = touchSupport ? $.EVENT_MOVE : 'mousemove';
  11. var endEventName = touchSupport ? $.EVENT_END : 'mouseup';
  12. var lockerHolderClassName = $.className('locker-holder');
  13. var lockerClassName = $.className('locker');
  14. var styleHolder = doc.querySelector('head') || doc.querySelector('body');
  15. styleHolder.innerHTML += "<style>.mui-locker-holder{overflow:hidden;position:relative;padding:0px;}.mui-locker-holder canvas{width:100%;height:100%;}</style>";
  16. var times = 2;
  17. function getElementLeft(element) {    
  18. var actualLeft = element.offsetLeft;    
  19. var current = element.offsetParent;    
  20. while (current !== null) {      
  21. actualLeft += current.offsetLeft;      
  22. current = current.offsetParent;    
  23. }    
  24. return actualLeft;  
  25. }  
  26. function getElementTop(element) {    
  27. var actualTop = element.offsetTop;    
  28. var current = element.offsetParent;    
  29. while (current !== null) {      
  30. actualTop += current.offsetTop;      
  31. current = current.offsetParent;    
  32. }    
  33. return actualTop;  
  34. }
  35. //定义 Locker 类
  36. var Locker = $.Locker = $.Class.extend({
  37. R: 26,
  38. CW: 400,
  39. CH: 320,
  40. OffsetX: 30,
  41. OffsetY: 30,
  42. /**
  43. * 构造函数
  44. * */
  45. init: function(holder, options) {
  46. var self = this;
  47. if (!holder) {
  48. throw "构造 Locker 时缺少容器元素";
  49. }
  50. self.holder = holder;
  51. //
  52. options = options || {};
  53. options.callback = options.callback || options.done || $.noop;
  54. options.times = options.times || times;
  55. self.options = options;
  56. self.holder.innerHTML = '<canvas></canvas>';
  57. //
  58. self.holder.classList.add(lockerHolderClassName);
  59. //初始化
  60. var canvas = self.canvas = $.qsa('canvas', self.holder)[0];
  61. canvas.on = canvas.addEventListener || function(name, handler, capture) {
  62. canvas.attachEvent('on' + name, handler, capture);
  63. };
  64. canvas.off = canvas.removeEventListener || function(name, handler, capture) {
  65. canvas.detachEvent('on' + name, handler, capture);
  66. };
  67. //
  68. if (self.options.width) self.holder.style.width = self.options.width + 'px';
  69. if (self.options.height) self.holder.style.height = self.options.height + 'px';
  70. self.CW = self.options.width || self.holder.offsetWidth || self.CW;
  71. self.CH = self.options.height || self.holder.offsetHeight || self.CH;
  72. //处理 “宽、高” 等数值, 全部扩大 times 倍
  73. self.R *= self.options.times;
  74. self.CW *= self.options.times;
  75. self.CH *= self.options.times;
  76. self.OffsetX *= self.options.times;
  77. self.OffsetY *= self.options.times;
  78. //
  79. canvas.width = self.CW;
  80. canvas.height = self.CH;
  81. var cxt = self.cxt = canvas.getContext("2d");
  82. //两个圆之间的外距离 就是说两个圆心的距离去除两个半径
  83. var X = (self.CW - 2 * self.OffsetX - self.R * 2 * 3) / 2;
  84. var Y = (self.CH - 2 * self.OffsetY - self.R * 2 * 3) / 2;
  85. self.pointLocationArr = self.caculateNinePointLotion(X, Y);
  86. self.initEvent(canvas, cxt, self.holder);
  87. //console.log(X);
  88. self.draw(cxt, self.pointLocationArr, [], null);
  89. setTimeout(function() {
  90. self.draw(cxt, self.pointLocationArr, [], null);
  91. }, 0);
  92. },
  93. /**
  94. * 计算
  95. */
  96. caculateNinePointLotion: function(diffX, diffY) {
  97. var self = this;
  98. var Re = [];
  99. for (var row = 0; row < 3; row++) {
  100. for (var col = 0; col < 3; col++) {
  101. var Point = {
  102. X: (self.OffsetX + col * diffX + (col * 2 + 1) * self.R),
  103. Y: (self.OffsetY + row * diffY + (row * 2 + 1) * self.R)
  104. };
  105. Re.push(Point);
  106. }
  107. }
  108. return Re;
  109. },
  110. /**
  111. * 绘制
  112. */
  113. draw: function(cxt, _PointLocationArr, _LinePointArr, touchPoint) {
  114. var self = this;
  115. var R = self.R;
  116. if (_LinePointArr.length > 0) {
  117. cxt.beginPath();
  118. for (var i = 0; i < _LinePointArr.length; i++) {
  119. var pointIndex = _LinePointArr[i];
  120. cxt.lineTo(_PointLocationArr[pointIndex].X, _PointLocationArr[pointIndex].Y);
  121. }
  122. cxt.lineWidth = (self.options.lindeWidth || 2) * self.options.times;
  123. cxt.strokeStyle = self.options.lineColor || "#999"; //连结线颜色
  124. cxt.stroke();
  125. cxt.closePath();
  126. if (touchPoint != null) {
  127. var lastPointIndex = _LinePointArr[_LinePointArr.length - 1];
  128. var lastPoint = _PointLocationArr[lastPointIndex];
  129. cxt.beginPath();
  130. cxt.moveTo(lastPoint.X, lastPoint.Y);
  131. cxt.lineTo(touchPoint.X, touchPoint.Y);
  132. cxt.stroke();
  133. cxt.closePath();
  134. }
  135. }
  136. for (var i = 0; i < _PointLocationArr.length; i++) {
  137. var Point = _PointLocationArr[i];
  138. cxt.fillStyle = self.options.ringColor || "#888"; //圆圈边框颜色
  139. cxt.beginPath();
  140. cxt.arc(Point.X, Point.Y, R, 0, Math.PI * 2, true);
  141. cxt.closePath();
  142. cxt.fill();
  143. cxt.fillStyle = self.options.fillColor || "#f3f3f3"; //圆圈填充颜色
  144. cxt.beginPath();
  145. cxt.arc(Point.X, Point.Y, R - ((self.options.ringWidth || 2) * self.options.times), 0, Math.PI * 2, true);
  146. cxt.closePath();
  147. cxt.fill();
  148. if (_LinePointArr.indexOf(i) >= 0) {
  149. cxt.fillStyle = self.options.pointColor || "#777"; //圆圈中心点颜色
  150. cxt.beginPath();
  151. cxt.arc(Point.X, Point.Y, R - ((self.options.pointWidth || 16) * self.options.times), 0, Math.PI * 2, true);
  152. cxt.closePath();
  153. cxt.fill();
  154. }
  155. }
  156. },
  157. isPointSelect: function(touches, linePoint) {
  158. var self = this;
  159. for (var i = 0; i < self.pointLocationArr.length; i++) {
  160. var currentPoint = self.pointLocationArr[i];
  161. var xdiff = Math.abs(currentPoint.X - touches.elementX);
  162. var ydiff = Math.abs(currentPoint.Y - touches.elementY);
  163. var dir = Math.pow((xdiff * xdiff + ydiff * ydiff), 0.5);
  164. if (dir < self.R) {
  165. if (linePoint.indexOf(i) < 0) {
  166. linePoint.push(i);
  167. }
  168. break;
  169. }
  170. }
  171. },
  172. initEvent: function(canvas, cxt, holder) {
  173. var self = this;
  174. var linePoint = [];
  175. var isDown = false; //针对鼠标事件
  176. //start
  177. self._startHandler = function(e) {
  178. e.point = event.changedTouches ? event.changedTouches[0] : event;
  179. e.point.elementX = (e.point.pageX - getElementLeft(holder)) * self.options.times;
  180. e.point.elementY = (e.point.pageY - getElementTop(holder)) * self.options.times;
  181. self.isPointSelect(e.point, linePoint);
  182. isDown = true;
  183. };
  184. canvas.on(startEventName, self._startHandler, false);
  185. //move
  186. self._moveHanlder = function(e) {
  187. if (!isDown) return;
  188. e.preventDefault();
  189. e.point = event.changedTouches ? event.changedTouches[0] : event;
  190. e.point.elementX = (e.point.pageX - getElementLeft(holder)) * self.options.times;
  191. e.point.elementY = (e.point.pageY - getElementTop(holder)) * self.options.times;
  192. var touches = e.point;
  193. self.isPointSelect(touches, linePoint);
  194. cxt.clearRect(0, 0, self.CW, self.CH);
  195. self.draw(cxt, self.pointLocationArr, linePoint, {
  196. X: touches.elementX,
  197. Y: touches.elementY
  198. });
  199. };
  200. canvas.on(moveEventName, self._moveHanlder, false);
  201. //end
  202. self._endHandler = function(e) {
  203. e.point = event.changedTouches ? event.changedTouches[0] : event;
  204. e.point.elementX = (e.point.pageX - getElementLeft(holder)) * self.options.times;
  205. e.point.elementY = (e.point.pageY - getElementTop(holder)) * self.options.times;
  206. cxt.clearRect(0, 0, self.CW, self.CH);
  207. self.draw(cxt, self.pointLocationArr, linePoint, null);
  208. //事件数据
  209. var eventData = {
  210. sender: self,
  211. points: linePoint
  212. };
  213. /*
  214. * 回调完成事件
  215. *
  216. * 备注:
  217. * 比较理想的做法是为 Locker 的实例启用事件机制,比如 locker.on('done',handler);
  218. * 在 mui 没有完整的公共事件模块前,此版本中 locker 实例暂通过 options.callback 处理
  219. */
  220. self.options.callback(eventData);
  221. //触发声明的DOM的自定义事件(暂定 done 为事件名,可以考虑更有针对的事件名 )
  222. $.trigger(self.holder, 'done', eventData);
  223. //-
  224. linePoint = [];
  225. isDown = false;
  226. };
  227. canvas.on(endEventName, self._endHandler, false);
  228. },
  229. pointLocationArr: [],
  230. /**
  231. * 清除图形
  232. * */
  233. clear: function() {
  234. var self = this;
  235. //self.pointLocationArr = [];
  236. if (self.cxt) {
  237. self.cxt.clearRect(0, 0, self.CW, self.CH);
  238. self.draw(self.cxt, self.pointLocationArr, [], {
  239. X: 0,
  240. Y: 0
  241. });
  242. }
  243. },
  244. /**
  245. * 释放资源
  246. * */
  247. dispose: function() {
  248. var self = this;
  249. self.cxt = null;
  250. self.canvas.off(startEventName, self._startHandler);
  251. self.canvas.off(moveEventName, self._moveHandler);
  252. self.canvas.off(endEventName, self._endHandler);
  253. self.holder.innerHTML = '';
  254. self.canvas = null;
  255. }
  256. });
  257. //添加 locker 插件
  258. $.fn.locker = function(options) {
  259. //遍历选择的元素
  260. this.each(function(i, element) {
  261. if (element.locker) return;
  262. if (options) {
  263. element.locker = new Locker(element, options);
  264. } else {
  265. var optionsText = element.getAttribute('data-locker-options');
  266. var _options = optionsText ? JSON.parse(optionsText) : {};
  267. _options.lineColor = element.getAttribute('data-locker-line-color') || _options.lineColor;
  268. _options.ringColor = element.getAttribute('data-locker-ring-color') || _options.ringColor;
  269. _options.fillColor = element.getAttribute('data-locker-fill-color') || _options.fillColor;
  270. _options.pointColor = element.getAttribute('data-locker-point-color') || _options.pointColor;
  271. _options.width = element.getAttribute('data-locker-width') || _options.width;
  272. _options.height = element.getAttribute('data-locker-height') || _options.height;
  273. element.locker = new Locker(element, _options);
  274. }
  275. });
  276. return this[0] ? this[0].locker : null;
  277. };
  278. //自动处理 class='mui-locker' 的 dom
  279. try {
  280. $('.' + lockerClassName).locker();
  281. } catch (ex) {}
  282. $.ready(function() {
  283. $('.' + lockerClassName).locker();
  284. });
  285. }(mui, document));