前端图形学实战: 从零开发一款轻量级滑动验证码组件(vue3 + vite版)

简介: 前端图形学实战: 从零开发一款轻量级滑动验证码组件(vue3 + vite版)


今天和大家分享一个新的图形学实战——滑动验证码的实现

你将收获

  • vue3组件的设计思路和技巧
  • canvas的常用绘图api用法
  • 滑动验证码组件的基本设计原理和实现过程
  • 如何从零发布vue3组件库

效果演示

按照笔者的写作习惯, 这里先和大家演示一下实现的效果:

image.png

正文

这篇文章我会带大家使用 vue3 + vite 来实现滑动验证码, 当然笔者之前也实现了基于 react 的滑动验证码组件, 感兴趣的可以参考我之前的文章:

同时也可以在 github 上查看到对应的实现代码:

github.com/MrXujiang/v…

技术实现

在着手开发组件之前我们需要提前明确组件的设计需求, 并制定合理的开放 api, 这里分享一下我总结的组件优雅设计的原则:

  • 可读性(代码格式统一清晰,注释完整,代码结构层次分明,编程范式使用得当)
  • 可用性(代码功能完整,在不同场景都能很好兼容,业务逻辑覆盖率)
  • 复用性(代码可以很好的被其他业务模块复用)
  • 可维护性(代码易于维护和扩展,并有一定的向下/向上兼容性)
  • 高性能(组件具有一定的性能, 如复杂场景的渲染, 计算等)

而我们滑动验证码组件要想让更多的人使用, 就必须做到一定程度的灵活可配置, 接下来是我总结的滑动验证码的核心 api:

  • 控制滑动验证码的显示隐藏(visible)
  • 控制滑动验证码的大小(width 和 height)
  • 控制滑块的样式(边长 l 和半径 r)
  • 滑块的提示文本(text)
  • 滑动验证码的图片数据源(imgUrls)
  • 验证成功的事件回调(onSuccess)
  • 验证失败的事件回调(onFail)
  • 用户自定义的验证逻辑(onCustomVertify)
  • 用户拖拽滑块时的回调(onDraw)
  • 刷新图片时的回调(onRefresh)

对应图示如下:

image.png

属性功能设计好之后我们来构想一下在vue项目中具体的使用方式:

<template>
  <div>
    <vertify 
      width="200" 
      height="80" 
      l="50" 
      r="5"
    ></vertify>
  </div>
</template>
<script setup>
// 业务逻辑...
</script>

以上基础准备好之后我们来可看一下滑动验证码的具体交互流程:

image.png

1.设计滑动验证码的基本骨架

这里我以vue3组合函数的方法来定义一下 vue-slider-veritfy 组件骨架:

<script lang="ts" setup>
import { ref, watch, onMounted } from "vue";
interface VertifyType {
  spliced: boolean;
  verified: boolean; // 简单验证拖动轨迹,为零时表示Y轴上下没有波动,可能非人为操作
  left: number; // 滑块的移动位置
  destX: number; // 滑块的目标位置
}
interface IProps {
  width?: number;
  visible?: boolean;
  height?: number;
  refreshIcon?: string;
  l?: number;
  r?: number;
  imgUrl?: string;
  text?: string;
  /**
   * @description   拖拽滑块时的回调, 参数为当前滑块拖拽的距离
   * @default       (l: number):void => {}
   */
  onDraw?: (l: number) => {};
  /**
   * @description   用户的自定义验证逻辑
   * @default       (arg: VertifyType) => VertifyType
   */
  onCustomVertify?: (arg: VertifyType) => VertifyType;
  /**
   * @description   重制刷新前的回调
   * @default       ():void => {}
   */
  onBeforeRefresh?: () => void;
  /**
   * @description   验证成功回调
   * @default       ():void => {}
   */
  onSuccess?: VoidFunction;
  /**
   * @description   验证失败回调
   * @default       ():void => {}
   */
  onFail?: VoidFunction;
  /**
   * @description   刷新时回调
   * @default       ():void => {}
   */
  onRefresh?: VoidFunction;
}
// 定义默认值
const props = withDefaults(defineProps<IProps>(), {
  width: 320,
  visible: true,
  height: 160,
  refreshIcon: "http://cdn.dooring.cn/dr/icon12.png",
  l: 42,
  r: 9,
  imgUrl: "",
  text: "",
});
// 构建dom结构
<template>
  <div
    className="vertifyWrap"
  >
    <div className="canvasArea">
      <canvas ref="canvasRef"></canvas>
    </div>
    <div
      :className="sliderClass"
    >
      <div className="sliderMask">
        <div
          className="slider"
        >
          <div className="sliderIcon">&rarr;</div>
        </div>
      </div>
      <div className="sliderText">{{ textTip }}</div>
    </div>
    <div
      className="refreshIcon"
    ></div>
    <div
      className="loadingContainer"
    >
      <div className="loadingIcon"></div>
      <span>加载中...</span>
    </div>
  </div>
