Web前端图形滑块检验组件实现

组件渲染图形:

初始化:                                                                                               滑到时:                                 

验证成功:                                                                                                 验证失败:

 

 

适用场景:

  相信大家看了此组件图片后已经明白它的使用场景,在此不做过多介绍

 

组件内容:

  组件CSS(sliderImgPuzzle.css):

  1 .block {
  2     position: absolute;
  3     left: 0;
  4     top: 0
  5 }
  6 
  7 .canvasContainer {
  8     /*display: none;*/
  9     position: absolute;
 10     bottom: 65px;
 11     background: #fff
 12 }
 13 
 14 .sliderImgPuzzleContainer {
 15     position: relative;
 16     text-align: center;
 17     width: 325px;
 18     height: 50px;
 19     font-size: 14px;
 20     line-height: 50px;
 21     background: #f7f9fa;
 22     color: #45494c;
 23     border: 1px solid #e4e7eb
 24 }
 25 
 26 .sliderImgPuzzleContainer_active .sliderImgPuzzle {
 27     height: 48px;
 28     top: -1px;
 29     border: 1px solid #1991fa
 30 }
 31 
 32 .sliderImgPuzzleContainer_active .sliderImgPuzzleMask {
 33     height: 48px;
 34     border-width: 1px
 35 }
 36 
 37 .sliderImgPuzzleContainer_success .sliderImgPuzzle {
 38     height: 48px;
 39     top: -1px;
 40     border: 1px solid #52ccba;
 41     background-color: #52ccba !important
 42 }
 43 
 44 .sliderImgPuzzleContainer_success .sliderImgPuzzleMask {
 45     height: 48px;
 46     border: 1px solid #52ccba;
 47     background-color: #d2f4ef
 48 }
 49 
 50 .sliderImgPuzzleContainer_success .sliderImgPuzzleIcon {
 51     background-position: 0 0 !important
 52 }
 53 
 54 .sliderImgPuzzleContainer_fail .sliderImgPuzzle {
 55     height: 48px;
 56     top: -1px;
 57     border: 1px solid #f57a7a;
 58     background-color: #f57a7a !important
 59 }
 60 
 61 .sliderImgPuzzleContainer_fail .sliderImgPuzzleMask {
 62     height: 48px;
 63     border: 1px solid #f57a7a;
 64     background-color: #fce1e1
 65 }
 66 
 67 .sliderImgPuzzleContainer_fail .sliderImgPuzzleIcon {
 68     top: 19px;
 69     background-position: 0 -82px !important
 70 }
 71 
 72 .sliderImgPuzzleContainer_active .sliderImgPuzzleText, .sliderImgPuzzleContainer_success .sliderImgPuzzleText, .sliderImgPuzzleContainer_fail .sliderImgPuzzleText {
 73     visibility: hidden
 74 }
 75 
 76 .sliderImgPuzzleMask {
 77     position: absolute;
 78     left: 0;
 79     top: 0;
 80     height: 50px;
 81     border: 0 solid #1991fa;
 82     background: #d1e9fe
 83 }
 84 
 85 .sliderImgPuzzle {
 86     position: absolute;
 87     top: 0;
 88     left: 0;
 89     width: 50px;
 90     height: 48px;
 91     background: #fff;
 92     box-shadow: 0 0 3px rgba(0, 0, 0, .3);
 93     cursor: pointer;
 94     transition: background .2s linear
 95 }
 96 
 97 .sliderImgPuzzle:hover {
 98     background: #1991fa
 99 }
