npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cat-paw-cup-react

v0.2.4

Published

Draggable cat paw cup with chili drops and classic overflow, extracted from the original blog experience.

Readme

cat-paw-cup-react

可拖动的猫爪杯 React 组件,提供礼物掉落、物理碰撞和爆杯动画。支持全屏悬浮、容器内展示、自定义礼物图片,以及通过参数和 ref 控制交互。

安装

在已有 React 项目中安装组件和 Motion:

npm install cat-paw-cup-react motion@^12.38.0

环境要求:

  • React 和 React DOM:18 或 19,使用相同主版本。
  • Motion:12.x,最低 12.38.0。
  • Node.js:20.19.0 或更高。
  • 模块格式:ESM,支持 TypeScript 类型提示。

组件样式需要单独导入:

import 'cat-paw-cup-react/style.css'

默认杯子和礼物图片已包含在包中,无需复制素材。使用 CSP 的网站需在 img-src 中允许 data:;使用远程自定义图片时,还需允许对应图片域名。

快速开始

import { CatPawCupScene } from 'cat-paw-cup-react'
import 'cat-paw-cup-react/style.css'

export default function App() {
  return <CatPawCupScene />
}

默认行为:

  • 在整个视口中显示一个可拖动的杯子。
  • 素材准备完成后等待 1000ms,再淡入 450ms。
  • 自动掉落 100 个内置礼物;后续每次默认掉落也是 100 个。
  • 每轮累计生成 300 个礼物时触发爆杯,再开始下一轮计数。
  • 默认水平居中并贴齐场景底部;窄屏下可能因右下角预留区而上移。

点击按钮触发掉落

heartSignal 是递增的触发信号。每增加 1,就请求一次 dropQuantity 数量的掉落。

import { useState } from 'react'
import { CatPawCupScene } from 'cat-paw-cup-react'
import 'cat-paw-cup-react/style.css'

export default function InteractiveCup() {
  const [signal, setSignal] = useState(0)

  return (
    <>
      <button
        type="button"
        style={{ position: 'fixed', right: 24, bottom: 40, zIndex: 90 }}
        onClick={() => setSignal(value => value + 1)}
      >
        添加礼物
      </button>
      <CatPawCupScene
        heartSignal={signal}
        autoDrop={false}
        dropQuantity={20}
      />
    </>
  )
}

信号应从 0 开始,不要传入数据库中的历史累计数量。信号减少时只重设基准,不会移除礼物。也可以不传 heartSignal,改用下方的 ref 方法。

容器内展示与主动控制

父容器必须有明确的宽度和高度。下面的示例可以直接使用内置素材:

import { useRef } from 'react'
import { CatPawCupScene, type CatPawCupSceneHandle } from 'cat-paw-cup-react'
import 'cat-paw-cup-react/style.css'

export default function GiftBox() {
  const cup = useRef<CatPawCupSceneHandle>(null)

  return (
    <>
      <button type="button" onClick={() => { void cup.current?.drop().catch(console.error) }}>
        添加一批
      </button>
      <button type="button" onClick={() => { void cup.current?.drop(7).catch(console.error) }}>
        添加 7 个
      </button>
      <button type="button" onClick={() => cup.current?.clear()}>清空</button>
      <button type="button" onClick={() => cup.current?.reset()}>重置</button>
      <button type="button" onClick={() => { void cup.current?.overflow().catch(console.error) }}>
        爆杯
      </button>

      <div style={{ width: '100%', height: 560 }}>
        <CatPawCupScene
          ref={cup}
          mode="container"
          autoDrop={false}
          dropQuantity={20}
          overflowThreshold={150}
          cupScale={0.8}
          onError={error => console.error(error)}
        />
      </div>
    </>
  )
}

容器模式会跟随父容器尺寸变化,并裁剪超出容器的图形。容器只有底部物理边界,顶部和两侧没有物理墙壁。可以同时使用多个独立容器;全屏模式通常只挂载一个实例。

位置、缩放与底部对齐

initialPosition 表示杯子左上角,单位为场景内的 CSS 像素:全屏模式以视口左上角为原点,容器模式以容器左上角为原点。

