UNPKG

vue3-captcha

Version:

a canvas captcha built in Vue3 with TypeScript

179 lines (178 loc) 5 kB
import { defineComponent, ref, computed, onMounted, openBlock, createElementBlock, normalizeStyle, unref } from "vue"; const captchaProps = { width: { type: [Number, String], default: 120 }, height: { type: [Number, String], default: 40 }, fontSize: { type: [Number, String], default: 14 }, borderColor: { type: String, default: "rgba(0,0,0,0.1)" }, bgColor: { type: String, default: "#fff" }, clickRefresh: { type: Boolean, default: true }, failRefresh: { type: Boolean, default: false } }; const colors = ["#0000FF", "#8A2BE2", "#A52A2A", "#DEB887", "#5F9EA0", "#7FFF00", "#D2691E", "#FF7F50", "#6495ED", "#DC143C"]; const operators = ["+", "-", "\xD7", "\xF7"]; const getRandomInt = (max) => { return Math.floor(Math.random() * max); }; const getOperator = () => { return operators[getRandomInt(operators.length)]; }; const getNumber = () => { return getRandomInt(10); }; const getSecondNumber = (operator) => { let num = getNumber(); if (operator === "\xF7" && num === 0) { return getSecondNumber(operator); } return num; }; class Captcha { constructor(canvasElement, width, height) { this.result = ""; this.context = canvasElement.getContext("2d"); canvasElement.width = width; canvasElement.height = height; this.width = width; this.height = height; this.init(); } init() { this.context.textBaseline = "top"; } refresh() { this.context.clearRect(0, 0, this.width, this.height); let operator = getOperator(); let num1 = getNumber(); let num2 = getSecondNumber(operator); let x = this.width / 3; let fontSize = Math.max(getRandomInt(this.height), 14); let numContent1 = { x: getRandomInt(Math.floor(x - fontSize)), y: getRandomInt(Math.floor(this.height - fontSize)), text: num1, color: colors[getRandomInt(colors.length)], fontSize }; fontSize = Math.max(getRandomInt(this.height), 14); let operatorContent = { x: x + getRandomInt(Math.floor(x - fontSize)), y: getRandomInt(Math.floor(this.height - fontSize)), text: operator, color: colors[getRandomInt(colors.length)], fontSize }; fontSize = Math.max(getRandomInt(this.height), 14); let numContent2 = { x: x * 2 + getRandomInt(Math.floor(x - fontSize)), y: getRandomInt(Math.floor(this.height - fontSize)), text: num2, color: colors[getRandomInt(colors.length)], fontSize }; this.result = this.compute(operator, num1, num2); this.draw([numContent1, operatorContent, numContent2]); } compute(operator, num1, num2) { let result = 0; switch (operator) { case "+": result = num1 + num2; break; case "-": result = num1 - num2; break; case "\xD7": result = num1 * num2; break; case "\xF7": result = num1 / num2; } return result.toFixed(2); } check(value) { let val = Number(value); if (isNaN(val)) { return false; } return val.toFixed(2) === this.result; } draw(contents) { let ctx = this.context; for (let i = 0; i < contents.length; i++) { let content = contents[i]; ctx.fillStyle = content.color; ctx.font = content.fontSize + "px serif"; ctx.fillText(content.text + "", content.x, content.y); } } } const _sfc_main = /* @__PURE__ */ defineComponent({ props: captchaProps, setup(__props, { expose }) { const props = __props; const canvas = ref(null); const style = computed(() => { let style2 = { borderWidth: "1px", borderStyle: "solid", borderColor: props.borderColor, width: "auto", height: "auto", background: props.bgColor, cursor: "default" }; style2.width = props.width + (typeof props.width === "number" ? "px" : ""); style2.height = props.height + (typeof props.height === "number" ? "px" : ""); if (props.clickRefresh) { style2.cursor = "pointer"; } return style2; }); let captch = null; onMounted(() => { captch = new Captcha(canvas.value, Number(props.width), Number(props.height)); captch.refresh(); }); const refresh = () => { captch == null ? void 0 : captch.refresh(); }; const check = (code) => { let isSuccess = captch ? captch.check(code) : false; if (!isSuccess && props.failRefresh) { refresh(); } return isSuccess; }; expose({ refresh, check }); const handleClick = () => { if (props.clickRefresh) { refresh(); } }; return (_ctx, _cache) => { return openBlock(), createElementBlock("canvas", { onClick: handleClick, style: normalizeStyle(unref(style)), ref_key: "canvas", ref: canvas }, null, 4); }; } }); _sfc_main.install = (app) => { app.component("Captcha", _sfc_main); }; export { captchaProps, _sfc_main as default };