MENU
前言版本一(html+JS+css)版本二(html+JS+css+canvas)
前言
1、版本一的样式比较齐全;
2、版本二的JS逻辑和功能效果比较完善,且是别人的代码,后续会对样式进行完善。[Gitee | 哔哩哔哩];
3、两个版本各有千秋,主要学习里面的一些技巧,这里主要介绍版本一的样式技巧;
4、行为验证码一般是后端实现,而且大概率是使用第三方插件,因为涉及到的逻辑和内容比较多,前后端实现起来都比较麻烦。
版本一(html+JS+css)
案例公共样式
body { margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh;}
案例一(clip-path裁剪的应用)
<style> .clip_path { width: 300px; height: 300px; background-color: #ff0000; } .clip_path:first-child { float: left; background-color: #0000ff; clip-path: inset(50px 20px 30px 40px round 10px); }</style><div> <div class="clip_path"></div> <div class="clip_path"></div></div>
案例一解析
代码块中包含两个<div>元素,每个元素都有类名为clip_path。此外,还有一些CSS样式被应用于这些元素。
CSS样式部分
1、clip_path类指定了一个固定的宽度(300像素)、高度(300像素)和背景颜色(红色)的区域。
2、clip_path:first-child选择器用于选择第一个具有类名clip_path的元素,并对其应用特定样式。
2.1、float: left;
将第一个元素左浮动,使其在其右侧的元素之前显示。
2.2、background-color: #0000ff;
将第一个元素的背景颜色更改为蓝色。
2.3、clip-path: inset(50px 20px 30px 40px round 10px);
将剪切路径应用于第一个元素,剪切路径的形状是一个矩形,内边距为50px 20px 30px 40px,并且角是圆形的,半径为10px。
HTML部分
两个<div>元素被包含在一个外部<div>中。
每个<div>元素都有类名为clip_path,因此它们都受到了相同的CSS样式的影响。
综上所述,代码将显示两个相同大小的红色方块,但第一个方块的背景颜色为蓝色,并具有一种剪切路径,使其内部形成一个圆角矩形区域。
案例一效果图
案例二(clamp最大值最小值的应用)
<style>.clamp {width: clamp(300px, 50vw, 600px);height: 300px;background-color: #ff0000;}</style><div class="clamp"></div>
案例二解析
一段简单的HTML和CSS代码段,用于创建一个具有限制宽度的元素。
1、<style>标签用于在HTML文档中定义CSS样式。
2、.clamp是一个CSS类选择器,它用于选择具有类名为"clamp"的HTML元素。
3、width: clamp(300px, 50vw, 600px);
是一个CSS属性,它使用clamp()函数来设置元素的宽度。clamp()函数接受三个参数,分别是最小宽度、首选宽度和最大宽度。在这个例子中,元素的宽度将被限制在300像素、视窗宽度的50%和600像素之间,以保证在不同设备和屏幕尺寸下有不同的大小。
4、height: 300px;
设置元素的高度为300像素。
5、background-color: #ff0000;
设置元素的背景颜色为红色。
<div class=“clamp”></div>是一个HTML<div>元素,它具有一个类名为"clamp",因此应用了上述定义的CSS样式。这个<div>元素将具有限制宽度的特性,宽度将根据视窗宽度和定义的最小和最大宽度进行调整,以便在不同设备和屏幕尺寸下呈现出不同的大小。
案例二效果图
功能效果图
html
<div id="captcha"> <div id="handle"> <span onmousedown="onmousedownFn()" mousemove="mousemoveFn()"></span> </div></div>
JavaScript
const captcha = document.querySelector('#captcha');const handle = document.querySelector('#handle');const button = document.querySelector('#handle span');const oLeft = handle.getBoundingClientRect().left;const buttonWidth = button.getBoundingClientRect().width;let flag = false;function onmousedownFn() { flag = true;}window.addEventListener('mousemove', ({ clientX }) => { if (flag) { captcha.style.setProperty('--moved', `${clientX - oLeft - buttonWidth / 2}px`); }});window.addEventListener('mouseup', ({ clientX }) => { if (flag) { const dis = clientX - oLeft; if (dis >= 430 && dis <= 450) { captcha.classList.add('passed'); alert('验证通过!'); } else { captcha.style.setProperty('--moved', '0px'); } flag = false; }});
style
* { margin: 0; padding: 0;}body { --width: 400px; --height: 260px; --puzzle-width: 80px; --puzzle-height: 80px; --moved: 0px; background-color: #008b8b; width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center;}#captcha { width: var(--width); height: var(--height); background-image: url('https://cn.bing.com/th?id=OHR.AlmondBloom_ZH-CN9441550492_1920x1080.jpg'); background-size: cover; background-position: center; position: relative; border-radius: 4px; box-shadow: 0px 2px 4px rgba(0, 0, 0, .3);}#captcha::before,#captcha::after { display: flex; position: absolute; content: ''; 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));}#captcha::after { transform: translateX(clamp( calc(var(--width) * -1), calc(var(--width) * -1 + var(--moved)), var(--puzzle-width))); transition: .25s all ease-in-out; cursor: pointer;}#captcha:active::after { transition: none;}#captcha::before { background-color: rgba(0, 0, 0, .6); background-blend-mode: multiply;}#handle { height: 30px; width: calc(var(--width) + var(--puzzle-width) * 2); border-radius: 18px; background-color: #eeeeee; box-shadow: inset 0 0 12px rgba(0, 0, 0, .2); border: 3px solid #eeeeee; position: absolute; bottom: -50px; left: calc(var(--puzzle-width) * 2 * -1);}#handle span { width: var(--puzzle-width); height: inherit; display: block; border-radius: inherit; background-color: #fff; box-shadow: 0 0 6px rgba(0, 0, 0, .25), 0 2px 4px rgba(0, 0, 0, .3); cursor: move; transform: translateX( clamp( 0px, var(--moved), calc(var(--width) + var(--puzzle-width)))); cursor: pointer; transition: .25s all ease-in-out;}#captcha:active #handle span { transition: none;}#captcha.passed::before,#captcha.passed::after,#captcha.passed #handle { opacity: 0;}
版本二(html+JS+css+canvas)
效果图
html
<div class="container"> <div id="captcha" style="position: relative;"></div> <div id="msg"></div></div><script src="./indexV2.js"></script><script> captcha.init(document.getElementById('captcha'), function() { document.getElementById('msg').innerHTML = '验证成功'; setTimeout(() => { document.getElementById('msg').innerHTML = ''; }, 2000); }, function() { document.getElementById('msg').innerHTML = '验证失败'; setTimeout(() => { document.getElementById('msg').innerHTML = ''; }, 1000); })</script>
JavaScript
(function(win) { // 滑块边长 let l = 42, // 滑块半径 r = 10, // canvas宽度 w = 310, //canvas高度 h = 155, PI = Math.PI; // 滑块的实际边长 const ll = l + r * 2; // 获取指定区间内的随机数 function getRandomNumberByRange(start, end) { return Math.round(Math.random() * (end - start) + start); } // 创建元素 function createElement(tagName) { return document.createElement(tagName); } // 创建画布 function createCanvas(width, height) { const canvas = createElement('canvas'); canvas.width = width; canvas.height = height; return canvas; } // 获取随机图片 function getRandomImg() { // 这个网站可以生成随机图片 return 'https://picsum.photos/300/150/?image=' + getRandomNumberByRange(0, 100); } // 创建图片 function createImg(onload) { const img = createElement('img'); img.crossOrigin = 'Anonymous'; img.onload = onload; img.onerror = () => { img.src = getRandomImg(); } img.src = getRandomImg(); return img; } // 添加样式 function addClass(tag, className) { tag.classList.add(className); } // 移除样式 function removeClass(tag, className) { tag.classList.remove(className); } // 绘制 function draw(ctx, operation, x, y) { ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + l / 2, y); ctx.arc(x + l / 2, y - r + 2, r, 0, 2 * PI); ctx.lineTo(x + l / 2, y); ctx.lineTo(x + l, y); ctx.lineTo(x + l, y + l / 2); ctx.arc(x + l + r - 2, y + l / 2, r, 0, 2 * PI); ctx.lineTo(x + l, y + l / 2); ctx.lineTo(x + l, y + l); ctx.lineTo(x, y + l); ctx.lineTo(x, y); ctx.fillStyle = '#fff'; ctx[operation](); ctx.beginPath(); ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI); ctx.globalCompositeOperation = 'xor'; ctx.fill(); } // 求和 function sum(x, y) { return x + y; } // 求平方 function square(x) { return x * x; } // 验证码类 class captcha { // 构造器 constructor(el, success, fail) { this.el = el; this.success = success; this.fail = fail; } // 初始化 init() { this.initDOM(); this.initImg(); this.draw(); this.bindEvents(); } // 初始化DOM initDOM() { const canvas = createCanvas(w, h), block = canvas.cloneNode(true), sliderContainer = createElement('div'), sliderMask = createElement('div'), slider = createElement('div'), refreshIcon = createElement('div'), sliderIcon = createElement('span'), text = createElement('span'); block.className = 'block'; sliderContainer.className = 'slider-container'; sliderMask.className = 'slider-mask'; slider.className = 'slider'; refreshIcon.className = 'refresh-icon'; sliderIcon.className = 'slider-icon'; text.className = 'slider-text'; text.innerHTML = '向右滑动滑块填充拼图'; const el = this.el; el.appendChild(canvas); el.appendChild(refreshIcon); el.appendChild(block); slider.appendChild(sliderIcon); sliderMask.appendChild(slider); sliderContainer.appendChild(sliderMask); sliderContainer.appendChild(text); el.appendChild(sliderContainer); Object.assign(this, { canvas, block, sliderContainer, refreshIcon, slider, sliderMask, sliderIcon, text, canvasCtx: canvas.getContext('2d'), blockCtx: block.getContext('2d') }); } // 初始化图像 initImg() { const img = createImg(() => { this.canvasCtx.drawImage(img, 0, 0, w, h); this.blockCtx.drawImage(img, 0, 0, w, h); const y = this.y - r * 2 + 2; const imageData = this.blockCtx.getImageData(this.x, y, ll, ll); this.block.width = ll; this.blockCtx.putImageData(imageData, 0, y); }); this.img = img; } // 绘画 draw() { this.x = getRandomNumberByRange(ll + 10, w - (ll + 10)); this.y = getRandomNumberByRange(10 + r * 2, h - (ll + 10)); draw(this.canvasCtx, 'fill', this.x, this.y); draw(this.blockCtx, 'clip', this.x, this.y); } // 清除 clean() { this.canvasCtx.clearRect(0, 0, w, h); this.blockCtx.clearRect(0, 0, w, h); this.block.width = w; } // 绑定事件 bindEvents() { this.el.onselectstart = () => false; this.refreshIcon.onclick = () => { this.reset(); } let originX, originY, trail = [], isMouseDown = false; this.slider.addEventListener('mousedown', function(e) { originX = e.x; originY = e.y; isMouseDown = true; }); document.addEventListener('mousemove', (e) => { if (!isMouseDown) return false; const moveX = e.x - originX; const moveY = e.y - originY; if (moveX < 0 || moveX + 38 >= w) return false; this.slider.style.left = moveX + 'px'; var blockLeft = (w - 40 - 20) / (w - 40) * moveX; this.block.style.left = blockLeft + 'px'; addClass(this.sliderContainer, 'slider-container-active'); this.sliderMask.style.width = moveX + 'px'; trail.push(moveY); }); document.addEventListener('mouseup', (e) => { if (!isMouseDown) return false; isMouseDown = false; if (e.x == originX) return false; removeClass(this.sliderContainer, 'slider-container-active'); this.trail = trail; const spliced = this.verify(); if (spliced) { addClass(this.sliderContainer, 'slider-container-success'); this.success && this.success(); } else { addClass(this.sliderContainer, 'slider-container-fail'); this.fail && this.fail(); setTimeout(() => { this.reset(); }, 1000); } }); } // 重置 reset() { this.sliderContainer.className = 'slider-container'; this.slider.style.left = 0; this.block.style.left = 0; this.sliderMask.style.width = 0; this.clean(); this.img.src = getRandomImg(); this.draw(); } // 验证 verify() { const left = parseInt(this.block.style.left); //10表示容错率,值越小,需要拼得越精确 return Math.abs(left - this.x) < 10; } } win.captcha = { init: function(element, success, fail) { new captcha(element, success, fail).init(); } }}(window));
style
* { margin: 0; padding: 0; background-color: #333333;}body { /* 方便演示,满屏居中 */ height: 100vh; display: flex; justify-content: center; align-items: center;}/* 小拼图 */.block { position: absolute; left: 0; top: 0;}/* 滑动条 */.slider-container { position: relative; text-align: center; width: 310px; height: 40px; line-height: 40px; margin-top: 15px; background-color: #f7f9fa; color: #45454c; border: 1px solid #e4e7eb;}.slider-mask { position: absolute; left: 0; top: 0; height: 40px; border: 0px solid #1991fa; background-color: #d1e9fe;}.slider { position: absolute; left: 0; top: 0; width: 40px; height: 40px; background: #fff; box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); cursor: pointer; transition: background 0.2s linear;}.slider-icon { position: absolute; left: 13px; top: 15px; width: 14px; height: 10px; background: url(/images/icon_light.png) 0 -26px; background-size: 34px 471px;}/* 滑动条活动态 */.slider-container-active .slider { height: 38px; top: -1px; border: 1px solid #1991fa;}.slider-container-active .slider-mask { height: 38px; border-width: 1px;}/* 滑动条成功态 */.slider-container-success .slider { height: 38px; top: -1px; border: 1px solid #52ccba; background-color: #52ccba !important;}.slider-container-success .slider-mask { height: 38px; border: 1px solid #52ccba; background-color: #d2f4ef;}/* 成功图标 */.slider-container-success .slider-icon { background-position: 0 0 !important;}/* 滑动条失败态 */.slider-container-fail .slider { height: 38px; top: -1px; border: 1px solid #f57a7a; background-color: #f57a7a !important;}.slider-container-fail .slider-mask { height: 38px; border: 1px solid #f57a7a; background-color: #fce1e1;}/* 失败图标 */.slider-container-fail .slider-icon { background-position: 0 -83px !important;}.slider-container-active .slider-text,.slider-container-success .slider-text,.slider-container-fail .slider-text { display: none;}.slider:hover { background: #1991fa;}.slider:hover .slider-icon { background-position: 0 -13px;}.refresh-icon { position: absolute; right: 0; top: 0; width: 34px; height: 34px; background: url(/images/icon_light.png) 0 -437px; background-size: 34px 471px; cursor: pointer;}#msg { height: 20px; line-height: 20px; text-align: center; margin-top: 15px;}