Просмотр исходного кода

验证码自适应容器尺寸,修复缩放失真

  - CaptchaCanvas 改为按容器 clientWidth/Height × DPR 绘制,HiDPI 下不再模糊
  - 字符按等分点排布、字号随高度缩放,容器尺寸变化时位置/字号同步
  - 加 ResizeObserver,外层尺寸响应式后无需再改组件
  - 删掉 .cap-wrap 写死的 165×54,由外层 class 决定尺寸
  - 移除未使用的 w/h props
画安 недель назад: 3
Родитель
Сommit
b4ffbb39e6
2 измененных файлов с 49 добавлено и 21 удалено
  1. 2 0
      .gitignore
  2. 47 21
      src/components/CaptchaCanvas.vue

+ 2 - 0
.gitignore

@@ -1,6 +1,8 @@
 .DS_Store
 node_modules
 /dist
+dist.zip
+*.zip
 
 
 # local env files

+ 47 - 21
src/components/CaptchaCanvas.vue

@@ -1,15 +1,24 @@
 <template>
   <div class="cap-wrap" @click="refresh" :title="'点击刷新验证码'">
-    <canvas ref="cv" :width="w" :height="h"></canvas>
+    <canvas ref="cv"></canvas>
   </div>
 </template>
 
 <script>
 export default {
   name: "CaptchaCanvas",
-  props: { value: String, w: { type: Number, default: 173 }, h: { type: Number, default: 64 } },
+  props: { value: String },
   data() { return { code: "" }; },
-  mounted() { this.refresh(); },
+  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";
@@ -18,37 +27,53 @@ export default {
     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, this.w, this.h);
-      const g = ctx.createLinearGradient(0, 0, this.w, this.h);
+      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, this.w, this.h);
+      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.strokeStyle = `rgba(60,180,255,${0.15 + Math.random() * 0.25})`;
         ctx.beginPath();
-        ctx.moveTo(Math.random() * this.w, Math.random() * this.h);
-        ctx.lineTo(Math.random() * this.w, Math.random() * this.h);
+        ctx.moveTo(Math.random() * w, Math.random() * h);
+        ctx.lineTo(Math.random() * w, Math.random() * h);
         ctx.stroke();
       }
 
-      // 字符
-      ctx.font = "bold 22px Arial";
+      // 字符:按容器宽高等分布点 + 字号随高度缩放
+      const n = this.code.length;
+      const fontSize = Math.max(12, Math.round(h * 0.55));
+      ctx.font = `bold ${fontSize}px Arial`;
       ctx.textBaseline = "middle";
-      for (let i = 0; i < this.code.length; i++) {
-        const x = 22 + i * 32;
-        const y = this.h / 2 + (Math.random() * 6 - 4);
+      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.rotate(((Math.random() * 14 - 7) * Math.PI) / 180);
         ctx.fillStyle = "rgba(210,245,255,0.92)";
-        ctx.fillText(this.code[i], -8, 0);
+        ctx.fillText(this.code[i], 0, 0);
         ctx.restore();
       }
     }
@@ -57,13 +82,14 @@ export default {
 </script>
 
 <style scoped>
-.cap-wrap{
-  width: 165px;
-  height: 54px;
+.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>
+canvas { display: block; }
+</style>