UNPKG

@ghini/kit

Version:

js practical tools to assist efficient development

139 lines (135 loc) 3.75 kB
// 加密使用sha256就行,高效安全,开销10微秒级;自定义fnv1a意义不大 export { captcha }; const DEFAULT_CONFIG = { width: 120, height: 40, length: 4, }; function captcha(options = {}) { const config = { ...DEFAULT_CONFIG, ...options }; const { width, height, length } = config; const code = verifyCode(length); // 生成干扰线元素 const interferenceLines = svgInterferenceLines(width, height) .map( (path) => `<path d="${path}" stroke="${randomColor( 170, 230 )}" stroke-width="1.3" fill="none"/>` ) .join(""); const svg = ` <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"> <rect width="100%" height="100%" fill="#f7f7f7"/> ${svgChars(code, config)} ${interferenceLines} ${svgNoiseDots(width, height)} </svg> `; // console.log(svg); return { svg, code, }; } function randomColor(min, max) { const r = Math.floor(Math.random() * (max - min) + min); const g = Math.floor(Math.random() * (max - min) + min); const b = Math.floor(Math.random() * (max - min) + min); return `rgb(${r},${g},${b})`; } // 生成验证码字符 function verifyCode(length) { const characters = "23457ACDFGHJKLPQRSTUVWXY23457"; let result = ""; const len = characters.length; for (let i = 0; i < length; i++) { const idx = Math.floor(Math.random() * len); result += characters[idx]; } return result; } // 生成干扰线路径 function svgInterferenceLines(width, height) { const basic = [ [ [0.05, 0.2, 0.95, 0.8], [0.05, 0.3, 0.95, 0.7], [0.05, 0.8, 0.95, 0.2], ], [ [0.05, 0.2, 0.95, 0.8], [0.05, 0.7, 0.95, 0.3], [0.05, 0.8, 0.95, 0.2], ], [ [0.05, 0.2, 0.95, 0.8], [0.35, 0.2, 0.65, 0.8], [0.05, 0.8, 0.95, 0.2], ], [ [0.05, 0.2, 0.95, 0.8], [0.35, 0.8, 0.65, 0.2], [0.05, 0.8, 0.95, 0.2], ], [ [0.05, 0.2, 0.95, 0.8], [0.35, 0.2, 0.65, 0.8], [0.35, 0.8, 0.65, 0.2], ], [ [0.05, 0.8, 0.95, 0.2], [0.35, 0.2, 0.65, 0.8], [0.35, 0.8, 0.65, 0.2], ], ]; const randomPattern = basic[Math.floor(Math.random() * 6)]; return randomPattern.map((item) => { const x1 = (item[0] + Math.random() * 0.1) * width; const y1 = (item[1] + Math.random() * 0.3 - 0.15) * height; const x2 = (item[2] - Math.random() * 0.1) * width; const y2 = (item[3] + Math.random() * 0.3 - 0.15) * height; return `M ${x1} ${y1} L ${x2} ${y2}`; }); } // 生成干扰点 function svgNoiseDots(width, height) { const dots = []; for (let i = 0; i < 100; i++) { const x = Math.random() * width; const y = Math.random() * height; dots.push( `<circle cx="${x}" cy="${y}" r="1" fill="${randomColor(150, 230)}"/>` ); } return dots.join(""); } // 生成字符元素 function svgChars(code, config) { const { width, height, length } = config; const fontSize = Math.floor(height * 0.65); const charWidth = width / length; const offsetX = charWidth / 2; return code .split("") .map((char, i) => { const rotate = (0.4 + Math.random() * 0.7) * (Math.round(Math.random()) * 2 - 1); const x = (i + 1) * charWidth - offsetX; const y = height / 2; return ` <g transform="translate(${x},${y}) rotate(${rotate * 57.3})"> <text x="-${fontSize / 3}" y="0" fill="${randomColor(100, 150)}" font-family="Arial" font-weight="bold" font-size="${fontSize}px" dominant-baseline="middle" >${char}</text> </g>`; }) .join(""); }