<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">简单验证码 · 脚本生成</title>
<!-- 干净简洁,无外部依赖 -->
<style>
* {
box-sizing: border-box;
}
body {
background: #f5f7fa;
min-height: 100vh;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}
.card {
background: white;
border-radius: 32px;
padding: 2rem 2.5rem 2.5rem;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.06), 0 8px 16px rgba(0, 0, 0, 0.04);
text-align: center;
max-width: 460px;
width: 100%;
transition: 0.2s;
}
h2 {
font-weight: 500;
font-size: 1.6rem;
letter-spacing: -0.01em;
color: #1e293b;
margin-top: 0;
margin-bottom: 0.75rem;
}
.sub {
color: #64748b;
font-size: 0.95rem;
margin-bottom: 1.8rem;
border-bottom: 1px solid #e9edf2;
padding-bottom: 1rem;
}
.capture-box {
background: #f8fafc;
border-radius: 24px;
padding: 1.25rem;
margin-bottom: 1.5rem;
border: 1px solid #e9edf2;
}
canvas {
display: block;
margin: 0 auto;
border-radius: 18px;
background: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);
width: 100%;
height: auto;
max-width: 240px;
aspect-ratio: 4 / 1.4; /* 保持宽高 120/42 ≈ 2.857, 近似 */
cursor: pointer;
transition: box-shadow 0.2s;
}
canvas:active {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.info-row {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
flex-wrap: wrap;
margin: 0.5rem 0 0.2rem;
}
.captcha-code {
font-family: 'Courier New', monospace;
font-size: 1.5rem;
font-weight: 500;
letter-spacing: 0.3em;
color: #0f172a;
background: #f1f5f9;
padding: 0.2rem 1rem;
border-radius: 60px;
display: inline-block;
min-width: 6em;
}
.btn-group {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.8rem;
margin-top: 1rem;
}
.btn {
background: white;
border: 1px solid #d1d9e6;
padding: 0.6rem 1.4rem;
border-radius: 60px;
font-size: 0.9rem;
font-weight: 500;
color: #1e293b;
cursor: pointer;
transition: 0.15s ease;
display: inline-flex;
align-items: center;
gap: 0.4rem;
box-shadow: 0 1px 2px rgba(0,0,0,0.02);
}
.btn-primary {
background: #1e293b;
border-color: #1e293b;
color: white;
box-shadow: 0 4px 8px rgba(30, 41, 59, 0.12);
}
.btn-primary:hover {
background: #0f172a;
border-color: #0f172a;
box-shadow: 0 6px 12px rgba(30, 41, 59, 0.18);
}
.btn-primary:active {
transform: scale(0.96);
}
.btn-outline:hover {
background: #f1f5f9;
border-color: #b0c0d4;
}
.btn-outline:active {
background: #e2e8f0;
transform: scale(0.96);
}
.footnote {
font-size: 0.8rem;
color: #94a3b8;
margin-top: 1.6rem;
border-top: 1px solid #ecf1f7;
padding-top: 1.2rem;
}
.footnote span {
background: #ecf1f7;
padding: 0.1rem 0.6rem;
border-radius: 30px;
font-family: monospace;
}
</style>
</head>
<body>
<div class="card">
<h2>🛡️ 验证码工坊</h2>
<div class="sub">点击图片 · 随机刷新</div>
<div class="capture-box">
<!-- 验证码画布:宽240x90 比实际稍大,但保留清晰度,内部绘制按比例 -->
<canvas id="captchaCanvas" width="240" height="90"></canvas>
</div>
<div class="info-row">
<span class="captcha-code" id="codeDisplay">A7K2</span>
</div>
<div class="btn-group">
<button class="btn btn-primary" id="refreshBtn">⟳ 刷新</button>
<button class="btn btn-outline" id="copyBtn">📋 复制</button>
</div>
<div class="footnote">
轻触 canvas 或点击刷新 · 自动生成4位字母数字
</div>
</div>
<script>
(function() {
// ----- 核心配置 -----
const canvas = document.getElementById('captchaCanvas');
const ctx = canvas.getContext('2d');
const codeDisplay = document.getElementById('codeDisplay');
// 图形参数 (适应 canvas 240x90)
const W = 240, H = 90;
canvas.width = W;
canvas.height = H;
// 字符集 (剔除容易混淆的 0/O, 1/I/l 等)
const CHARSET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
const CODE_LENGTH = 4;
// 当前验证码 (纯大写字母+数字)
let currentCode = '';
// ----- 生成随机验证码 -----
function generateRandomCode() {
let code = '';
for (let i = 0; i < CODE_LENGTH; i++) {
const idx = Math.floor(Math.random() * CHARSET.length);
code += CHARSET[idx];
}
return code;
}
// ----- 绘制验证码 (带干扰) -----
function drawCaptcha(code) {
// 清空背景 (使用柔和底色)
ctx.fillStyle = '#f2f5fa';
ctx.fillRect(0, 0, W, H);
// 1️⃣ 绘制噪点背景 (浅色圆点)
ctx.save();
for (let i = 0; i < 120; i++) {
const x = Math.random() * W;
const y = Math.random() * H;
const radius = Math.random() * 3.5 + 1.2;
const gray = 160 + Math.floor(Math.random() * 60);
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = `rgba(${gray}, ${gray+20}, ${gray+40}, 0.15)`;
ctx.fill();
}
ctx.restore();
// 2️⃣ 随机干扰线条 (2~3条)
ctx.save();
for (let i = 0; i < 2 + Math.floor(Math.random() * 2); i++) {
ctx.beginPath();
const startX = Math.random() * W * 0.2;
const startY = Math.random() * H;
const endX = W * 0.6 + Math.random() * W * 0.4;
const endY = Math.random() * H;
ctx.moveTo(startX, startY);
// 曲线路径 制造波浪
const cpx1 = (startX + endX) / 3 + (Math.random() - 0.5) * 50;
const cpy1 = Math.random() * H;
const cpx2 = (startX + endX) * 0.7 + (Math.random() - 0.5) * 40;
const cpy2 = Math.random() * H;
ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, endX, endY);
ctx.strokeStyle = `rgba(60, 80, 120, ${0.2 + Math.random() * 0.25})`;
ctx.lineWidth = 1.2 + Math.random() * 2.2;
ctx.stroke();
}
ctx.restore();
// 3️⃣ 绘制文字 (核心验证码)
ctx.save();
const charCount = code.length;
const baseX = 20; // 起始X偏移
const spacing = (W - baseX * 1.8) / (charCount - 0.5); // 字符间距
const fontSize = 36 + Math.floor(Math.random() * 8); // 36~43
ctx.font = `bold ${fontSize}px 'Courier New', monospace, 'Segoe UI'`;
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
for (let i = 0; i < charCount; i++) {
const ch = code[i];
// 每个字符独立坐标 + 随机偏移 + 旋转
const x = baseX + i * spacing + (Math.random() - 0.5) * 12;
const y = H / 2 + (Math.random() - 0.5) * 16;
const angle = (Math.random() - 0.5) * 0.35; // -17° ~ 17°
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
// 随机颜色 (明亮但有区别)
const hue = 200 + Math.floor(Math.random() * 70); // 蓝绿到橙紫
const sat = 45 + Math.floor(Math.random() * 40); // 50~85
const lig = 30 + Math.floor(Math.random() * 30); // 30~60
ctx.fillStyle = `hsl(${hue}, ${sat}%, ${lig}%)`;
// 轻微阴影增强可读性
ctx.shadowColor = 'rgba(0,0,0,0.08)';
ctx.shadowBlur = 6;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
// 描边 (细白边增加辨识)
ctx.strokeStyle = 'rgba(255,255,240,0.25)';
ctx.lineWidth = 1.2;
ctx.strokeText(ch, 0, 0);
ctx.fillText(ch, 0, 0);
ctx.restore();
}
ctx.restore();
// 4️⃣ 随机额外干扰 (细碎小点 & 短线)
ctx.save();
for (let i = 0; i < 20; i++) {
const x = Math.random() * W;
const y = Math.random() * H;
ctx.beginPath();
ctx.arc(x, y, 1 + Math.random() * 2.5, 0, Math.PI * 2);
ctx.fillStyle = `rgba(50,60,80,${0.1 + Math.random() * 0.25})`;
ctx.fill();
}
// 少量短线
for (let i = 0; i < 5; i++) {
const x1 = Math.random() * W;
const y1 = Math.random() * H;
const x2 = x1 + (Math.random() - 0.5) * 40;
const y2 = y1 + (Math.random() - 0.5) * 18;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.strokeStyle = `rgba(70,80,100,${0.08 + Math.random() * 0.15})`;
ctx.lineWidth = 0.8 + Math.random() * 1.5;
ctx.stroke();
}
ctx.restore();
// 5️⃣ 外边框 / 装饰 (极浅线)
ctx.save();
ctx.strokeStyle = 'rgba(200,210,225,0.3)';
ctx.lineWidth = 1.5;
ctx.strokeRect(2, 2, W-4, H-4);
ctx.restore();
}
// ----- 刷新验证码 (生成+绘制+更新UI) -----
function refreshCaptcha() {
const newCode = generateRandomCode();
currentCode = newCode;
drawCaptcha(newCode);
codeDisplay.textContent = newCode;
}
// ----- 复制验证码到剪贴板 (带反馈) -----
async function copyCodeToClipboard() {
const code = currentCode || codeDisplay.textContent;
if (!code) return;
try {
await navigator.clipboard.writeText(code);
// 轻提示 (通过按钮文本闪烁)
const copyBtn = document.getElementById('copyBtn');
const originalText = copyBtn.innerText;
copyBtn.innerText = '✅ 已复制';
copyBtn.style.transition = '0.1s';
setTimeout(() => {
copyBtn.innerText = originalText;
}, 1000);
} catch (err) {
// fallback (针对部分环境)
const textarea = document.createElement('textarea');
textarea.value = code;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
alert('验证码已复制: ' + code);
}
}
// ----- 初始化 & 事件绑定 -----
function init() {
// 首次生成
refreshCaptcha();
// 刷新按钮
document.getElementById('refreshBtn').addEventListener('click', function(e) {
e.preventDefault();
refreshCaptcha();
});
// 复制按钮
document.getElementById('copyBtn').addEventListener('click', function(e) {
e.preventDefault();
copyCodeToClipboard();
});
// 点击canvas刷新 (体验)
canvas.addEventListener('click', function(e) {
// 避免误触菜单等
refreshCaptcha();
});
// 键盘快捷键: R 刷新 (可选项)
document.addEventListener('keydown', function(e) {
if (e.key === 'r' || e.key === 'R') {
// 如果焦点不在输入框等,执行刷新 (但防止意外的页面刷新)
if (!e.target.closest('input, textarea, select, button')) {
e.preventDefault();
refreshCaptcha();
}
}
});
// 额外装饰: 悬停提示
canvas.title = '点击刷新验证码';
}
// 启动所有
init();
})();
</script>
</body>
</html>
验证码生成与交互
您可以通过点击或快捷键轻松获取新的验证码,并一键复制使用。

- 验证码生成:脚本每次会随机生成一个包含4位大写字母和数字的验证码,并自动绘制到画布上。
- 核心操作:您可以通过点击验证码图片或“刷新”按钮来获取新验证码,点击“复制”按钮可以快速将当前验证码复制到剪贴板。
- 视觉与干扰:生成的验证码加入了随机颜色、旋转、噪点和干扰线,在保证可读性的同时增加了一定的识别难度。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播