前言
滑动验证码相信大家肯定会经常遇到,其实在婚恋app源码开发时,我们也可以实现该功能,这样就能更好的保护用户信息和平台数据的安全,具体应该如何做呢?
技术实现
接下来将会以我的思路来带大家在婚恋app源码中实现滑动验证码组件。
1. 明确功能
在开发前我们首先要明确要实现哪些功能:
1、随机图片(这里不做过多描述,采用Picsum实现)
2、位置随机的镂空图案绘制
3、利用canvas进行图案裁剪
4、可滑动控制的拖拽滑块
5、图片加载时的loading效果
6、在婚恋app源码中能实现刷新按钮的点击
7、验证拖拽位置是否满足要求
2. 基本框架
我们先搭建出基本的框架
<div v-if="visiblity" class="slidingWrap">
<div class="slidingTitle"></div>
<!-- 画布区域 -->
<div class="canvasArea">
<!-- 主要渲染整体背景及镂空图案 -->
<canvas class="canvas-bg" :width="width" :height="height"></canvas>
<canvas
class="block"
:height="height"
:style="{ left: `${blockLeft}px` }"
></canvas>
</div>
<!-- 底部滑块 -->
<div
:class="['sliderWrap', status]"
:style="{ width: `${width}px` }"
@mouseleave="handleDrapUp"
>
<div
class="progress"
:style="{
width: `${blockLeft}px`
}"
></div>
<div
class="slider-block"
ref="slider"
:style="{ left: `${blockLeft}px` }"
@mousedown="handleDrapDown"
@mouseup="handleDrapUp"
@mousemove="handleDragMove"
>
→
</div>
</div>
<!-- 刷新按钮 -->
<div class="refresh" @click="onRefreshBtnClick"></div>
<!-- 加载中提示 -->
<div
class="loading"
v-show="isLoading"
:style="{ width: `${width}px`, height: `${height}px` }"
>
<img
src="https://img1.baidu.com/it/u=909624486,4182680343&fm=26&fmt=auto"
alt=""
/>
加载中...
</div>
</div>
然后定义一些支持婚恋app源码配置的属性
props: {
// 组件是否可见
visiblity: {
type: Boolean,
default: false
},
// 组件的宽度
width: {
type: Number,
default: 300
},
// 组件的高度
height: {
type: Number,
default: 200
},
// 滑块的长度
l: {
type: Number,
default: 42
},
// 滑块的半径
r: {
type: Number,
default: 9
},
// 滑块的容错值
tolerant: {
type: Number,
default: 5
},
// 成功时的回调
onSuccess: {
type: Function,
default: () => {}
},
// 刷新时的回调
onRefresh: {
type: Function,
default: () => {}
},
// 失败时的回调
onFail: {
type: Function,
default: () => {}
},
// 关闭回调
closed: {
type: Function,
default: () => {}
}
3.实现镂空效果的图片
上图我们可以发现是一个不规则的图形,下面简单画一个草图
在开始之前,我们首先要了解用到的api:
- beginPath(): 当你想创建一个新的路径时,调用此方法
- moveTo(): 将一个新的子路径的起始点移动到(x,y)坐标的方法
- arc(): 绘制圆弧路径的方法
- lineTo(): 使用直线连接子路径的终点到x,y坐标的方法
- stroke(): 描边
- fill(): 填充
- clip(): 裁剪
- save():通过将当前状态放入栈中,保存 canvas 全部状态的方法。
- restore():通过在绘图状态栈中弹出顶端的状态,将 canvas 恢复到最近的保存状态的方法。
- getImageData():返回一个ImageData对象,用来描述canvas区域隐含的像素数据
- putImageData():将数据从已有的 ImageData 对象绘制到位图的方法
- drawImage():在canvas上绘制图像。
代码实现如下:
drawPath(x, y, l, r, ctx, operation) {
ctx.beginPath();
ctx.moveTo(x, y);
ctx.arc(x + l / 2, y - r + 2, r, 0.72 * Math.PI, 2.26 * Math.PI);
ctx.lineTo(x + l, y);
ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * Math.PI, 2.78 * Math.PI);
ctx.lineTo(x + l, y + l);
ctx.lineTo(x, y + l);
// anticlockwise为一个布尔值。为true时,是逆时针方向,否则顺时针方向
ctx.arc(
x + r - 2,
y + l / 2,
r + 0.4,
2.76 * Math.PI,
1.24 * Math.PI,
true
);
ctx.lineTo(x, y);
ctx.lineWidth = 2;
ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
ctx.strokeStyle = "rgba(255, 255, 255, 0.5)";
ctx.stroke();
ctx.globalCompositeOperation = "destination-over";
// 判断是填充还是裁切, 裁切主要用于生成图案滑块
operation === "fill" ? ctx.fill() : ctx.clip();
}
通过该方法我们可以在canvas中画出对应的图案,细心的同学可以发现,我们在body中写了两个canvas标签,那么他们的作用分别是什么呢?
- canvas-bg主要功能是绘制背景图片和镂空图案
- block主要功能是对图案进行裁剪并进行移动
4、初始化函数
/**
* 1. 随机获取到图片
* 2. 随机生成图片滑块位置
* 3. 截取图片并移动到初始位置
* 4. 画出镂空图案
* 5. 拿到下方滑块的初始位置
*/
initCanvas() {
// 拿到canvas对象
let canvas = document.getElementsByClassName("canvas-bg")[0];
let blockcanvas = document.getElementsByClassName("block")[0];
// 画笔
let ctx = canvas.getContext("2d");
let blockCtx = blockcanvas.getContext("2d");
// 每次重新绘制图片时,清除画布原有内容
ctx.clearRect(0, 0, this.width, this.height);
blockcanvas.width = this.width;
// 生成image对象
let img = new Image();
// 解决跨域问题
img.crossOrigin = "";
img.onload = () => {
ctx.drawImage(img, 0, 0);
blockCtx.drawImage(img, 0, 0);
this.isLoading = false;
const ImageData = blockCtx.getImageData(x, y1, this.l + 2 * this.r, y);
// 裁剪后重置画布的状态
blockCtx.restore();
blockcanvas.width = this.l + 2 * this.r;
blockCtx.putImageData(ImageData, 0, y1);
};
// 图片访问地址,后面增加时间戳,防止拿缓存
img.src = `https://picsum.photos/${this.width}/${
this.height
}?time=${+new Date()}`;
// x y 位置随机
let { x, y } = this.getXY();
this.drawPath(x, y, this.l, this.r, ctx, "fill");
// 裁剪前保存画布的状态
blockCtx.save();
this.drawPath(x, y, this.l, this.r, blockCtx, "clip");
const y1 = y - this.r * 2 - 1;
}
我们知道,婚恋app源码中滑动验证码的主要功能就是判断是否人为操作,我们通常采用的方式就是随机验证,这里我们前端采用Math.random()方法进行实现,更安全的做法是通过后端进行返回,生成随机位置的函数如下:
// 随机生成滑块的目标位置
getXY() {
let x =
this.width / 3 +
Math.random() * ((this.width * 3) / 4 - this.width / 3);
let y =
this.height / 3 +
Math.random() * ((this.height * 3) / 5 - this.height / 3);
this.x = x;
return { x, y };
}
这里限制了位置坐标的范围,让滑块的目标位置更加的合理,让目标位置在白色区域范围内:
5.滑块的拖拽
完成以上代码后,页面布局基本为这个样子:
观察我们可以发现,我们拖拽下方箭头时,block跟随滑块移动即可,那么如何在婚恋app源码中实现一个滑块的拖拽呢?
我们可以想一下拖拽动作是如何发生的,按下鼠标左键后移动鼠标即可拖拽,鼠标释放完成拖拽,我们可以把它分解为三个动作:mousedown、mousemove、mouseup,我们监听三个动作即可完成相应的操作。
handleDragMove(e) {
if (!this.isDrop) return false;
e.preventDefault();
// 获取鼠标位置
const eventX = e.clientX || e.touches[0].clientX;
let moveX = eventX - this.blockInitLeft - 20;
// 对滑块的位置进行风控处理
if (moveX < 0 || moveX + 40 + 2 * this.r > this.width) return false;
// 设置滑块的的位置及进度条的宽度
this.blockLeft = moveX;
},
// 初始化未完成时,不可滑动
// 滑动开始时,将状态置为等待状态
handleDrapDown() {
if (this.isLoading) return;
this.isDrop = true;
this.status = "wait";
},
// 拖拽动作完成之后执行
handleDrapUp() {
if (!this.isDrop) return;
this.isDrop = false;
// 判断是否拖拽的指定位置
let val = Math.abs(this.x - this.blockLeft);
this.status = val > this.tolerant ? "fail" : "success";
// 执行相应的生命周期函数
if (this.status === "fail") {
this.onFail();
} else {
this.onSuccess();
}
// 1s后触发关闭回调或者刷新动作
setTimeout(() => {
if (this.status === "success" && this.visiblity) {
this.closed();
return;
}
this.handleRefresh();
}, 1000);
}
最后
婚恋app源码的滑块验证码组件大致功能已经完成,还有一些细节方面需要优化,希望以上内容能给大家带来帮助。