头像
YIBI

正在加载...

WindChime 风铃组件

2026-06-11Vue组件物理模拟Canvas

概述

WindChime 是一个纯客户端 Vue 组件,模拟绳子悬挂的风铃效果。结构为:挂点 → 绳子 → 玻璃球 → 玻璃棍 → 卡片,支持拖拽交互、风力模拟和自定义卡片内容。

物理模型

组件使用摆锤链(pendulum chain)物理模型:

  1. 玻璃球:弹簧-阻尼绳悬挂,支持拖拽投掷
  2. 玻璃棍:通过摆锤力矩从球底部悬挂,具有转动惯量(滞后响应),弹性边界触顶反弹
  3. 卡片:通过短绳从棍底端悬挂,转动惯量比棍更大(明显滞后),同样具有弹性边界

棍子和卡片不是直接跟随球移动,而是通过力矩传递逐级响应——球位移 → 棍偏转 → 卡片偏转,产生真实的延迟和回荡效果。


使用示例

基础用法

vue
<template>
  <div>
    <h1>页面</h1>

    <ClientOnly>
      <WindChime />
    </ClientOnly>
  </div>
</template>

带自定义参数和卡片内容

vue
<template>
  <div>
    <ClientOnly>
      <WindChime
        :rest-rope-length="180"
        :ball-size="80"
        ball-image="/images/crystal-ball.png"
        :stick-length="60"
        :anchor-offset-x="70"
        :spring-constant="0.04"
        :damping="0.96"
        :enable-wind="true"
        :wind-intensity="1.5"
      >
        <!-- 自定义卡片内容插槽 -->
        <template #default>
          <div style="text-align: center;">
            <span style="font-size: 2rem;">🎋</span>
            <p style="margin: 4px 0; color: #4a6741;">许愿签</p>
            <p style="font-size: 0.7rem; color: #666;">心想事成</p>
          </div>
        </template>
      </WindChime>
    </ClientOnly>
  </div>
</template>

Props 详细说明

属性名 类型 默认值 说明
ballImage String '' 玻璃球内显示的图片 URL(可选)
ballSize Number 72 玻璃球直径,单位 px
ballStyle Object {} 传递给玻璃球的额外 CSS 样式
stickLength Number 50 玻璃棍高度,单位 px
cardWidth Number 90 卡片宽度,单位 px
cardHeight Number 120 卡片高度,单位 px
cardBgColor String 'rgba(255,255,255,0.92)' 卡片背景色
restRopeLength Number 160 静止时绳子长度(挂点到球顶部),单位 px
anchorOffsetX Number 50 挂点水平位置,视口宽度百分比(vw),50 即居中
anchorOffsetY Number 25 挂点垂直位置,绳子起点距容器顶部的距离,单位 px
positionMode String 'absolute' 定位模式:'absolute'(随页面滚动消失)或 'fixed'(始终固定)
springConstant Number 0.035 弹簧系数,越大回弹力越强、摆动越快
damping Number 0.965 球阻尼系数(每帧速度保留率),越接近 1 摆动越久
gravity Number 0.35 重力加速度,影响下垂程度
enableWind Boolean true 是否启用风吹效果
windIntensity Number 1.0 风力强度倍数
zIndex Number 1000 CSS z-index 层级

技术要点

  • 纯客户端组件:依赖浏览器 DOM 和 requestAnimationFrame,务必用 <ClientOnly> 包裹,避免 SSR 报错
  • 多实例支持:同一页面可放置多个 <WindChime>,调整 anchorOffsetX 避免重叠
  • 插槽:卡片内容完全由默认插槽控制,支持任意 HTML 或 Vue 组件
  • 性能requestAnimationFrame 持续运行物理模拟,页面隐藏或销毁时自动停止
  • TypeScript:组件使用 <script setup lang="ts">,函数参数含类型注解