增加背景样式种类

This commit is contained in:
2025-12-06 12:55:26 +08:00
parent b9691b0965
commit f41e9757bf
4 changed files with 415 additions and 11 deletions
@@ -0,0 +1,206 @@
<template>
<canvas ref="canvas" class="bg-gradient"></canvas>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'
const canvas = ref<HTMLCanvasElement | null>(null)
let ctx: CanvasRenderingContext2D | null = null
let width = 0
let height = 0
let raf = 0
let running = true
// Mouse interaction
const mouse = { x: 0, y: 0, tx: 0, ty: 0 }
function parseColor(c: string): [number, number, number] {
if (!c) return [0, 0, 0]
if (c.startsWith('#')) {
const v = c.slice(1)
const n = v.length === 3 ? v.split('').map(ch => ch + ch).join('') : v
const r = parseInt(n.slice(0, 2), 16)
const g = parseInt(n.slice(2, 4), 16)
const b = parseInt(n.slice(4, 6), 16)
return [r, g, b]
}
return [255, 255, 255]
}
function getThemeColors() {
const cs = getComputedStyle(document.documentElement)
const accent = parseColor(cs.getPropertyValue('--color-accent').trim())
const primary = parseColor(cs.getPropertyValue('--color-bg-primary').trim())
return { accent, primary }
}
function resize() {
if (!canvas.value) return
width = window.innerWidth
height = window.innerHeight
canvas.value.width = width
canvas.value.height = height
}
// Mesh points
interface Point {
x: number
y: number
originX: number
originY: number
noiseOffsetX: number
noiseOffsetY: number
}
const points: Point[] = []
const gridSize = 100 // Size of mesh cells
function initMesh() {
points.length = 0
const cols = Math.ceil(width / gridSize) + 2
const rows = Math.ceil(height / gridSize) + 2
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
const x = (i - 1) * gridSize
const y = (j - 1) * gridSize
points.push({
x,
y,
originX: x,
originY: y,
noiseOffsetX: Math.random() * 1000,
noiseOffsetY: Math.random() * 1000
})
}
}
}
function draw() {
if (!ctx) return
ctx.clearRect(0, 0, width, height)
// Smooth mouse
mouse.x += (mouse.tx - mouse.x) * 0.05
mouse.y += (mouse.ty - mouse.y) * 0.05
const time = performance.now() / 2000
const { accent, primary } = getThemeColors()
// Update points
points.forEach(p => {
// Simplex-like noise movement (simplified with sine/cos)
const noiseX = Math.sin(time + p.noiseOffsetX) * 30
const noiseY = Math.cos(time + p.noiseOffsetY) * 30
// Mouse influence (gentle push)
const dx = mouse.x - p.originX
const dy = mouse.y - p.originY
const dist = Math.sqrt(dx*dx + dy*dy)
const maxDist = 400
let mouseX = 0
let mouseY = 0
if (dist < maxDist) {
const force = Math.pow(1 - dist / maxDist, 2) * 40
const angle = Math.atan2(dy, dx)
mouseX = Math.cos(angle) * force
mouseY = Math.sin(angle) * force
}
p.x = p.originX + noiseX - mouseX
p.y = p.originY + noiseY - mouseY
})
// Draw mesh
ctx.lineWidth = 1
// Create gradient for lines
const gradient = ctx.createLinearGradient(0, 0, width, height)
gradient.addColorStop(0, `rgba(${accent[0]}, ${accent[1]}, ${accent[2]}, 0.05)`)
gradient.addColorStop(0.5, `rgba(${accent[0]}, ${accent[1]}, ${accent[2]}, 0.1)`)
gradient.addColorStop(1, `rgba(${accent[0]}, ${accent[1]}, ${accent[2]}, 0.05)`)
ctx.strokeStyle = gradient
const cols = Math.ceil(width / gridSize) + 2
const rows = Math.ceil(height / gridSize) + 2
// Draw connections
ctx.beginPath()
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
const index = i * rows + j
const p = points[index]
if (i < cols - 1) {
const right = points[(i + 1) * rows + j]
if (p) ctx.moveTo(p.x, p.y)
if (right) ctx.lineTo(right.x, right.y)
}
if (j < rows - 1) {
const bottom = points[i * rows + (j + 1)]
if (p) ctx.moveTo(p.x, p.y)
if (bottom) ctx.lineTo(bottom.x, bottom.y)
}
}
}
ctx.stroke()
// Fill subtle gradient background
const bgGradient = ctx.createRadialGradient(
mouse.x, mouse.y, 0,
mouse.x, mouse.y, Math.max(width, height)
)
bgGradient.addColorStop(0, `rgba(${accent[0]}, ${accent[1]}, ${accent[2]}, 0.03)`)
bgGradient.addColorStop(1, 'rgba(0,0,0,0)')
ctx.fillStyle = bgGradient
ctx.fillRect(0, 0, width, height)
}
function loop() {
if (!running) return
draw()
raf = requestAnimationFrame(loop)
}
function onMouseMove(e: MouseEvent) {
mouse.tx = e.clientX
mouse.ty = e.clientY
}
function onResize() {
resize()
initMesh()
}
onMounted(() => {
if (!canvas.value) return
ctx = canvas.value.getContext('2d')
resize()
initMesh()
window.addEventListener('resize', onResize)
window.addEventListener('mousemove', onMouseMove)
raf = requestAnimationFrame(loop)
})
onBeforeUnmount(() => {
running = false
cancelAnimationFrame(raf)
window.removeEventListener('resize', onResize)
window.removeEventListener('mousemove', onMouseMove)
})
</script>
<style scoped>
.bg-gradient {
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
}
</style>
@@ -0,0 +1,153 @@
<template>
<canvas ref="canvas" class="bg-grid"></canvas>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'
const canvas = ref<HTMLCanvasElement | null>(null)
let ctx: CanvasRenderingContext2D | null = null
let width = 0
let height = 0
let raf = 0
let running = true
// Mouse interaction
const mouse = { x: 0, y: 0, tx: 0, ty: 0 }
// Grid properties
const gridSize = 50 // Increased grid size for cleaner look
let offset = 0
function parseColor(c: string): [number, number, number] {
if (!c) return [0, 0, 0]
if (c.startsWith('#')) {
const v = c.slice(1)
const n = v.length === 3 ? v.split('').map(ch => ch + ch).join('') : v
const r = parseInt(n.slice(0, 2), 16)
const g = parseInt(n.slice(2, 4), 16)
const b = parseInt(n.slice(4, 6), 16)
return [r, g, b]
}
return [255, 255, 255]
}
function getThemeColors() {
const cs = getComputedStyle(document.documentElement)
const accent = parseColor(cs.getPropertyValue('--color-accent').trim())
const text = parseColor(cs.getPropertyValue('--color-text-primary').trim())
return { accent, text }
}
function resize() {
if (!canvas.value) return
width = window.innerWidth
height = window.innerHeight
canvas.value.width = width
canvas.value.height = height
}
function draw() {
if (!ctx) return
// Clear canvas
ctx.clearRect(0, 0, width, height)
// Smooth mouse
mouse.x += (mouse.tx - mouse.x) * 0.1
mouse.y += (mouse.ty - mouse.y) * 0.1
const { accent, text } = getThemeColors()
const time = performance.now() / 1000
// Moving grid effect
offset = (time * 20) % gridSize // Slower movement
ctx.lineWidth = 1
// Draw vertical lines
for (let x = 0; x <= width; x += gridSize) {
const dx = x - mouse.x
const dist = Math.abs(dx)
const maxDist = 400
let drawX = x
let alpha = 0.03 // Very subtle base opacity
if (dist < maxDist) {
const force = Math.cos((dist / maxDist) * Math.PI / 2)
drawX += (dx > 0 ? 1 : -1) * force * 15
alpha += force * 0.08 // Subtle highlight
}
const [r, g, b] = x % (gridSize * 4) === 0 ? accent : text
ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${alpha})`
ctx.beginPath()
ctx.moveTo(drawX, 0)
ctx.lineTo(drawX, height)
ctx.stroke()
}
// Draw horizontal lines
for (let y = offset; y <= height; y += gridSize) {
const dy = y - mouse.y
const dist = Math.abs(dy)
const maxDist = 400
let drawY = y
let alpha = 0.03
if (dist < maxDist) {
const force = Math.cos((dist / maxDist) * Math.PI / 2)
drawY += (dy > 0 ? 1 : -1) * force * 15
alpha += force * 0.08
}
const [r, g, b] = Math.abs(y - offset) % (gridSize * 4) < 1 ? accent : text
ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${alpha})`
ctx.beginPath()
ctx.moveTo(0, drawY)
ctx.lineTo(width, drawY)
ctx.stroke()
}
}
function loop() {
if (!running) return
draw()
raf = requestAnimationFrame(loop)
}
function onMouseMove(e: MouseEvent) {
mouse.tx = e.clientX
mouse.ty = e.clientY
}
onMounted(() => {
if (!canvas.value) return
ctx = canvas.value.getContext('2d')
resize()
window.addEventListener('resize', resize)
window.addEventListener('mousemove', onMouseMove)
raf = requestAnimationFrame(loop)
})
onBeforeUnmount(() => {
running = false
cancelAnimationFrame(raf)
window.removeEventListener('resize', resize)
window.removeEventListener('mousemove', onMouseMove)
})
</script>
<style scoped>
.bg-grid {
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
}
</style>