源码代码下载
git@gitee.com:mryangjianxin/puzzle-verification-code.git
首先,我们要了解一个拼图验证码所时怎么组成的
- 一个完整的图片
- 一个在图片上的遮罩层,来区别我们需要填充的位置,和我们要移动的”块“
- 一个可以操作”块“的滑动条
- 得到一个居中的图片
DOCTYPE html>
<html lang&#61;"en"><head><meta charset&#61;"UTF-8"><meta http-equiv&#61;"X-UA-Compatible" content&#61;"IE&#61;edge"><meta name&#61;"viewport" content&#61;"width&#61;device-width, initial-scale&#61;1.0"><title>拼图验证码实现title><link rel&#61;"stylesheet" href&#61;"css/VerificationCode.css" type&#61;"text/css">
head><body><div id&#61;"captcha">div>
body><script type&#61;"text/Javascript" src&#61;"js/VerificationCode.js">script>html>
body {display: flex;justify-content: center;align-items: center;min-height: 100vh;
}#captcha {--width: 400px;--height: 260px;display: block;width: var(--width);height: var(--height);border-radius: 4px;background-image: url(https://images.unsplash.com/photo-1606787366850-de6330128bfc?ixlib&#61;rb-1.2.1&ixid&#61;MnwxMjA3fDB8MHxwzaG90by1wYWdlfHx8fGVufDB8fHx8&auto&#61;format&fit&#61;crop&w&#61;2700&q&#61;80);background-size: cover;background-position: center;position: relative;box-shadow: 0px 2px 4px rgba(0, 0, 0, .3);
}
结果如下&#xff1a;
![显示结果](https://img8.php1.cn/3cdc5/1256d/78c/a2d38ffdc5ee38ff.png?x-oss-process&#61;image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxODUzNDE3,size_16,color_FFFFFF,t_70)
- 利用 Pseudo Element 伪元素创造一个缺口
inset() 函数是一个图形函数&#xff0c;用于指定某种基本图形类型。inset()函数用于定义矩形。
calc() 此 CSS 函数允许在声明 CSS 属性值时执行一些计算。
-webkit 这种就叫做浏览器私有前缀&#xff0c;是浏览器对于新CSS属性的一个提前支持
ackground-blend-mode 属性定义每个背景层&#xff08;颜色和/或图像&#xff09;的混合模式。
#captcha {--puzzle-width: 80px;--puzzle-height: 80px;
}#captcha::before,
#captcha::after {position: absolute;content: &#39;拼图&#39;;display: block;width: inherit;height: inherit;background-image: inherit;background-size: inherit;background-position: inherit;clip-path: inset(calc((var(--height) - var(--puzzle-height)) / 2)var(--puzzle-width)calc((var(--height) - var(--puzzle-height)) / 2)calc(var(--width) - var(--puzzle-width) * 2));-webkit-clip-path: inset(calc((var(--height) - var(--puzzle-height)) / 2)var(--puzzle-width)calc((var(--height) - var(--puzzle-height)) / 2)calc(var(--width) - var(--puzzle-width) * 2));
}#captcha::after {transform: translatex(calc((var(--width)*-1)))
}#captcha::before {background-color: rgba(0, 0, 0, .6);background-blend-mode: multiply;
}
![在这里插入图片描述](https://img8.php1.cn/3cdc5/1256d/78c/07e489997907e88e.png?x-oss-process&#61;image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxODUzNDE3,size_16,color_FFFFFF,t_70)
- 实现滚动条
在实现滚动条时我们使用的是在原来的基础上在下面添块级元素&#xff0c;注意&#xff1a;我们这里不使用伪元素来实现原因是在Javascript中不能直接监听伪类元素的事件&#xff0c;是为了后面我们使用Javascript操作做准备
clamp() 函数的作用是把一个值限制在一个上限和下限之间&#xff0c;当这个值超过最小值和最大值的范围时&#xff0c;在最小值和最大值之间选择一个值使用。它接收三个参数&#xff1a;最小值、首选值、最大值。
<div id&#61;"captcha"><div id&#61;"handle"><span>span>div>div>
注意&#xff1a;由于我们使用了clamp()函数&#xff0c;限制了按钮的最大值和最小值&#xff0c;我们发现我们移动的块也是随着移动的&#xff0c;我们就可以使用同一个值来修改&#xff0c;这样就可以达到按钮和拼图的同步
#captcha {--moved: 0px;
}
#captcha::after {transform: translatex(calc((var(--width)*-1)))
}
修改为
#captcha::after {transform: translatex(clamp(calc(var(--width) * -1),calc((var(--width) * -1) &#43; var(--moved)),calc(var(--puzzle-width))));
}
#handle {width: calc(var(--width) &#43; var(--puzzle-width) * 2);height: 30px;border-radius: 18px;background-color: #eee;position: absolute;bottom: -50px;left: calc(var(--puzzle-width) * 2 * -1);box-shadow: inset 0px 0px 12px rgba(0, 0, 0, .2);border: 3px solid #ccc;
}
#handle span {display: block;width: var(--puzzle-width);height: inherit;border-radius: inherit;background-color: #fff;box-shadow: inset 0px 0px 6px rgba(0, 0, 0, .25), 0px 2px 4px rgba(0, 0, 0, .3);position: absolute;cursor: move;transform: translatex(clamp(0px,var(--moved),calc(var(--width) &#43; var(--puzzle-width))));
}
当我们修改 --moved 值时&#xff0c;就会出现以下及结果
![在这里插入图片描述](https://img8.php1.cn/3cdc5/1256d/78c/c48bb6b767f8ee98.png?x-oss-process&#61;image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxODUzNDE3,size_16,color_FFFFFF,t_70)
- 加入Javascript逻辑&#xff0c;更改–moved 的值
const captcha &#61; document.querySelector(&#39;#captcha&#39;)
const handle &#61; document.querySelector(&#39;#handle&#39;)
const button &#61; document.querySelector(&#39;#handle span&#39;)
button.addEventListener(&#39;mousedown&#39;, (e) &#61;> {、
}
window.addEventListener(&#39;mousemove&#39;, (e) &#61;> {}
window.addEventListener(&#39;mouseup&#39;, (e) &#61;> {}
在这里我们需要定义一个flag (shouldMove)&#xff0c;来判断他是否在点击状态
Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。
setProperty() 方法用于设置一个新的 CSS 属性&#xff0c;同时也可以修改 CSS 声明块中已存在的属性。
let shouldMove &#61; falsebutton.addEventListener(&#39;mousedown&#39;, (e) &#61;> {shouldMove &#61; true
})window.addEventListener(&#39;mouseup&#39;, (e) &#61;> {if (shouldMove) {}
}
我们最终的Javascript代码
let shouldMove &#61; false
const captcha &#61; document.querySelector(&#39;#captcha&#39;)
const handle &#61; document.querySelector(&#39;#handle&#39;)
const button &#61; document.querySelector(&#39;#handle span&#39;)
button.addEventListener(&#39;mousedown&#39;, (e) &#61;> {shouldMove &#61; true
})
window.addEventListener(&#39;mousemove&#39;, (e) &#61;> {if (shouldMove) {const offsetLeft &#61; handle.getBoundingClientRect().leftconst buttonWidth &#61; button.getBoundingClientRect().widthcaptcha.style.setProperty(&#39;--moved&#39;, &#96;${e.clientX - offsetLeft - buttonWidth / 2}px&#96;)}
})
window.addEventListener(&#39;mouseup&#39;, (e) &#61;> {if (shouldMove) {const finalOffset &#61; e.clientX - handle.getBoundingClientRect().leftif (finalOffset >&#61; 430 && finalOffset <&#61; 450) {captcha.classList.add(&#39;passed&#39;)} else {captcha.style.setProperty(&#39;--moved&#39;, &#39;0px&#39;)}shouldMove &#61; false}
})
到我们拼图满足条件时&#xff0c;我们就要将拼图 “拼接” 在图片上&#xff0c;也就是让遮罩层的透明度改为0
#captcha.passed::before,
#captcha.passed::after,
#captcha.passed #handle {opacity: 0;
}
- 优化&#xff0c;加入回弹效果
但是我们发现&#xff0c;按钮和拼图在移动和拼接时都是非产地个生硬
我们在为 #handle span 设置 transition 时&#xff0c;发现不会套用在拖到过程中
#handle span {transition: .25s all ease-in-out;
}
所以我们可以通过active &#xff0c;即在点下去时设定状态为transition为none
#captcha:active #handle span {transition: none;
}
同时也设定到#captcha::after中
#captcha::after{transition: .25s all ease-in-out;
}
#captcha:active::after {transition: none;
}
参考文献
快速访问视频
快速访问文章