晨曦温暖的阳光 發表於 2025-6-5 18:37:00

前端实现数字验证码

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<div>
<div>
<blockquote>
<p>该代码的主要功能是生成一个图形验证码,并将其绘制在HTML的<code>&lt;canvas&gt;</code>元素上。验证码由4个随机数字组成,背景和文字颜色随机生成,且文字有一定的旋转角度,增加了识别的难度。</p>
</blockquote>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202506/2149129-20250605183735333-193703092.png" alt="" loading="lazy"></p>
<h3><strong>核心逻辑</strong>:</h3>
<blockquote>
<ul>
<li>
<p><strong><code>useImageVerify</code></strong>:这是一个Vue 3的Composition API函数,用于管理验证码的生成和绘制。它返回一个包含<code>domRef</code>(指向<code>&lt;canvas&gt;</code>元素的引用)、<code>imgCode</code>(生成的验证码字符串)、<code>setImgCode</code>(设置验证码的函数)和<code>getImgCode</code>(重新生成验证码的函数)的对象。</p>
</li>
<li>
<p><strong><code>randomNum</code></strong>:生成指定范围内的随机整数。</p>
</li>
<li>
<p><strong><code>randomColor</code></strong>:生成指定范围内的随机RGB颜色。</p>
</li>
<li>
<p><strong><code>draw</code></strong>:在<code>&lt;canvas&gt;</code>上绘制验证码。包括以下步骤:</p>
<ul>
<li>填充背景色。</li>
<li>绘制4个随机数字,每个数字有不同的字体大小和旋转角度。</li>
<li>绘制5条随机干扰线。</li>
<li>绘制41个随机干扰点。</li>
</ul>
</li>
</ul>
</blockquote>
<h3><strong>实现细节</strong>:</h3>
<blockquote>
<ul>
<li><strong>随机性</strong>:通过<code>randomNum</code>和<code>randomColor</code>函数确保验证码的每个部分(数字、颜色、旋转角度等)都是随机的。</li>
<li><strong>Canvas绘制</strong>:使用Canvas API进行绘制,包括<code>fillRect</code>、<code>fillText</code>、<code>stroke</code>、<code>arc</code>等方法。</li>
<li><strong>Vue生命周期</strong>:在<code>onMounted</code>钩子中调用<code>getImgCode</code>,确保在组件挂载后立即生成验证码。</li>
</ul>
</blockquote>
<h3><strong>扩展性</strong>:</h3>
<blockquote>
<ul>
<li>可以通过修改<code>NUMBER_STRING</code>来支持更多字符(如字母)。</li>
<li>可以通过调整<code>randomNum</code>和<code>randomColor</code>的参数来改变验证码的样式和复杂度。</li>
</ul>
</blockquote>
<h3><strong>使用场景</strong>:</h3>
<blockquote>
<ul>
<li>该验证码生成函数适用于需要简单图形验证码的场景,如登录、注册等表单验证。</li>
</ul>
</blockquote>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">import { ref, onMounted } from "vue";

/**
* 绘制图形验证码
* @param width - 图形宽度
* @param height - 图形高度
*/
export const useImageVerify = (width = 120, height = 40) =&gt; {
const domRef = ref&lt;HTMLCanvasElement&gt;();
const imgCode = ref("");

function setImgCode(code: string) {
    imgCode.value = code;
}

function getImgCode() {
    if (!domRef.value) return;
    imgCode.value = draw(domRef.value, width, height);
}

onMounted(() =&gt; {
    getImgCode();
});

return {
    domRef,
    imgCode,
    setImgCode,
    getImgCode
};
};

function randomNum(min: number, max: number) {
const num = Math.floor(Math.random() * (max - min) + min);
return num;
}

function randomColor(min: number, max: number) {
const r = randomNum(min, max);
const g = randomNum(min, max);
const b = randomNum(min, max);
return `rgb(${r},${g},${b})`;
}

function draw(dom: HTMLCanvasElement, width: number, height: number) {
let imgCode = "";

const NUMBER_STRING = "0123456789";

const ctx = dom.getContext("2d");
if (!ctx) return imgCode;

ctx.fillStyle = randomColor(180, 230);
ctx.fillRect(0, 0, width, height);
for (let i = 0; i &lt; 4; i += 1) {
    const text = NUMBER_STRING;
    imgCode += text;
    const fontSize = randomNum(18, 41);
    const deg = randomNum(-30, 30);
    ctx.font = `${fontSize}px Simhei`;
    ctx.textBaseline = "top";
    ctx.fillStyle = randomColor(80, 150);
    ctx.save();
    ctx.translate(30 * i + 15, 15);
    ctx.rotate((deg * Math.PI) / 180);
    ctx.fillText(text, -15 + 5, -15);
    ctx.restore();
}
for (let i = 0; i &lt; 5; i += 1) {
    ctx.beginPath();
    ctx.moveTo(randomNum(0, width), randomNum(0, height));
    ctx.lineTo(randomNum(0, width), randomNum(0, height));
    ctx.strokeStyle = randomColor(180, 230);
    ctx.closePath();
    ctx.stroke();
}
for (let i = 0; i &lt; 41; i += 1) {
    ctx.beginPath();
    ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
    ctx.closePath();
    ctx.fillStyle = randomColor(150, 200);
    ctx.fill();
}
return imgCode;
}</pre>
</div>
</div>
<h2>vue页面使用</h2>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">&lt;script setup lang="ts"&gt;
import { watch } from "vue";
import { useImageVerify } from "./hooks";

defineOptions({
name: "ReImageVerify"
});

interface Props {
code?: string;
}

interface Emits {
(e: "update:code", code: string): void;
}

const props = withDefaults(defineProps&lt;Props&gt;(), {
code: ""
});

const emit = defineEmits&lt;Emits&gt;();

const { domRef, imgCode, setImgCode, getImgCode } = useImageVerify();

watch(
() =&gt; props.code,
newValue =&gt; {
    setImgCode(newValue);
}
);
watch(imgCode, newValue =&gt; {
emit("update:code", newValue);
});

defineExpose({ getImgCode });
&lt;/script&gt;

&lt;template&gt;
&lt;canvas
    ref="domRef"
    width="120"
    height="40"
    class="cursor-pointer"
    @click="getImgCode"
/&gt;
&lt;/template&gt;</pre>
</div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></p><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/18912921
頁: [1]
查看完整版本: 前端实现数字验证码