100 
101 .sliderImgPuzzle:hover .sliderImgPuzzleIcon {
102     background-position: 0 -13px
103 }
104 
105 .sliderImgPuzzleIcon {
106     position: absolute;
107     top: 19px;
108     left: 17px;
109     width: 14px;
110     height: 12px;
111     background: url(../images/icon.png) 0 -26px;
112     background-size: 34px 471px
113 }
114 
115 .refreshIcon {
116     position: absolute;
117     right: 0;
118     top: 0;
119     width: 34px;
120     height: 34px;
121     cursor: pointer;
122     background: url(../images/icon.png) 0 -437px;
123     background-size: 34px 471px
124 }
View Code

  组件js(sliderImgPuzzle.js) 

  1 ;(function ($) {
  2     $.fn.SliderImgPuzzle = function (setting) {
  3         var defaults = {callback: false}
  4         var setting = $.extend(defaults, setting);
  5         const l = 42, r = 10, w = 325, h = 155, PI = Math.PI
  6         const L = l + r * 2
  7 
  8         function getRandomNumberByRange(start, end) {
  9             return Math.round(Math.random() * (end - start) + start)
 10         }
 11 
 12         function createCanvas(width, height) {
 13             const canvas = createElement('canvas')
 14             canvas.width = width
 15             canvas.height = height
 16             return canvas
 17         }
 18 
 19         function createImg(onload) {
 20             const img = createElement('img')
 21             img.crossOrigin = "Anonymous"
 22             img.onload = onload
 23             img.onerror = () => {
 24                 img.src = getRandomImg()
 25             }
 26             img.src = getRandomImg()
 27             return img
 28         }
 29 
 30         function createElement(tagName) {
 31             return document.createElement(tagName)
 32         }
 33 
 34         function addClass(tag, className) {
 35             tag.classList.add(className)
 36         }
 37 
 38         function removeClass(tag, className) {
 39             tag.classList.remove(className)
 40         }
 41 
 42         function getRandomImg() {
 43             return 'https://picsum.photos/300/150/?image=' + getRandomNumberByRange(0, 200)
 44         }
 45 
 46         function draw(ctx, operation, x, y, type) {
 47             if (type == 1)
 48                 draw1(ctx, operation, x, y); else if (type == 2)
 49                 draw2(ctx, operation, x, y); else if (type == 3)
 50                 draw3(ctx, operation, x, y); else if (type == 4)
 51                 draw4(ctx, operation, x, y); else if (type == 5)
 52                 draw5(ctx, operation, x, y); else if (type == 6)
 53                 draw6(ctx, operation, x, y);
 54         }
 55 
 56         function draw6(ctx, operation, x, y) {
 57             ctx.beginPath()
 58             ctx.moveTo(x, y)
 59             ctx.lineTo(x + l, y)
 60             ctx.lineTo(x + l, y + l / 2)
 61             ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
 62             ctx.lineTo(x + l, y + l / 2)
 63             ctx.lineTo(x + l, y + l)
 64             ctx.lineTo(x + l / 2, y + l)
 65             ctx.arc(x + l / 2, y + l + 4, r, 0, 2 * PI)
 66             ctx.lineTo(x + l / 2, y + l)
 67             ctx.lineTo(x, y + l)
 68             ctx.lineTo(x, y)
 69             ctx.fillStyle = '#fff'
 70             ctx[operation]()
 71             ctx.beginPath()
 72             ctx.arc(x + l / 2, y, r, 2 * PI, 1 * PI)
 73             ctx.globalCompositeOperation = "xor"
 74             ctx.fill()
 75         }
 76 
 77         function draw5(ctx, operation, x, y) {
 78             ctx.beginPath()
 79             ctx.moveTo(x, y)
 80             ctx.lineTo(x + l / 2, y)
 81             ctx.arc(x + l / 2, y - r + 4, r, 0, 2 * PI)
 82             ctx.lineTo(x + l / 2, y)
 83             ctx.lineTo(x + l, y)
 84             ctx.lineTo(x + l, y + l / 2)
 85             ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
 86             ctx.lineTo(x + l, y + l / 2)
 87             ctx.lineTo(x + l, y + l)
 88             ctx.lineTo(x, y + l)
 89             ctx.lineTo(x, y)
 90             ctx.fillStyle = '#fff'
 91             ctx[operation]()
 92             ctx.beginPath()
 93             ctx.arc(x + l / 2, y + l, r, 1 * PI, 2 * PI)
 94             ctx.globalCompositeOperation = "xor"
 95             ctx.fill()
 96         }
 97 
 98         function draw4(ctx, operation, x, y) {
 99             ctx.beginPath()
100             ctx.moveTo(x, y)
101             ctx.lineTo(x + l / 2, y)
102             ctx.arc(x + l / 2, y - r + 4, r, 0, 2 * PI)
103             ctx.lineTo(x + l / 2, y)
104             ctx.lineTo(x + l, y)
105             ctx.lineTo(x + l, y + l / 2)
106             ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
107             ctx.lineTo(x + l, y + l / 2)
108             ctx.lineTo(x + l, y + l)
109             ctx.lineTo(x + l / 2, y + l)
110             ctx.arc(x + l / 2, y + l + 4, r, 0, 2 * PI)
111             ctx.lineTo(x + l / 2, y + l)
112             ctx.lineTo(x, y + l)
113             ctx.lineTo(x, y)
114             ctx.fillStyle = '#fff'
115             ctx[operation]()
116             ctx.beginPath()
117             ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI)
118             ctx.globalCompositeOperation = "xor"
119             ctx.fill()
120         }
121 
122         function draw3(ctx, operation, x, y) {
123             ctx.beginPath()
124             ctx.moveTo(x, y)
125             ctx.lineTo(x + l / 2, y)
126             ctx.lineTo(x + l, y)
127             ctx.lineTo(x + l, y + l / 2)
128             ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
129             ctx.lineTo(x + l, y + l / 2)
130             ctx.lineTo(x + l, y + l)
131             ctx.lineTo(x + l / 2, y + l)
132             ctx.arc(x + l / 2, y + l + 4, r, 0, 2 * PI)
133             ctx.lineTo(x + l / 2, y + l)
134             ctx.lineTo(x, y + l)
135             ctx.lineTo(x, y)
136             ctx.fillStyle = '#fff'
137             ctx[operation]()
138             ctx.beginPath()
139             ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI)
140             ctx.globalCompositeOperation = "xor"
141             ctx.fill()
142         }
143 
144         function draw2(ctx, operation, x, y) {
145             ctx.beginPath()
146             ctx.moveTo(x, y)
147             ctx.lineTo(x + l / 2, y)
148             ctx.lineTo(x + l, y)
149             ctx.lineTo(x + l, y + l / 2)
150             ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
151             ctx.lineTo(x + l, y + l / 2)
152             ctx.lineTo(x + l, y + l)
153             ctx.lineTo(x, y + l)
154             ctx.lineTo(x, y)
155             ctx.fillStyle = '#fff'
156             ctx[operation]()
157             ctx.beginPath()
158             ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI)
159             ctx.globalCompositeOperation = "xor"
160             ctx.fill()
161         }
162 
163         function draw1(ctx, operation, x, y) {
164             ctx.beginPath()
165             ctx.moveTo(x, y)
166             ctx.lineTo(x + l / 2, y)
167             ctx.arc(x + l / 2, y - r + 4, r, 0, 2 * PI)
168             ctx.lineTo(x + l / 2, y)
169             ctx.lineTo(x + l, y)
170             ctx.lineTo(x + l, y + l / 2)
171             ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
172             ctx.lineTo(x + l, y + l / 2)
173             ctx.lineTo(x + l, y + l)
174             ctx.lineTo(x, y + l)
175             ctx.lineTo(x, y)
176             ctx.fillStyle = '#fff'
177             ctx[operation]()
178             ctx.beginPath()
179             ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI)
180             ctx.globalCompositeOperation = "xor"
181             ctx.fill()
182         }
183 
184         function sum(x, y) {
185             return x + y
186         }
187 
188         function square(x) {
189             return x * x
190         }
191 
192         class sliderImgPuzzle {
193             constructor({el, onSuccess, onFail, onRefresh}) {
194                 this.el = el
195                 this.onSuccess = onSuccess
196                 this.onFail = onFail
197                 this.onRefresh = onRefresh
198                 this.isEnable = true
199             }
200 
201             init() {
202                 this.initDOM()
203                 this.initImg()
204                 this.draw()
205                 this.bindEvents()
206                 return this;
207             }
208 
209             initDOM() {
210                 const canvas = createCanvas(w, h)
211                 const block = canvas.cloneNode(true)
212                 const canvasContainer = createElement('div')
213                 const sliderImgPuzzleContainer = createElement('div')
214                 const refreshIcon = createElement('div')
215                 const sliderImgPuzzleMask = createElement('div')
216                 const sliderImgPuzzle = createElement('div')
217                 const sliderImgPuzzleIcon = createElement('span')
218                 const text = createElement('span')
219                 canvasContainer.className = 'canvasContainer'
220                 block.className = 'block'
221                 sliderImgPuzzleContainer.className = 'sliderImgPuzzleContainer'
222                 refreshIcon.className = 'refreshIcon'
223                 sliderImgPuzzleMask.className = 'sliderImgPuzzleMask'
224                 sliderImgPuzzle.className = 'sliderImgPuzzle'
225                 sliderImgPuzzleIcon.className = 'sliderImgPuzzleIcon'
226                 text.innerHTML = '向右滑动滑块填充拼图'
227                 text.className = 'sliderImgPuzzleText'
228                 const el = this.el
229                 canvasContainer.appendChild(canvas)
230                 canvasContainer.appendChild(refreshIcon)
231                 canvasContainer.appendChild(block)
232                 el.appendChild(canvasContainer)
233                 sliderImgPuzzle.appendChild(sliderImgPuzzleIcon)
234                 sliderImgPuzzleMask.appendChild(sliderImgPuzzle)
235                 sliderImgPuzzleContainer.appendChild(sliderImgPuzzleMask)
236                 sliderImgPuzzleContainer.appendChild(text)
237                 el.appendChild(sliderImgPuzzleContainer)
238                 Object.assign(this, {
239                     canvas,
240                     block,
241                     canvasContainer,
242                     sliderImgPuzzleContainer,
243                     refreshIcon,
244                     sliderImgPuzzle,
245                     sliderImgPuzzleMask,
246                     sliderImgPuzzleIcon,
247                     text,
248                     canvasCtx: canvas.getContext('2d'),
249                     blockCtx: block.getContext('2d')
250                 })
251             }
252 
253             initImg() {
254                 const img = createImg(() => {
255                     this.canvasCtx.drawImage(img, 0, 0, w, h)
256                     this.blockCtx.drawImage(img, 0, 0, w, h)
257                     const y = this.y - r * 2 + 2
258                     const ImageData = this.blockCtx.getImageData(this.x, y, L + r + 4, L + r + 4)
259                     this.block.width = L
260                     this.blockCtx.putImageData(ImageData, 0, y)
261                 })
262                 this.img = img
263             }
264 
265             draw() {
266                 this.x = getRandomNumberByRange(L + 10, w - (L + 10))
267                 this.y = getRandomNumberByRange(10 + r * 2, h - (L + 10))
268                 var num = Math.floor(Math.random() * 6 + 1);
269                 draw(this.canvasCtx, 'fill', this.x, this.y, num)
270                 draw(this.blockCtx, 'clip', this.x, this.y, num)
271             }
272 
273             clean() {
274                 this.canvasCtx.clearRect(0, 0, w, h)
275                 this.blockCtx.clearRect(0, 0, w, h)
276                 this.block.width = w
277             }
278 
279             bindEvents() {
280                 this.el.onselectstart = () => false
281                 this.refreshIcon.onclick = () => {
282                     this.reset()
283                     typeof this.onRefresh === 'function' && this.onRefresh()
284                 }
285                 let originX, originY, trail = [], isMouseDown = false
286                 this.sliderImgPuzzle.ctrl = this;
287                 this.sliderImgPuzzle.addEventListener('mousedown', function (e) {
288                     if (!this.parentElement.parentElement.classList.contains("sliderImgPuzzleContainer_success")) {
289                         originX = e.x, originY = e.y
290                         isMouseDown = true
291                     }
292                     else {
293                         isMouseDown = false
294                     }
295                     if (!this.ctrl.isEnable) isMouseDown = false;
296                 })
297                 // this.sliderImgPuzzle.addEventListener('mouseover', function (e) {
298                 //     if (this.ctrl.isEnable)
299                 //         this.parentElement.parentElement.parentElement.querySelector(".canvasContainer").style.display = "block";
300                 // })
301                 // this.canvasContainer.parentElement.addEventListener('mouseleave', function (e) {
302                 //     this.querySelector(".canvasContainer").style.display = "none";
303                 // })
304                 document.addEventListener('mousemove', (e) => {
305                     if (!isMouseDown) return false
306                     const moveX = e.x - originX
307                     const moveY = e.y - originY
308                     if (moveX < 0 || moveX + 38 >= w) return false
309                     this.sliderImgPuzzle.style.left = moveX + 'px'
310                     var blockLeft = (w - 40 - 20) / (w - 40) * moveX
311                     this.block.style.left = blockLeft + 'px'
312                     addClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_active')
313                     this.sliderImgPuzzleMask.style.width = moveX + 'px'
314                     trail.push(moveY)
315                 })
316                 document.addEventListener('mouseup', (e) => {
317                     if (!isMouseDown) return false
318                     isMouseDown = false
319                     if (e.x == originX) return false
320                     removeClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_active')
321                     this.trail = trail
322                     const {spliced, TuringTest} = this.verify()
323                     if (spliced) {
324                         if (TuringTest) {
325                             addClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_success')
326                             typeof this.onSuccess === 'function' && this.onSuccess()
327                         } else {
328                             addClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_fail')
329                             this.text.innerHTML = '再试一次'
330                             this.reset()
331                         }
332                     } else {
333                         addClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_fail')
334                         typeof this.onFail === 'function' && this.onFail()
335                         setTimeout(() => {
336                             this.reset()
337                         }, 1000)
338                     }
339                 })
340             }
341 
342             verify() {
343                 const arr = this.trail
344                 const average = arr.reduce(sum) / arr.length
345                 const deviations = arr.map(x => x - average)
346                 const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length)
347                 const left = parseInt(this.block.style.left)
348                 return {spliced: Math.abs(left - this.x) < 10, TuringTest: average !== stddev,}
349             }
350 
351             reset() {
352                 this.sliderImgPuzzleContainer.className = 'sliderImgPuzzleContainer'
353                 this.sliderImgPuzzle.style.left = 0
354                 this.block.style.left = 0
355                 this.sliderImgPuzzleMask.style.width = 0
356                 this.clean()
357                 this.img.src = getRandomImg()
358                 this.draw()
359             }
360 
361             enablePuzzle(enable) {
362                 if (enable) {
363                     this.isEnable = true;
364                 }
365                 else this.isEnable = false;
366             }
367         }
368 
369         this.each(function () {
370             var $SliderImgPuzzle = $(this);
371             setting.el = $SliderImgPuzzle[0];
372             var sip = new sliderImgPuzzle(setting).init();
373             $SliderImgPuzzle.data("LsSliderImgPuzzle", sip);
374         });
375     }
376 })(jQuery);
View Code

  组件HTML(sliderImgPuzzle.html)

 1 <!DOCTYPE html>
 2 <html lang="zh">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>滑动拼图验证码</title>
 6     <link rel="stylesheet" href="../css/sliderImgPuzzle.css">
 7     <style>
 8         .container {
 9             width: 310px;
10             margin: 300px auto;
11         }
12         input {
13             display: block;
14             width: 290px;
15             line-height: 40px;
16             margin: 10px 0;
17             padding: 0 10px;
18             outline: none;
19             border:1px solid #c8cccf;
20             border-radius: 4px;
21             color:#6a6f77;
22         }
23         #msg {
24             width: 100%;
25             line-height: 40px;
26             font-size: 14px;
27             text-align: center;
28         }
29         a:link,a:visited,a:hover,a:active {
30             margin-left: 100px;
31             color: #0366D6;
32         }
33 
34     </style>
35 </head>
36 <body>
37 <div class="container">
38     <div id="silderpuzzle" style="position: relative;padding-bottom: 15px;"></div>
39     <div id="msg"></div>
40     <button id="setenable">请先设置控件为可用</button>
41     <button id="setdisabled">设置控件不可用</button>
42     <button id="reflash">刷新控件</button>
43 </div>
44 <script src="http://www.jq22.com/jquery/jquery-2.1.1.js"></script>
45 <script src="../js/sliderImgPuzzle.js"></script>
46 <script>
47     //--------------------滑块拼图验证控件初始化-----
48     var option = {
49         onSuccess: function () {
50             document.getElementById('msg').innerHTML = '验证成功!'
51         },
52         onFail: cleanMsg,
53         onRefresh: cleanMsg
54     }
55     $("#silderpuzzle").SliderImgPuzzle(option);
56     //--------------------滑块拼图验证控件初始化完成-----
57 
58     //--------------------获取控件对象-----
59     var imgPuzzle = $("#silderpuzzle").data("LsSliderImgPuzzle");
60 
61     //--------------------回调函数
62     function cleanMsg() {
63         alert("回调函数")
64     }
65 
66     $("#setenable").on("click",function(){
67         //--------------------对象提供的enable的方法
68         imgPuzzle.enablePuzzle(true);
69     })
70     $("#setdisabled").on("click",function(){
71         //--------------------对象提供的enable的方法
72         imgPuzzle.enablePuzzle(false);
73     })
74     $("#reflash").on("click",function(){
75         //--------------------对象提供的enable的方法
76         imgPuzzle.reset();
77     })
78 </script>
79 </body>
80 </html>
View Code

 

总结:

  该组件为轻量级组件,使用简单,一看便知。支持自定义扩展,如有不会的朋友可以拿来即用,非常方便!

 

GitHub地址:

   https://github.com/TopSkyhua/SliderImgPuzzle

 

转载于:https://www.cnblogs.com/top-sky-hua/p/10612923.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值