</template>

上面代码需要注意的就是 withDefaultsdefineProps , defineProps 主要用来定义 vue 组件的属性类型, withDefaults 可以用来定义组件属性的默认值, 这也是在设计 vue3 组件中常用的 api

2. 滑动验证码核心功能实现

接下来我们需要实现以下几个核心功能:

  • 镂空效果的 canvas 图片实现
  • 镂空图案 canvas 实现
  • 滑块移动和验证逻辑实现

上面的描述可能比较抽象,我画张图示意一下:

image.png

在开始编码之前我们需要对 canvas 有个基本的了解,建议不熟悉的朋友可以参考高效 canvas 学习文档: Canvas of MDN

由上图可知首先要解决的问题就是如何用 canvas 画不规则的图形,这里我简单的画个草图:

image.png

我们只需要使用 canvas 提供的 路径api 画出上图的路径,并将路径填充为任意半透明的颜色即可。建议大家不熟悉的可以先了解如下 api :

  • beginPath() 开始路径绘制
  • moveTo() 移动笔触到指定点
  • arc() 绘制弧形
  • lineTo() 画线
  • stroke() 描边
  • fill() 填充
  • clip() 裁切路径

由于实现方式比较固定, 偏 canvas 语法层, 这里直接分享一下代码:

const drawPath = (ctx: any, x: number, y: number, operation: "fill" | "clip") => {
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI);
  ctx.lineTo(x + l, y);
  ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI);
  ctx.lineTo(x + l, y + l);
  ctx.lineTo(x, y + l);
  ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true);
  ctx.lineTo(x, y);
  ctx.lineWidth = 2;
  ctx.fillStyle = "rgba(255, 255, 255, 0.7)";
  ctx.strokeStyle = "rgba(255, 255, 255, 0.7)";
  ctx.stroke();
  ctx.globalCompositeOperation = "destination-over";
  operation === "fill" ? ctx.fill() : ctx.clip();
};

这里需要补充的一点是 canvasglobalCompositeOperation 属性,它的主要目的是设置如何将一个源(新的)图像绘制到目标(已有)的图像上。

  • 源图像 = 我们打算放置到画布上的绘图
  • 目标图像 = 我们已经放置在画布上的绘图

w3c上有个形象的例子:

image.png

这里之所以设置该属性是为了让镂空的形状不受背景底图的影响并覆盖在背景底图的上方。如下:

image.png

接下来我们只需要将图片绘制到画布上即可:

const canvasCtx = canvasRef.current.getContext('2d')
// 绘制镂空形状
drawPath(canvasCtx, 50, 50, 'fill')
// 画入图片
canvasCtx.drawImage(img, 0, 0, width, height)

接下来我们只需要用 javascript 实现随机图片和随机位置即可。

我们再来实现一下镂空效果:

const blockCtx = blockRef.value.getContext('2d')
drawPath(blockCtx, 50, 50, 'clip')
blockCtx.drawImage(img, 0, 0, width, height)
// 提取图案滑块并放到最左边
const y1 = 50 - r * 2 - 1
const ImageData = blockCtx.getImageData(xRef.value - 3, y1, L, L)
// 调整滑块画布宽度
blockRef.value.width = L
blockCtx.putImageData(ImageData, 0, y1)