<CatPawCupScene
  mode="container"
  cupScale={0.8}
  initialPosition={{ x: 80, y: 120 }}
/>

杯子设计尺寸为 305 × 323。cupScale 是期望缩放上限;可用空间不足时,组件会继续缩小杯子。

未设置 initialPosition 时,杯子水平居中、底部贴齐场景。向下拖动可以到达底部,reset() 会恢复当前配置的初始位置。显式传入的位置会被限制在可拖动范围内。

全屏模式在视口宽度小于 640px 时,默认避让右下角宽 104px、高 120px 的区域。杯子与该区域重叠时会向上移,并与该区域保留 8px 距离。需要关闭避让时:

<CatPawCupScene reserveHeartArea={false} />

关闭后应由使用方安排页面按钮的位置,避免与杯子重叠。容器模式不使用这个预留区。

自定义礼物

<CatPawCupScene
  gift={{
    image: '/images/star.png',
    size: 24,
    collisionRadius: 12,
    restitution: 0.3,
  }}
/>

请将图片路径替换为项目中可访问的资源。图片保持纵横比,size 指最长边;collisionRadius 指礼物之间的圆形碰撞半径,杯壁碰撞另按图片旋转包络处理。透明留白较大的图片建议先裁掉多余留白。

自定义图片加载失败、超时或被跨域策略拒绝时,组件会调用 onError,并回退到内置礼物图片及其默认物理配置。

CupGift 参数

| 属性 | 默认值 | 含义 | | --- | --- | --- | | image | 内置辣条 | 相对路径、HTTP(S) 或 data URL | | size | 18.3 | scale=1 时图片最长边(CSS px) | | collisionRadius | size / 2 | scale=1 时礼物间碰撞半径 | | mass | 原辣条质量 | 质量,实际按 scale² 缩放 | | friction | 原辣条配置 | 摩擦系数,0–1 | | airFriction | 原辣条配置 | 空气阻力,0–1 | | restitution | 原辣条配置 | 弹性,0–1 |

组件参数

所有参数均可选。下表中的默认值适用于未传入对应参数的情况。

| 参数 | 类型 / 默认值 | 含义 | | --- | --- | --- | | heartSignal | number / 0 | 单调递增的点击信号;减少时只重设基准 | | autoDrop | boolean / true | 挂载或 reset 后是否自动掉落 | | initialQuantity | number / 100 | 自动掉落数量,可为 0 | | dropQuantity | number / 100 | 每次点击、无参数 drop() 的数量,可为 0 | | overflowThreshold | number / 300 | 每轮累计生成数量的爆杯阈值,包含自动掉落和杯外礼物 | | cupScale | number / 1 | 设计尺寸 305 × 323 的缩放上限,会继续缩小以适配场景 | | initialPosition | { x, y } / 水平居中,底部贴齐场景 | 杯子左上角,场景内 CSS 像素,自动限制到可拖范围;移动端预留区可能进一步上移 | | spawnPosition | 'cup' \| { x, y } / 'cup' | 礼物出生点;坐标是场景内 CSS 像素,可在杯外 | | spawnOffset | { x, y } / { x: 0, y: -200 } | 相对杯口中心的设计像素偏移,乘杯子实际 scale;仅 cup 模式生效 | | gift | CupGift / 辣条 | 见下表 | | draggable | boolean / true | 是否允许拖动 | | paused | boolean / false | 暂停物理、掉落和爆杯;新命令保留在队列中 | | mode | 'viewport' \| 'container' / 'viewport' | 全屏悬浮或填满当前父容器 | | reserveHeartArea | boolean / true | 全屏且宽度小于 640px 时,给右下角爱心留空;容器模式不留空 | | entryDelayMs | number / 1000 | 素材准备好后的入场等待;随后淡入 450ms | | dropGapMs | number / 10 | 同批两个礼物生成间隔,毫秒 | | gravity | number / 1 | 重力系数,0 表示无重力 | | zIndex | number / 80 | Canvas 层级;拖动层 +1,提示层 +2 | | className | string | 容器根节点 / 全屏 Canvas 层的附加类 | | style | CSSProperties | 容器根节点样式,仅 container 模式使用 | | onReady | () => void | 场景初始化完成,可调用 ref;无需等待它才能排队 | | onError | (error: Error) => void | 资源失败等错误;自定义图片失败是可恢复错误 | | onOverflowStart | () => void | 每次自动 / 手动爆杯开始 | | onOverflowEnd | () => void | 爆杯与杯子恢复均完成;取消时不触发 |

