手写一个登录时的滑块验证码

1,613 阅读7分钟

手写一个登录时的滑块验证码

因为最近一直在写各种页面,之前写了一个登录注册的页面实现一个好看通用且响应式布局,自适应的登录/注册页面 - 掘金 (juejin.cn),那么登录注册的时候可能会弹出来一个滑块验证码,同时有时候系统会验证是否是人机操作也会设置一个滑块验证码来区分是否是真人和人机。

首先这里是一个ui库里的好看的提交按钮,因为之前登录注册页的时候,可能按钮不太美观,于是这里我就用一个按钮假装是一个登录或者注册页面。

滑块验证码

先上效果,我们先看到的是一个比较好看的按钮,然后滑块验证可能只是最基础的外观。当我们点击这个按钮时,会弹出一个滑块验证码区,我们取消滑动它,然后下面会给出文字告诉我们验证是否成功。

按钮

按钮有一个alt的属性,我们看到的字就是以alt的值显示的。我把按钮里原本的字给隐藏起来了,然后置顶按钮的时候,字会逐步的慢慢出现,移走的时候,字会有个动画的消失。 我这里依旧使用了css变量控制延迟时间的不同与之前的文章的个人主页的导航栏类似手写一个类似博客的个人主页 css动画效果多 - 掘金 (juejin.cn)

<!-- 按钮 -->
  <button  alt="Submit">
    <i style="--i:1">S</i>
    <i style="--i:2">u</i>
    <i style="--i:3">b</i>
    <i style="--i:4">m</i>
    <i style="--i:5">i</i>
    <i style="--i:6">t</i>
  </button>
  
  button {
  /* 将按钮设置为弹性布局并垂直和水平居中对齐 */
  display: flex;
  align-items: center;
  justify-content: center;
  /* 将按钮的高度设置为 50 像素 */
  height: 50px;
  /* 将按钮的定位方式设置为相对定位 */
  position: relative;
  /* 将按钮的左右内边距设置为 20 像素 */
  padding: 0 20px;
  /* 将按钮的字体大小设置为 18 像素,并将字母转换为大写 */
  font-size: 18px;
  text-transform: uppercase;
  /* 将按钮的边框和阴影设置为无 */
  border: 0;
  box-shadow: hsl(210deg 87% 36%) 0px 7px 0px 0px;
  /* 将按钮的背景颜色设置为蓝色 */
  background-color: hsl(210deg 100% 44%);
  /* 将按钮的圆角半径设置为 12 像素 */
  border-radius: 12px;
  /* 将按钮的溢出内容隐藏 */
  overflow: hidden;
  /* 将按钮的过渡效果时间设置为 31 毫秒,并使用贝塞尔曲线作为过渡函数 */
  transition: 31ms cubic-bezier(.5, .7, .4, 1);
}

button:before {
  /* 将按钮的 alt 属性的值作为伪元素的内容 */
  content: attr(alt);
  /* 将伪元素设置为弹性布局并垂直和水平居中对齐 */
  display: flex;
  align-items: center;
  justify-content: center;
  /* 将伪元素的定位方式设置为绝对定位 */
  position: absolute;
  /* 将伪元素的位置设置为距离父元素的上下左右边距都为 0 */
  inset: 0;
  /* 将伪元素的字体大小设置为 15 像素 */
  font-size: 15px;
  /* 将伪元素的字体加粗 */
  font-weight: bold;
  /* 将伪元素的字体颜色设置为白色 */
  color: white;
  /* 将伪元素的字母间距设置为 4 像素 */
  letter-spacing: 4px;
  /* 将伪元素的不透明度设置为 1,即完全不透明 */
  opacity: 1;
}

button:active {
  /* 将按钮的阴影和过渡效果时间设置为无 */
  box-shadow: none;
  transition: none;
  /* 将按钮向下移动 7 像素 */
  transform: translateY(7px);
  /* 将按钮的过渡效果时间设置为 35 毫秒,并使用贝塞尔曲线作为过渡函数 */
  transition: 35ms cubic-bezier(.5, .7, .4, 1);
}

button:hover:before {
  /* 将所有属性的过渡效果时间设置为 0 秒,即没有过渡效果 */
  transition: all .0s;
  /* 将伪元素向下移动 100% 的距离,即移出按钮的可见区域 */
  transform: translateY(100%);
  /* 将伪元素的不透明度设置为 0,即完全透明 */
  opacity: 0;
}

button i {
  /* 将图标的颜色、字体大小、字体加粗、字母间距、字体样式、过渡效果时间、向上移动的距离和不透明度设置 */
  color: white;
  font-size: 15px;
  font-weight: bold;
  letter-spacing: 4px;
  font-style: normal;
  transition: all 2s ease;
  transform: translateY(-20px);
  opacity: 0;
}

button:hover i {
  /* 将图标的过渡效果时间设置为 0.3 秒,并使用贝塞尔曲线作为过渡函数 */
  transition: all .3s ease;
  /* 将图标向上移动 0 像素 */
  transform: translateY(0px);
  /* 将图标的过渡延迟时间设置为 0.05 秒乘以变量 i 的值 */
  transition-delay: calc(0.05s * var(--i));
  /* 将图标的不透明度设置为 1,即完全不透明 */
  opacity: 1;
}

滑块区域

设置了一个滑块区域,然后里面有一个圆球,和一个验证的框框代表验证区域。当红球移动到绿色的区域里,代表验证成功,否则就是失败。

<!-- 滑块验证弹窗 -->
  <div class="overlay">
    <div class="container">
      <div class="slider"></div>
      <div class="right"></div>
    </div>
    <div class="text">请按住滑块,拖动到最右边<span>完成验证</span></div>
      <div class="result"></div>
  </div>
  
  /* 遮罩层 */
