需求,小而美
<PasswordResult :passwords="[1, 2, 3, 6, 8, 4, 7]" width="300"/>
开整:
- 先这样
- 再这样
- 计算角度
- 搞定
<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>