上面的代码我们用到了 getImageDataputImageData,这两个 api 主要用来获取 canvas 画布场景像素数据和对场景进行像素数据的写入。实现后 的效果如下:

image.png

实现滑块移动和验证逻辑

实现滑块移动的方案也比较简单,我们只需要利用鼠标的 event 事件即可:

  • onMouseDown
  • onMouseMove
  • onMouseUp

image.png

以上是一个简单的示意图,具体实现代码如下:

const handleDragMove = (e) => {
    if (!isMouseDownRef.value) return false
    e.preventDefault()
    // 为了支持移动端, 可以使用e.touches[0]
    const eventX = e.clientX || e.touches[0].clientX
    const eventY = e.clientY || e.touches[0].clientY
    const moveX = eventX - originXRef.value
    const moveY = eventY - originYRef.value
    if (moveX < 0 || moveX + 36 >= width) return false
    sliderLeft.value = moveX;
    const blockLeft = (width - l - 2r) / (width - l) * moveX
    blockRef.value.style.left = blockLeft + 'px'
}

当然我们还需要对拖拽停止后的事件做监听,来判断是否验证成功,并埋入成功和失败的回调。

我们发现 vue3 版本的实现和我之前的 react 版本的方式类似, 主要区别就在于 api 用法和定义上, 这里分享一下具体的变量定义:

const {
  text,
  l,
  r,
  imgUrl,
  width,
  height,
  visible,
  onBeforeRefresh,
  onRefresh,
  onFail,
  onSuccess,
  onCustomVertify,
  onDraw,
} = props;
const isLoading = ref(false);
const sliderLeft = ref(0);
const sliderClass = ref("sliderContainer");
const textTip = ref(text);
const canvasRef = ref<any>(null);
const blockRef = ref<any>(null);
const imgRef = ref<any>(null);
const isMouseDownRef = ref<boolean>(false);
const trailRef = ref<number[]>([]);
const originXRef = ref<number>(0);
const originYRef = ref<number>(0);
const xRef = ref<number>(0);
const yRef = ref<number>(0);
const PI = Math.PI;
const L = l + r * 2 + 3; // 滑块实际边长

完整的实现效果如下:

image.png

将实现的滑动验证码组件发布到 npm 上

发布流程我在 从零开发一款轻量级滑动验证码插件 有详细的介绍, 接下来给大家展示一下通过 vite 打包组件包的配置:

export default defineConfig({
  ...baseConfig,
  build: {
    outDir: 'dist',
    lib: {
      entry: resolve(__dirname, '../packages/index.ts'),
      name: 'vue-slider-vertify',
      fileName: (format) => `vue-slider-vertify.${format}.js`,
    },
    rollupOptions: {
      // 确保外部化处理不想打包进库的依赖
      external: ['vue'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: 'Vue'
        }
      }
    }
  },
  plugins: [
    ...(baseConfig as any).plugins,
    dts(),
  ]
});

打包后的 dist 目录结构如下:

image.png

如果大家想了解详细的源码和打包过程, 可以在 github 上查看完整代码, 地址如下:

github.com/MrXujiang/v…

如果大家想通过 npm 的方式直接使用, 可以用我已经发布到 npm 服务器的 react-slider-vertify, 按照如下方式安装和使用:

# 或者 npm add @alex_xu/react-slider-vertify
yarn add @alex_xu/react-slider-vertify

在代码里使用:

import React from 'react';
import { Vertify } from '@alex_xu/react-slider-vertify';
export default () => {
    return <Vertify 
            width={320}
            height={160}
            onSuccess={() => alert('success')} 
            onFail={() => alert('fail')} 
            onRefresh={() => alert('refresh')} 
        />
};

扩展

大家也可以基于自己的业务需求改造我们的验证码, 实现更负复杂的功能, 改造成多种形态的验证码, 也欢迎大家随时贡献, 一起打造非常有意思的验证码组件。

后期规划

后面会继续围绕图形可视化来实现更多有意思的应用, 比如3D可视化, 图形编辑器, 可视化图表等, 如果大家感兴趣, 可以参考我的github: gitee.com/lowcode-chi…