.overlay {
  position: fixed; /* 固定定位 */
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5); /* 半透明黑色背景 */
  z-index: 999; /* 设置层级 */
  display: none; /* 默认隐藏 */
}

/* 滑块验证器容器 */
.container {
  width: 360px; /* 宽度 */
  margin: 50px auto; /* 居中对齐 */
  height: 50px; /* 高度 */
  background-color: #f5f5f5; /* 背景色 */
  border: 1px solid #ccc; /* 边框 */
  border-radius: 50px; /* 圆角 */
  overflow: hidden; /* 隐藏滑块外部部分 */
}

/* 滑块 */
.slider {
  z-index: 999; /* 设置层级 */
  width: 48px; /* 宽度 */
  height: 48px; /* 高度 */
  background-color: #f10215; /* 背景色 */
  border-radius: 50%; /* 圆形 */
  position: relative; /* 相对定位 */
  cursor: pointer; /* 鼠标指针样式 */
  transition: transform 0.3s ease-out; /* 过渡动画 */
}

/* 滑块验证成功时的样式 */
.slider.success {
  background-color: #4caf50; /* 背景色 */
}

/* 滑块验证失败时的样式 */
.slider.error {
  background-color: #f44336; /* 背景色 */
}

/* 滑块右侧的提示文字 */
.right {
  margin-top: -50px; /* 上移 */
  margin-left: 290px; /* 左移 */
  width: 70px; /* 宽度 */
  height: 50px; /* 高度 */
  background-color: transparent; /* 背景透明 */
  border: 5px solid greenyellow; /* 边框 */
  border-radius: 30px; /* 圆角 */
  position: absolute; /* 绝对定位 */
}

/* 提示文字样式 */
.text {
  font-size: 20px; /* 字体大小 */
  padding: 20px; /* 内边距 */
  text-align: center; /* 居中对齐 */
}

/* 提示文字中的关键字样式 */
.text span {
  font-weight: bold; /* 字体加粗 */
  color: #f10215; /* 颜色 */
}

/* 验证结果提示框样式 */
.result {
  font-size: 20px; /* 字体大小 */
  padding: 20px; /* 内边距 */
  text-align: center; /* 居中对齐 */
  display: none; /* 默认隐藏 */
}

/* 验证结果提示框验证成功时的样式 */
.result.success {
  color: #4caf50; /* 颜色 */
}

/* 验证结果提示框验证失败时的样式 */
.result.error {
  color: #f44336; /* 颜色 */
}

滑块实现的主要原理

三个监听事件,和一个封装的判断成功与否的函数。注意的是我们mouseup的监听必须要是window,因为不这样的话,当我们鼠标滑出滑块的区域,小球就在停在那里,不会做任何事情。

// 获取触发滑块验证器的按钮元素
var button = document.querySelector('button');

// 获取遮罩层元素和滑块元素
var overlay = document.querySelector('.overlay');
var slider = document.querySelector('.slider');

// 标记是否正在拖动滑块,以及拖动开始时的鼠标横坐标和当前滑块的横坐标
var isDragging = false;
var startX = 0;
var currentX = 0;

// 给触发滑块验证器的按钮添加点击事件,点击时显示遮罩层
button.addEventListener('click', function () {
  overlay.style.display = 'block';
});

// 给滑块添加鼠标按下事件,按下时开始拖动滑块
slider.addEventListener('mousedown', function (e) {
  // 标记正在拖动滑块
  isDragging = true;
  // 记录拖动开始时的鼠标横坐标
  startX = e.clientX;
});

// 给滑块添加鼠标移动事件,移动时根据鼠标位置更新滑块的位置
slider.addEventListener('mousemove', function (e) {
  if (isDragging) {
    // 计算当前滑块的横坐标
    currentX = e.clientX - startX;
    // 限制滑块的横坐标在 0 到 300 之间
    if (currentX < 0) {
      currentX = 0;
    } else if (currentX > 300) {
      currentX = 300;
    }
    // 更新滑块的位置
    slider.style.left = currentX + 'px';
  }
});

// 给整个文档添加鼠标松开事件,松开时判断滑块是否拖动到了指定位置,如果是则显示验证通过的提示信息,否则显示验证失败的提示信息
document.addEventListener('mouseup', function (e) {
  if (isDragging) {
    if (currentX >= 300) {
      // 显示验证通过的提示信息
      showResult(true);
    } else {
      // 显示验证失败的提示信息,并重置滑块的位置
      slider.style.left = 0;
      showResult(false);
    }
    // 重置拖动状态和滑块位置
    currentX = 0;
    isDragging = false;
  }
});

// 显示验证结果的函数,根据验证结果显示不同的提示信息和样式,并在一定时间后隐藏提示信息和重置滑块位置
function showResult(success) {
  var result = document.querySelector('.result');
  if (success) {
    // 显示验证通过的提示信息和样式
    result.innerHTML = '验证通过!';
    result.classList.remove('error');
    result.classList.add('success');
    result.style.display = 'block';
    // 一定时间后隐藏提示信息和重置滑块位置
    setTimeout(function () {
      overlay.style.display = 'none';
      result.style.display = 'none';
      result.classList.remove('success');
      slider.style.left = 0;
    }, 1000);
  } else {
    // 显示验证失败的提示信息和样式
    result.innerHTML = '验证失败,请重试。';
    result.classList.remove('success');
    result.classList.add('error');
    result.style.display = 'block';
  }
}

源码-掘金/滑块验证