
📃个人主页:编程的一拳超人
⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞
于高山之巅,方见大河奔涌;于群峰之上,更觉长风浩荡。 ——《人民日报》
文章目录
验证码通过“Canvas 绘制”与“Base64 图片”渲染两种不同的实现方式显示
一、Canvas 绘制:接口回调“字符串”通过绘制的方式显示到前端✨
1、前端代码(Canvas 绘制):包含验证码区域、绘制验证码方法🎊
<view @tap="refreshCaptcha" class="captcha-container">
<canvas type="2d" id="captchaCanvas"></canvas>
</view>
/**获取账号密码登录的图片验证码*/
refreshCaptcha() {
const that = this;
uni.request({
url: that.$helper.HHH,
method: 'POST',
data: {},
success: res => {
if (res.data.result) {
that.originalValidCode = res.data.data2; // 保存验证码文本
that.drawCaptcha(res.data.data); // 绘制验证码
} else {
uni.showToast({
title: '获取验证码失败',
icon: 'none'
});
}
},
fail: () => {
uni.showToast({
title: '获取验证码失败',
icon: 'none'
});
}
});
},
/**绘制验证码,直接使用即可*/
async drawCaptcha(text) {
try {
// 获取 canvas 实例
const query = uni.createSelectorQuery().in(this);
const canvas = await new Promise(resolve => {
query.select('#captchaCanvas')
.fields({
node: true,
size: true
})
.exec((res) => {
resolve(res[0].node);
});
});
const ctx = canvas.getContext('2d');
// 设置 canvas 大小
const dpr = uni.getSystemInfoSync().pixelRatio;
canvas.width = 100 * dpr;
canvas.height = 32 * dpr;
ctx.scale(dpr, dpr);
// 清空画布
ctx.clearRect(0, 0, 100, 32);
// 设置背景色
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, 100, 32);
// 绘制文字
ctx.fillStyle = '#333333';
ctx.font = 'bold 20px Arial';
ctx.textBaseline = 'middle';
// 计算文字总宽度以居中显示
const textWidth = ctx.measureText(text).width;
const startX = (100 - textWidth) / 2;
// 随机倾斜每个字符
for (let i = 0; i < text.length; i++) {
const x = startX + i * (textWidth / text.length);
const y = 16; // 垂直居中
ctx.save();
// 随机旋转角度
const angle = (Math.random() - 0.5) * 0.3;
ctx.translate(x, y);
ctx.rotate(angle);
ctx.fillText(text[i], 0, 0);
ctx.restore();
}
// 添加干扰线
for (let i = 0; i < 3; i++) {
ctx.beginPath();
ctx.strokeStyle = `rgb(${Math.random()*150},${Math.random()*150},${Math.random()*150})`;
ctx.moveTo(Math.random() * 100, Math.random() * 32);
ctx.lineTo(Math.random() * 100, Math.random() * 32);
ctx.stroke();
}
// 添加干扰点
for (let i = 0; i < 30; i++) {
ctx.fillStyle = `rgb(${Math.random()*150},${Math.random()*150},${Math.random()*150})`;
ctx.beginPath();
ctx.arc(Math.random() * 100, Math.random() * 32, 1, 0, 2 * Math.PI);
ctx.fill();
}
} catch (e) {
console.error('绘制验证码失败:', e);
}
},
2、效果🎊
二、Base64 图片渲染:接口回调直接显示服务器返回的 “Base64 图片”显示到前端✨
1、前端代码(Canvas 绘制):包含验证码区域、绘制验证码方法🎊
- 确保后端返回的 Base64 数据 不带前缀(如
data:image/png;base64,
),前端需要手动拼接:this.captchaImage = `data:image/png;base64,${res.data.base64}`;
<view @tap="refreshCaptcha" class="captcha-container">
<canvas type="2d" id="captchaCanvas"></canvas>
</view>
// 1. 定义数据
data() {
return {
captchaImage: '' // 存储 Base64 图片数据
}
},
// 2. 获取验证码(从后端接口)
methods: {
async refreshCaptcha() {
try {
const res = await api.getCaptcha(); // 替换为你的接口
this.captchaImage = `data:image/png;base64,${res.data.base64}`;
} catch (error) {
console.error('获取验证码失败:', error);
}
}
},
// 3. 初始化时加载
onLoad() {
this.refreshCaptcha();
}
2、效果🎊
三、总结🪄
通过用 Base64 图片渲染验证码,你实现了以下优化:
- 降低前端复杂度:无需维护 Canvas 绘制逻辑。
- 提升安全性:验证码由后端生成,避免前端模拟。
- 更好兼容性:Base64 图片在所有平台表现一致。
1、修改方案对比
特性 | Canvas 绘制 | Base64 图片渲染 |
---|---|---|
实现方式 | 动态生成图形/文字 | 直接显示服务器返回的 Base64 图片 |
性能 | 依赖前端计算 | 直接渲染,性能更优 |
维护性 | 需维护绘制逻辑 | 仅替换图片源即可 |
安全性 | 前端生成,可能被破解 | 后端生成,更安全 |
2、注意事项
1. Base64 格式处理
- 确保后端返回的 Base64 数据 不带前缀(如
data:image/png;base64,
),前端需要手动拼接:this.captchaImage = `data:image/png;base64,${res.data.base64}`;
2. 图片适配样式
- 添加 CSS 确保图片显示正常:
.captcha-container image { width: 100%; /* 宽度自适应容器 */ height: auto; /* 高度按比例缩放 */ }
3. 缓存问题
- 如果验证码图片不变,浏览器可能缓存旧图。解决方法:
- 在 URL 后加随机参数(如
?t=${Date.now()}
) - 或要求后端在 HTTP 头中设置
Cache-Control: no-cache
。
- 在 URL 后加随机参数(如
4. 安全性增强
- 建议后端对验证码 Base64 数据加密,前端解密后使用(防止中间人攻击)。
3、 后端接口建议
确保后端返回的数据结构包含原始 Base64 数据:
{
"code": 200,
"data": {
"base64": "iVBORw0KGgoAAAANSUhEUgAAAMgAA..." // 纯 Base64 字符串
}
}
4、 性能优化
- 压缩图片:后端应返回压缩后的 Base64 数据(减少传输体积)。
- 懒加载:验证码图片仅在需要时请求(如点击刷新时)。