在 uni-app
中使用 canvas 绘制验证码图片与在 Web 中使用 HTML5 canvas 类似,但 uni-app
提供了一套跨平台的 API,使得开发者可以在不同平台上使用相同的代码。以下是一个基本的示例,展示如何在 uni-app
中使用 canvas 绘制一个简单的验证码图片。
首先,你需要在页面的 .vue
文件中添加一个 <canvas>
标签,并为其指定一个 ref 或者 id,以便在 JavaScript 中引用它。
效果图:
组件代码:
<template>
<view v-if="visible" class="captcha-modal">
<view class="captcha-container">
<text class="close" @click="close">×</text>
<text class="captcha-text"> 校验码 </text>
<!-- 纯数字验证码 -->
<!-- <text class="captcha-value">{{ captcha }}</text> -->
<view class="captcha-value-view">
<canvas class="captcha-value-image" canvas-id="captchaCanvas"></canvas>
</view>
<view class="captcha-view">
<input class="input-captcha" type="text" v-model="userInput" placeholder="请输入" @confirm="checkCaptcha" />
<!-- <button class="captcha-btn" @click="generateCaptcha">刷新</button> -->
<button class="cu-btn" @click="generateCaptcha">刷新</button>
</view>
<button class="check-btn" @click="checkCaptcha">校验</button>
<text class="error-message" v-if="errorMessage" >{{ errorMessage }}</text>
</view>
</view>
</template>
<script>
export default {
name: 'CaptchaPage',
emits: ['success'],
props: {
visible: {
type: Boolean,
default: false
}
},
data() {
return {
captcha: '',
userInput: '',
errorMessage: ''
};
},
watch: {
// 监听visible变化,如果变为true则生成验证码
visible(newVal) {
if (newVal) {
setTimeout(() => {
this.generateCaptcha();
this.userInput = '';
this.errorMessage = '';
}, 500); // 延迟 500 毫秒
}
}
},
methods: {
generateCaptcha() {
const ctx = uni.createCanvasContext('captchaCanvas', this); // 创建 canvas 绘图上下文
// 设置背景色
ctx.setFillStyle('#ffffff');
ctx.fillRect(0, 0, 100, 40);
// 随机生成验证码字符串
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let captchaText = '';
for (let i = 0; i < 4; i++) {
captchaText += chars.charAt(Math.floor(Math.random() * chars.length));
}
// 更新组件的 captchaText 数据
this.captcha = captchaText;
this.userInput = '';
this.errorMessage = '';
// 设置字体样式
ctx.setFontSize(28);
ctx.setFillStyle('#333333');
ctx.setTextAlign('center');
// 绘制验证码文本
ctx.fillText(captchaText, 50, 30); // 文本绘制在中心点
// 绘制干扰线
for (let i = 0; i < 3; i++) {
ctx.beginPath();
ctx.setStrokeStyle('rgba(0,0,0,' + Math.random() * 0.5 + ')');
ctx.moveTo(Math.random() * 100, Math.random() * 40);
ctx.lineTo(Math.random() * 100, Math.random() * 40);
ctx.stroke();
}
// 绘制干扰点
for (let i = 0; i < 100; i++) {
ctx.fillStyle = 'rgba(0,0,0,' + Math.random() * 0.3 + ')';
ctx.beginPath();
ctx.arc(Math.random() * 100, Math.random() * 40, 1, 0, 2 * Math.PI);
ctx.fill();
}
// 将绘制的内容导出到 canvas 显示
ctx.draw(true); // 这里的 true 表示绘制完成后执行回调
},
/* 纯数字验证码
generateCaptcha() {
this.captcha = Math.floor(100000 + Math.random() * 900000).toString();
this.userInput = '';
this.errorMessage = '';
},
*/
checkCaptcha() {
if (this.userInput.toLowerCase() === this.captcha.toLowerCase()) {
this.$emit('success','Y'); // 验证码正确时关闭验证码,向父组件发出success事件
} else {
this.errorMessage = '验证码错误,请重试。';
}
},
close() {
this.$emit('success',"N"); // 关闭验证码,向父组件发出success事件
}
}
};
</script>
<style scoped>
.captcha-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.captcha-container {
background-color: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
width: 80%;
max-width: 400px;
text-align: center;
}
.captcha-text {
font-size: 16px;
color: #333;
margin-bottom: 10px;
}
.captcha-value {
font-size: 24px;
color: #f44336;
margin-bottom: 20px;
}
.captcha-value-view{
display: flex;
justify-content: center;
align-items: center;
padding-top: 10px;
}
.captcha-value-image {
width: 100px;
height: 40px;
background-color: rgba(0, 0, 0, 0.1);
}
.captcha-view{
display: flex; /* 启用Flex布局 */
flex-direction: row; /* 设置主轴方向为水平方向 */
align-items: center; /* 子元素在交叉轴上的对齐方式,这里设置为居中 */
justify-content: space-between; /* 子元素在主轴上的对齐方式,这里设置为两端对齐 */
padding:10px 0px 10px 0px; /* 内边距 */
}
.input-captcha {
flex: 1; /* 弹性布局,让输入框占据剩余空间 */
margin-right: 10px; /* 右边距,用于与按钮隔开一定距离 */
left: 0px;
padding: 8px; /* 内边距 */
border: 1px solid #ccc; /* 边框 */
border-radius: 4px; /* 边框圆角 */
height: 40px;
font-size: 18px;
}
/* 设置占位符的文字样式 没有生效 */
.input-captcha::placeholder {
color: #999; /* 占位符文字颜色,可按需设置 */
font-size: 16px; /* 占位符文字大小 */
}
.captcha-btn {
padding: 0px 20px; /* 内边距 */
background-color: #0081ff; /* 背景色 */
color: white; /* 文字颜色 */
border: none; /* 去除边框 */
border-radius: 4px; /* 边框圆角 */
height: 40px;
font-size: 16px;
}
.check-btn {
margin-top: 10px;
padding: 0px 20px;
line-height: 45px;
background-color: #0081ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.close {
position: absolute;
top: 10px;
right: 15px;
font-size: 34px;
color: #aaa;
cursor: pointer;
}
.error-message {
display: inline-block; /* 或 block,根据需要选择 */
height: 30px; /* 设置高度,但注意这通常不会有任何效果,因为文本内容会溢出 */
color: red;
line-height: 30px; /* 可以通过设置行高来垂直居中文本(如果高度固定的话) */
font-size: 14px;
}
</style>
父组件代码
<CaptchaPage :visible="showCaptchaModal" @success="onCaptchaSuccess" />