如果文章对你有帮助, 欢迎点赞评论, 让我们一起探索真正的前端技术。


目录
相关文章
|
2月前
|
前端开发 API 开发者
harmonyOS基础- 快速弄懂HarmonyOS ArkTs基础组件、布局容器(前端视角篇)
本文由黑臂麒麟(6年前端经验)撰写,介绍ArkTS开发中的常用基础组件与布局组件。基础组件包括Text、Image、Button等,支持样式设置如字体颜色、大小和加粗等,并可通过Resource资源引用统一管理样式。布局组件涵盖Column、Row、List、Grid和Tabs等,支持灵活的主轴与交叉轴对齐方式、分割线设置及滚动事件监听。同时,Tabs组件可实现自定义样式与页签切换功能。内容结合代码示例,适合初学者快速上手ArkTS开发。参考华为开发者联盟官网基础课程。
210 75
harmonyOS基础- 快速弄懂HarmonyOS ArkTs基础组件、布局容器(前端视角篇)
|
2月前
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
164 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
3月前
|
前端开发 安全 开发工具
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
241 90
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
1月前
|
监控 前端开发 小程序
陪练,代练,护航,代打小程序源码/前端UNIAPP-VUE2.0开发 后端Thinkphp6管理/具备家政服务的综合型平台
这款APP通过技术创新,将代练、家政、娱乐社交等场景融合,打造“全能型生活服务生态圈”。以代练为切入点,提供模块化代码支持快速搭建平台,结合智能匹配与技能审核机制,拓展家政服务和商业管理功能。技术架构具备高安全性和扩展性,支持多业务复用,如押金冻结、录屏监控等功能跨领域应用。商业模式多元,包括交易抽成、增值服务及广告联名,同时设计跨领域积分体系提升用户粘性,实现生态共生与B端赋能。
130 11
|
2月前
|
缓存 安全 Android开发
Python实战:搭建短信转发器,实现验证码自动接收与处理
在移动互联网时代,短信验证码是重要的安全手段,但手动输入效率低且易出错。本文介绍如何用Python搭建短信转发器,实现验证码自动接收、识别与转发。通过ADB工具监听短信、正则表达式或ddddocr库提取验证码,并利用Flask框架转发数据。系统支持多设备运行,具备安全性与性能优化功能,适合自动化需求场景。未来可扩展更多功能,提升智能化水平。
151 1
|
3月前
|
安全 前端开发 开发工具
【01】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-项目开发实战-优雅草卓伊凡拟开发一个一站式家政服务平台-前期筹备-暂定取名斑马家政软件系统-本项目前端开源-服务端采用优雅草蜻蜓Z系统-搭配ruoyi框架admin后台-全过程实战项目分享-从零开发到上线
【01】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-项目开发实战-优雅草卓伊凡拟开发一个一站式家政服务平台-前期筹备-暂定取名斑马家政软件系统-本项目前端开源-服务端采用优雅草蜻蜓Z系统-搭配ruoyi框架admin后台-全过程实战项目分享-从零开发到上线
150 5
【01】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-项目开发实战-优雅草卓伊凡拟开发一个一站式家政服务平台-前期筹备-暂定取名斑马家政软件系统-本项目前端开源-服务端采用优雅草蜻蜓Z系统-搭配ruoyi框架admin后台-全过程实战项目分享-从零开发到上线
|
3月前
|
JSON 前端开发 测试技术
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
155 10
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
|
3月前
|
JSON 前端开发 API
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
124 5
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
|
3月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
87 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
3月前
|
JSON 前端开发 数据可视化
前端开发者狂喜!30K star开源组件库,界面美观度/开发速度双碾压!
嗨,大家好,我是小华同学。Layui 是一款开源前端 UI 组件库,具有极简设计、强大功能和卓越性能,支持布局、表单、表格、弹层等六大模块,组件高度可定制。它无需复杂构建工具,直接面向浏览器开发,极大提升开发效率与界面美观度。适合新手和老手,快来试试吧!