数值范围

  • 数量取整并限制在 0–10000;爆杯阈值至少为 1。
  • cupScale 限制在 0.05–5;实际尺寸仍受场景空间限制。
  • 礼物尺寸限制在 1–512,重力限制在 0–10。
  • 非法数值回退到默认值。
  • 固定出生点可以在场景外,不会自动限制到可见区域。
  • spawnPosition="cup" 在每批开始时确定出生点,不会在该批掉落期间持续追踪杯子。

Ref 方法与执行顺序

| 方法 | 返回值 | 行为 | | --- | --- | --- | | drop(quantity?) | Promise<void> | 将掉落请求加入队列;省略数量时使用 dropQuantity。 | | overflow() | Promise<void> | 排在已有请求之后,执行一次爆杯并重置本轮计数。 | | clear() | void | 取消当前与排队中的请求,清除礼物和特效,计数归零;保持杯子位置,不自动掉落。 | | reset() | void | 清空场景并恢复当前 initialPosition,根据 autoDrop 和 initialQuantity 决定是否重新自动掉落。 |

掉落请求按顺序处理,达到阈值时拆分批次。例如,阈值为 10、请求 25 个礼物,会执行:

掉落 10 个 → 爆杯 → 掉落 10 个 → 爆杯 → 掉落 5 个

drop() 的 Promise 在整个请求及其触发的爆杯完成后兑现,不表示所有礼物已经落稳。clear()、reset() 或卸载取消请求时,相关 Promise 正常兑现;执行错误会 reject,并调用 onError。调用返回 Promise 的方法时应处理错误。

参数更新规则

| 修改内容 | 生效方式 | | --- | --- | | 缩放、重力、掉落间隔、暂停、拖动开关 | 更新后立即生效。 | | 礼物图片与物理配置 | 下一批生效;已有礼物保留其图片。 | | dropQuantity | 每次请求入队时确定数量。 | | overflowThreshold | 在下一个批次分段边界读取。 | | autoDrop、initialQuantity、initialPosition | 不会自动重放;调用 reset() 后按新配置重新开始。 | | mode | 重建场景。 |

仅重新创建回调函数或内容相同的配置对象,不会重建整个物理场景。

暂停与减少动态效果

paused={true} 或标签页隐藏时,物理模拟、掉落和爆杯暂停;恢复后继续执行,不补跑暂停期间的时间。暂停期间的新命令保留在队列中。

系统开启“减少动态效果”时,组件先显示“启用互动”按钮。用户启用后才执行等待中的 ref 命令;启用前的 heartSignal 信号会被忽略。autoDrop={false} 只控制自动掉落,不会关闭这项无障碍行为。

Next.js App Router

在客户端组件中使用 next/dynamic 延迟加载,并关闭该组件的服务端渲染:

'use client'

import dynamic from 'next/dynamic'
import 'cat-paw-cup-react/style.css'

const CatPawCupScene = dynamic(() => import('cat-paw-cup-react'), { ssr: false })

export default function Cup() {
  return <CatPawCupScene />
}

在需要显示组件的页面或布局中引用 Cup。如果要随页面切换重新初始化,可改变组件的 key;如果要保留场景,可将其挂载在共享布局中。

常见问题

组件没有样式或拖动区域不正确? 确认已导入 cat-paw-cup-react/style.css。

容器模式没有显示? 确认父容器有明确宽高,并检查资源错误回调。

窄屏下杯子没有贴底? 检查 reserveHeartArea;默认会避让右下角区域。显式设置的 initialPosition 也会影响初始位置。

改了初始位置或首次掉落数量,为什么没有重播? 这些参数不会触发自动重置;通过 ref 调用 reset()。

没有传入交互信号也会掉落? autoDrop 默认为 true;需要完全由按钮或 ref 控制时,将其设为 false。

版本记录 · 来源与许可说明