用SVG画个9宫格密码展示

198 阅读1分钟

需求,小而美

<PasswordResult :passwords="[1, 2, 3, 6, 8, 4, 7]" width="300"/>

image.png

开整:

  1. 先这样
  2. 再这样
  3. 计算角度
  4. 搞定
<script setup lang="ts">
import type { PropType } from "vue"

const { passwords } = defineProps({
    passwords: {
        type: Array as PropType<number[]>,
        default: () => []
    }
})

const rotateFrom3Points = (point1: [number, number], point2: [number, number]) => {
    const [x1, y1] = point1
    const [x2, y2] = point2
    return Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI
}

/*
30  (60, 60) 30 (60, 60) 30 (60, 60) 30 = 480
* */
const matrix = [
    [80, 80], [240, 80], [400, 80],
    [80, 240], [240, 240], [400, 240],
    [80, 400], [240, 400], [400, 400]
] as [number, number][]

const select = new Set(passwords)
const lines = [] as [number, number, number, number][]
for (let i = 0; i < passwords.length - 1; i++) {
    const start = matrix[passwords[i] - 1]
    const end = matrix[passwords[i + 1] - 1]
    lines.push([start[0], start[1], end[0], end[1]])
}
const triangles = [] as [number, number, number][]
for (let i = 0; i < passwords.length - 1; i++) {
    const point = matrix[passwords[i] - 1]
    const point2 = matrix[passwords[i + 1] - 1]
    triangles.push([point[0], point[1], rotateFrom3Points(point, point2) + 90])
}
</script>
<template>
    <svg class="dev_box" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 480">
        <g stroke="#bfefd2" stroke-width="25">
            <path v-for="([x1, y1, x2, y2], i) in lines" :key="i" :d="`M${x1},${y1} L${x2},${y2}`" />
        </g>
        <g fill="#e4faf1">
            <circle v-for="([x, y], i) in matrix" :key="i" :cx="x" :cy="y" r="55"
                    :fill="select.has(i + 1)? '#e4faf1': '#F3F3F3'" />
            <circle v-for="([x, y], i) in matrix" :key="i" :cx="x" :cy="y" r="20" fill="none"
                    :stroke="select.has(i + 1) ? '#2ba46e' : '#C5C5C5'" stroke-width="13" />
        </g>
        <g fill="#2ba46e">
            <polygon v-for="([x1, y1, r], i) in triangles" :key="i"
                     :points="`${x1 - 15},${y1 + 5 - 38} ${x1 + 15},${y1 + 5 - 38} ${x1},${y1 - 15 - 35}`"
                     :transform="`rotate(${r},${x1},${y1})`" />
        </g>
    </svg>
</template>