vue3-captcha
Version:
a canvas captcha built in Vue3 with TypeScript
179 lines (178 loc) • 5 kB
JavaScript
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 };