大家好,我是 Steven。
这一期我们会做一个拖拉形式的拼图验证码,是近年很常见的一种验证码方式,有别于传统的猜文字的那种:
那我们就开始吧。
这个教程的视频版本在 www.bilibili.com/video/BV1NM… ,欢迎三连关注!
基础架构
打开 CodePen 编辑器,在 HTML 的部份加入一个 <div>
,id
是 captcha
。
来到 CSS 的部份,加入 body
选择器,使用 Flexbox 的方式将内容上下左右居中。加入 #captcha
选择器,display
设定为 block
,宽度设定为 400px
,高度设定为 260px
,由于稍后会用到这个宽度和高度进行运算,所以将它们分拆,写成 CSS 变量。
加入少许圆角,以及设置背景图片。背景图片就是拼图的图片,在 Unsplash 的网站上,找到这张挺适合的图片:
然后复制它的连结,套用到 background-image
上。为了让它填满这个容器,background-size
设定为 cover
,background-position
设定为中间 center
,然后 position
设定为 relative
,再加入一点阴影,就可以了。
两块拼图
接下来要制作两块拼图,一块在外面,一块在里面。
里面的那块是拼图的缺口的提示,既然刚好是两块,我就直接用 Pseudo Element (伪元素)制作。
加入 #captcha::before
和 #captcha::after
选择器,position
设定为 absolute
,要将伪元素显示出来,必须要设置 content
属性,设定为空白字串。display
设定为 block
,接下来的几项设定,都是根据父元素去设定,包括宽度、高度、背景图片、background-size
以及 background-position
。
因为背景图片是一样的,所以看不出有任何分别,暂时将背景图片的设定移除,将背景颜色设定为红色,就可以看到伪元素的大小和位置了。
接下来就要设置拼图方块的大小,回到 #captcha
那里,设定两个 CSS 变量,分别是 --puzzle-width
和 --puzzle-height
,都设定为 80px
。
然后通过 clip-path
属性的 inset()
,设定按上右下左向内缩小的方式制作遮罩,这里先设定 -webkit
的版本。
第一个设定值是由上向内方向缩小,设定为 calc()
,var(--height)
减去 var(--puzzle-height)
再除以 2
,会得出 90px
。
第二个设定值,是从右向内方向缩小,设定为一个拼图方块的大小就好了,var(--puzzle-width)
。
第三个设定值,是从下向内方向缩小,这个设定与第一个设定值相同,复制贴上一下。
第四个设定值,是从左向内方向缩小,设定为 calc()
,var(--width)
减去 var(--puzzle-width)
乘以 2
,得出 230px
。
现在看到红色方块的位置,就是 80px
乘以 80px
的正方形。复制一下 -webkit
的设定,同时套用到 clip-path
。
然后将其中一个方块向左移出来,加入 #captcha::after
选择器,设定 transform: translatex(-300px)
。将红色的方块改回背景图片,这个拼图方块就被移出来了,将它改为 calc(var(--width) * -1)
更加精确的将它对齐。
然后里面的那个方块怎样设置呢?加入 #captcha::before
选择器,设定背景颜色为 60%
透明度的黑色,然后设置 background-blend-mode
为 multiply
,就可以将半透明的黑色,与背景图片混合到一起。
拉动条
这个部份制作完成了,接下来制作下方的拉动条,在 HTML 的 #captcha
内加入一个 <div>
,id
名为 handle
,再在里面加入一个 span
,用于制作里面的按钮。不用伪类元素制作按钮的原因是,在 JavaScript 中不能直接监听伪类元素的事件。
在 CSS 的部份加入 #handle
选择器,宽度设定为 calc()
,var(--width)
,加上 var(--puzzle-width)
乘以 2
,高度设定为 30px
,圆角设定为 15px
。
背景颜色设定为浅灰色,position
设定为 absolute
,移到下方,bottom
设定为 -50px
,再向左移动,left
设定为 calc()
,var(--puzzle-width) * 2 * -1
,加入一点向内的阴影,制作一点立体感,再加一个浅灰色的框线。由于加了框线,高度不同了,所以将圆角更改为 18px
。
然后处理拖拉的按钮,加入 #handle span
选择器,display
设定为 block
,宽度设定为与拼图一样。高度和圆角的设定都继承自父元素,背景颜色设定为白色,再分别加入向内和向外的阴影,造出立体的效果。
position
设定为 absolute
,以及改一改游标的样式,设定为 move
。
然后要处理它水平拖拉的位移,先加入一个 CSS 变量,名为 --moved
,赋值为 0px
;或者先赋值为 200px
,这样方便查看效果:
通过 transform: translatex()
移动它。而这里需要有个最小和最大值,不然就会超出这条杆。加入 clamp()
函式,第一个是最小值,设定为 0px
,第二个是 --moved
的值,而第三个是最大值,设定为宽度加拼图的宽度。
回到 --moved
那里,试试改为极端的数字,例如 1000px
就会移到最右,而 -1000px
就会移到最左。先将它暂时改为 400px
,方便我们处理拼图的位移。
去到 #captcha::after
的选择器,改为 clamp()
,最小值设定为宽度乘以 -1
,移动的值设定为宽度乘以 -1
,再加 --moved
。而最大值,就等于拼图的宽度,现在试一试更改 --moved
的值,看看效果,大致上就是这样了。
好了,现在我们知道只要调整 --moved
的值,就可以同步移动拖拉按钮以及拼图。所以下一步只需要加入 JavaScript 的逻辑,更改 --moved
的值就可以了。
JavaScript 逻辑的部分
去到 JavaScript 的部份,先定义几个常量,获取会用到的选择器,分别有 captcha
、handle
以及 button
。
然后加入三个事件监听器,分别是:
mousedown
,点击下去的时候;mousemove
移动的时候;mouseup
放手的时候。
这里也要定义一个 flag
,叫做 shouldMove
,用来判断是否在点击的状态下。
然后在 mousedown
的时候,将它设定为 true
。
去到 mousemove
里面,如果 shouldMove
是 true
的话,定义常量 offsetLeft
,获取拉动杆与画面左边的位移。再定义常量 buttonWidth
,获取按钮的宽度。以上两个常量配合游标的位置,就可以计算出正确的 --moved
的结果,通过 setProperty()
,更改 #captcha
下 --moved
的值,即是 CSS 中的这个变量。
设定为 e.clientX
,即是游标的 X 位置,减去 offsetLeft
,再减去 buttonWidth
除 2
,游标的 X 位置,减移动杆与左边的距离,再减按钮宽度除 2
。除 2
的原因是中心点设定到按钮的中间位置。
然后在 mouseup
那里,将 shouldMove
设定为 false
。
移动的效果实现到了,但发现有一个问题,就是在拖拉的过程中,如果游标不是在按钮上面,就会拖不动,原因是已经离开了 button
本身,事件监听器就监听不到了。所以将 mousemove
和 mouseup
的监听,从 button
改为 window
,就可以了。
最后,处理成功拼图的逻辑。
判断当 shouldMove
是 true
的时候,将 shouldMove
等于 false
移到里面。然后定义常量 finalOffset
获取拖动的距离,将 e.clientX
减去拖拉杆向左的位移,通过 console.log()
输出一下。
这种验证码通常会允许少许的误差,所以我们就将 430
至 450
作为可接受的范围,如果不在这个范围的话,将 --moved
设定为 0px
,让按钮回到原来的位置;如果成功的话,就加一个 class
名为 passed
到 #captcha
上,我们再到 CSS 那边处理样式的改变。
回到 CSS 的部份,当 #captcha
是 passed
的时候,将 ::before
、::after
和里面的 #handle
的透明度设定为 0
。
然后我想在拼图失败的时候,加入一个回弹的效果。去到 #handle span
里面,加入 transition
的设定:
虽然可以做到回弹的效果,不过这个 transition
的设定,并不想套用在拖动的过程中,所以我们可以通过 #captcha:active
,即是点下去的状态,设定 #handle span
的 transition
为 none
。
同样的设定,套用到 #captcha::after
选择器里面。
我们来看看这个案例的完成效果
以上,就是这一集要介绍的全部内容。
这个案例的源代码在 codepen.io/stevenlei/p…
你的支持是我的动力,请关注 CodingStartup 起码课,我们一起加油!
- B站: space.bilibili.com/451368848
- YouTube: youtube.com/codingstart…
- 掘金: juejin.cn/user/249773…