Skip to content

VitePress 首页随机生成花里胡哨的背景图

实现

新建一个组件,放在首页某个插槽里。如放在 #home-hero-after 中。

首先在 template 中建立 Canvas 画布,并定义好样式:

.vitepress/theme/components/Canvas

vue
<template>
  <canvas
    class="canvas"
    ref="canvasRef"
    @mousedown="handleMouseDown"
    @mousemove="handleMouseMove"
    @mouseup="handleMouseUp"
  />
</template>

<script>
const getRandomInt = (min, max) => {
  return Math.floor(Math.random() * (max - min) + min)
}
export default {
  name: 'Canvas',
  props: {
    globalCompositeOperation: {
      type: String,
      default: 'lighter',
    },
  },
  data() {
    return {
      ctx: null,
    }
  },
  methods: {
    initCanvas() {
      const canvas = this.$refs.canvasRef
      const width = window.innerWidth
      const height = window.innerHeight
      canvas.width = width
      canvas.height = height
      this.ctx = canvas.getContext('2d')
      this.ctx.clearRect(0, 0, width, height)
      if (this.globalCompositeOperation) {
        this.ctx.globalCompositeOperation = this.globalCompositeOperation
      }
    },
    generateParams() {
      const width = window.innerWidth
      const height = window.innerHeight
      const emptyX = width > 400 ? 50 : 0
      const emptyYFront = height > 550 ? 300 : 50
      const emptyYRear = height > 640 ? 120 : 0
      const r = getRandomInt(10, 25)
      const x = getRandomInt(emptyX, width - r * 2 - emptyX)
      const y = getRandomInt(emptyYFront, height - r * 2 - emptyYRear)
      const red = getRandomInt(0, 255)
      const green = getRandomInt(0, 255)
      const blue = getRandomInt(0, 255)
      const opacity = Math.random() * 0.05 + 0.05
      const color = `rgba(${red}, ${green}, ${blue}, ${opacity})`
      const isStroke = Math.random() > 0.5
      return { x, y, r, color, isStroke }
    },
    drawRect() {
      const { x, y, r, color, isStroke } = this.generateParams()
      if (isStroke) {
        this.ctx.strokeStyle = color
        this.ctx.strokeRect(x, y, r * 2, r * 2)
      } else {
        this.ctx.fillStyle = color
        this.ctx.fillRect(x, y, r * 2, r * 2)
      }
    },
    drawArc() {
      const { x, y, r, color, isStroke } = this.generateParams()
      this.ctx.beginPath()
      if (isStroke) {
        this.ctx.strokeStyle = color
        this.ctx.arc(x, y, r, 0, 2 * Math.PI)
        this.ctx.stroke()
      } else {
        this.ctx.fillStyle = color
        this.ctx.arc(x, y, r, 0, 2 * Math.PI)
        this.ctx.fill()
      }
      this.ctx.closePath()
    },
    resize() {
      this.initCanvas()
      const width = window.innerWidth
      const density = width > 640 ? 50 : 15
      for (let i = 0; i < density; i++) {
        this.drawArc()
      }
      for (let i = 0; i < density; i++) {
        this.drawRect()
      }
    },
  },
  mounted() {
    this.resize()
    this.resize = this.resize.bind(this)
    window.addEventListener('resize', this.resize)
  },
  beforeDestory() {
    window.removeEventListener('resize', this.resize)
  },
}
</script>

<style scoped>
.canvas {
  width: 100%;
  height: 100%;
  display: block;
  background: transparent;
  position: fixed;
  left: 0;
  top: 0;
  z-index: -1;
}
</style>

.vitepress/theme/components/Hero

vue
<script setup>
import Canvas from './Canvas.vue'
</script>

<template>
  <Canvas />
</template>

.vitepress/theme/components/Layout

vue
<script setup>
import DefaultTheme from 'vitepress/theme'
import Hero from './Hero.vue'
import Utterances from './Utterances.vue'

const { Layout } = DefaultTheme
</script>

<template>
  <Layout>
    <template #home-hero-after>
      <Hero />
    </template>

    <template #doc-after>
      <Utterances />
    </template>
  </Layout>
</template>

.vitepress/theme/index.ts

typescript
import { type Theme } from 'vitepress'
import { AntdTheme } from 'vite-plugin-vitepress-demo/theme'
import DefaultTheme from 'vitepress/theme'
// @ts-ignore
import Utterances from './components/Utterances.vue'
// @ts-ignore
import Layout from './components/Layout.vue'
import 'uno.css'
import './styles/index.css'

export default {
  ...DefaultTheme,
  Layout,
  enhanceApp({ app }) {
    app.component('Demo', AntdTheme)
    app.component('Utterances', Utterances)
  },
  setup() {},
} as Theme

Released under the MIT License.