-
-
Notifications
You must be signed in to change notification settings - Fork 45
/
Copy pathSquaresBackground.vue
112 lines (91 loc) · 2.95 KB
/
SquaresBackground.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<script setup lang="ts">
// Source: https://www.reactbits.dev/backgrounds/squares
// converted to Vue
const props = withDefaults(
defineProps<{
speed?: number
borderColor?: string
squareSize?: number
}>(),
{
speed: 0.5,
borderColor: "#999",
squareSize: 40,
},
)
const gridOffset = ref({ x: 0, y: 0 })
const requestRef = ref<number | null>(null)
const numSquaresX = ref(0)
const numSquaresY = ref(0)
const canvas = ref<HTMLCanvasElement>()
const ctx = ref<CanvasRenderingContext2D>()
onMounted(() => {
canvas.value = document.querySelector("canvas") as HTMLCanvasElement
if (!canvas.value) return
ctx.value = canvas.value.getContext("2d")!
if (!ctx.value) return
resizeCanvas()
window.addEventListener("resize", resizeCanvas)
requestRef.value = requestAnimationFrame(updateAnimation)
})
onUnmounted(() => {
window.removeEventListener("resize", resizeCanvas)
if (requestRef.value) cancelAnimationFrame(requestRef.value)
})
const resizeCanvas = () => {
if (!canvas.value) return
canvas.value.width = canvas.value.offsetWidth
canvas.value.height = canvas.value.offsetHeight
numSquaresX.value = Math.ceil(canvas.value.width / props.squareSize) + 1
numSquaresY.value = Math.ceil(canvas.value.height / props.squareSize) + 1
}
const drawGrid = () => {
if (!canvas.value || !ctx.value) return
const context = ctx.value
context.clearRect(0, 0, canvas.value.width, canvas.value.height)
const startX =
Math.floor(gridOffset.value.x / props.squareSize) * props.squareSize
const startY =
Math.floor(gridOffset.value.y / props.squareSize) * props.squareSize
for (
let x = startX;
x < canvas.value.width + props.squareSize;
x += props.squareSize
) {
for (
let y = startY;
y < canvas.value.height + props.squareSize;
y += props.squareSize
) {
const squareX = x - (gridOffset.value.x % props.squareSize)
const squareY = y - (gridOffset.value.y % props.squareSize)
context.strokeStyle = props.borderColor
context.strokeRect(squareX, squareY, props.squareSize, props.squareSize)
}
}
const gradient = context.createRadialGradient(
canvas.value.width / 2,
canvas.value.height / 2,
0,
canvas.value.width / 2,
canvas.value.height / 2,
Math.hypot(canvas.value.width / 2, canvas.value.height / 2),
)
gradient.addColorStop(0, "rgba(0, 0, 0, 0)")
gradient.addColorStop(1, "#171717")
context.fillStyle = gradient
context.fillRect(0, 0, canvas.value.width, canvas.value.height)
}
const updateAnimation = () => {
const effectiveSpeed = Math.max(props.speed, 0.1)
gridOffset.value.x =
(gridOffset.value.x - effectiveSpeed + props.squareSize) % props.squareSize
gridOffset.value.y =
(gridOffset.value.y - effectiveSpeed + props.squareSize) % props.squareSize
drawGrid()
requestRef.value = requestAnimationFrame(updateAnimation)
}
</script>
<template>
<canvas ref="canvas" class="w-full h-full border-none block" />
</template>