| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- <template>
- <div class="cap-wrap" @click="refresh" :title="'点击刷新验证码'">
- <canvas ref="cv"></canvas>
- </div>
- </template>
- <script>
- export default {
- name: "CaptchaCanvas",
- props: { value: String },
- data() { return { code: "" }; },
- mounted() {
- this.refresh();
- if (typeof ResizeObserver !== "undefined") {
- this.ro = new ResizeObserver(() => this.draw());
- this.ro.observe(this.$el);
- }
- },
- beforeDestroy() {
- if (this.ro) { this.ro.disconnect(); this.ro = null; }
- },
- methods: {
- randChar() {
- const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
- return chars[Math.floor(Math.random() * chars.length)];
- },
- refresh() {
- this.code = Array.from({ length: 4 }).map(() => this.randChar()).join("");
- this.$emit("input", this.code);
- this.draw();
- },
- draw() {
- const cv = this.$refs.cv;
- if (!cv) return;
- const wrap = this.$el;
- const w = wrap.clientWidth || 120;
- const h = wrap.clientHeight || 60;
- const dpr = window.devicePixelRatio || 1;
- cv.width = Math.round(w * dpr);
- cv.height = Math.round(h * dpr);
- cv.style.width = w + "px";
- cv.style.height = h + "px";
- const ctx = cv.getContext("2d");
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
- // 背景
- ctx.clearRect(0, 0, w, h);
- const g = ctx.createLinearGradient(0, 0, w, h);
- g.addColorStop(0, "rgba(14,45,120,0.65)");
- g.addColorStop(1, "rgba(8,20,55,0.85)");
- ctx.fillStyle = g;
- ctx.fillRect(0, 0, w, h);
- // 干扰线
- for (let i = 0; i < 5; i++) {
- ctx.strokeStyle = `rgba(60,180,255,${0.15 + Math.random() * 0.25})`;
- ctx.beginPath();
- ctx.moveTo(Math.random() * w, Math.random() * h);
- ctx.lineTo(Math.random() * w, Math.random() * h);
- ctx.stroke();
- }
- // 字符:按容器宽高等分布点 + 字号随高度缩放
- const n = this.code.length;
- const fontSize = Math.max(12, Math.round(h * 0.55));
- ctx.font = `bold ${fontSize}px Arial`;
- ctx.textBaseline = "middle";
- ctx.textAlign = "center";
- for (let i = 0; i < n; i++) {
- const x = (w * (i + 0.5)) / n;
- const y = h / 2 + (Math.random() * 6 - 4);
- ctx.save();
- ctx.translate(x, y);
- ctx.rotate(((Math.random() * 14 - 7) * Math.PI) / 180);
- ctx.fillStyle = "rgba(210,245,255,0.92)";
- ctx.fillText(this.code[i], 0, 0);
- ctx.restore();
- }
- }
- }
- };
- </script>
- <style scoped>
- .cap-wrap {
- width: 100%;
- height: 100%;
- border: 1px solid #3D6EB8;
- border-radius: 8px;
- overflow: hidden;
- cursor: pointer;
- box-sizing: border-box;
- }
- canvas { display: block; }
- </style>
|