返回

从B站官网实现滑动拼图验证码中的三点体会

前端

为什么要弄这个滑动拼图验证码呢?因为相对来说,前端的实现比较复杂和深入,相比12306的图片验证码而言,对前端技术要求高得多。另外,滑动拼图验证码是众多验证码中最难破解的一种,如今已经广泛地应用在各种网站中。

先来看看最后效果:

(效果演示截图)

接下来,让我们一步步分解代码。

1. 原理

拼图验证码的原理并不复杂,只需通过拖动鼠标来将拼图块滑到正确的位置即可。当拼图块被拖动到正确位置时,验证码将被验证通过。

2. 实现方法

1)HTML 结构

<div class="puzzle-container">
  <div class="puzzle-image-container">
    <img src="puzzle-image.jpg" alt="Puzzle Image">
  </div>
  <div class="puzzle-pieces-container">
    <div class="puzzle-piece" data-x="0" data-y="0"></div>
    <div class="puzzle-piece" data-x="100" data-y="0"></div>
    <div class="puzzle-piece" data-x="200" data-y="0"></div>
    <div class="puzzle-piece" data-x="0" data-y="100"></div>
    <div class="puzzle-piece" data-x="100" data-y="100"></div>
    <div class="puzzle-piece" data-x="200" data-y="100"></div>
    <div class="puzzle-piece" data-x="0" data-y="200"></div>
    <div class="puzzle-piece" data-x="100" data-y="200"></div>
    <div class="puzzle-piece" data-x="200" data-y="200"></div>
  </div>
</div>

2)CSS 样式

.puzzle-container {
  width: 300px;
  height: 300px;
}

.puzzle-image-container {
  width: 300px;
  height: 300px;
  overflow: hidden;
}

.puzzle-image {
  width: 600px;
  height: 600px;
}

.puzzle-pieces-container {
  width: 300px;
  height: 300px;
  position: relative;
}

.puzzle-piece {
  width: 100px;
  height: 100px;
  position: absolute;
  cursor: pointer;
}

3)JavaScript 代码

const puzzleContainer = document.querySelector('.puzzle-container');
const puzzleImageContainer = document.querySelector('.puzzle-image-container');
const puzzleImage = document.querySelector('.puzzle-image');
const puzzlePiecesContainer = document.querySelector('.puzzle-pieces-container');
const puzzlePieces = document.querySelectorAll('.puzzle-piece');

// 拼图块的原始位置
const puzzlePieceOriginalPositions = [];

// 拼图块的当前位置
const puzzlePieceCurrentPositions = [];

// 记录拼图是否完成
let puzzleCompleted = false;

// 为每个拼图块添加事件监听器
puzzlePieces.forEach((puzzlePiece) => {
  puzzlePiece.addEventListener('mousedown', (e) => {
    // 记录拼图块的原始位置
    puzzlePieceOriginalPositions.push({
      x: e.clientX,
      y: e.clientY,
    });

    // 记录拼图块的当前位置
    puzzlePieceCurrentPositions.push({
      x: puzzlePiece.offsetLeft,
      y: puzzlePiece.offsetTop,
    });

    // 开始拖动拼图块
    puzzlePiece.style.transition = 'none';
    puzzlePiece.style.zIndex = 100;

    // 鼠标移动时,更新拼图块的位置
    document.addEventListener('mousemove', (e) => {
      puzzlePiece.style.left = `${e.clientX - puzzlePieceOriginalPositions[0].x + puzzlePieceCurrentPositions[0].x}px`;
      puzzlePiece.style.top = `${e.clientY - puzzlePieceOriginalPositions[0].y + puzzlePieceCurrentPositions[0].y}px`;
    });

    // 鼠标松开时,停止拖动拼图块
    document.addEventListener('mouseup', () => {
      puzzlePiece.style.transition = 'all 0.2s ease-in-out';
      puzzlePiece.style.zIndex = 0;

      // 检查拼图是否完成
      if (isPuzzleCompleted()) {
        puzzleCompleted = true;

        // 显示提示信息
        alert('拼图完成!');
      }

      // 清空数组
      puzzlePieceOriginalPositions.length = 0;
      puzzlePieceCurrentPositions.length = 0;
    });
  });
});

// 检查拼图是否完成
function isPuzzleCompleted() {
  for (let i = 0; i < puzzlePieces.length; i++) {
    if (puzzlePieces[i].offsetLeft !== puzzlePieces[i].dataset.x || puzzlePieces[i].offsetTop !== puzzlePieces[i].dataset.y) {
      return false;
    }
  }

  return true;
}

3. 难点与解决方法

在实现这个功能时,我遇到了几个难点:

  • 如何生成拼图块?

为了生成拼图块,我使用了 HTML5 的 <canvas> 元素。首先,我将原图绘制到 <canvas> 元素上,然后使用 JavaScript 代码将 <canvas> 元素分成 9 个部分,并为每个部分创建一个拼图块。

  • 如何拖动拼图块?

为了拖动拼图块,我使用了 JavaScript 代码。首先,我获取拼图块的原始位置,然后使用 mousemove 事件监听器来更新拼图块的位置。当鼠标松开时,我停止拖动拼图块。

  • 如何判断拼图是否完成?

为了判断拼图是否完成,我使用了 JavaScript 代码。首先,我获取所有拼图块的当前位置,然后将它们与拼图块的原始位置进行比较。如果所有拼图块的当前位置都与拼图块的原始位置相同,则拼图完成。

4. 总结

通过这个项目,我学到了很多东西。我不仅学会了如何实现滑动拼图验证码,还学会了如何使用 HTML5 的 <canvas> 元素和 JavaScript 代码来创建交互式网页。