Files
2026-06-27 14:13:39 +08:00

4.2 KiB
Raw Permalink Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

本文件为 Claude Code 在本仓库中工作提供指引。请使用中文回复。

项目概述

基于 Leafer-UI 的 React 无限滑动画布组件(InfiniteCanvas)。卡片以网格平铺,支持四向无限环绕、拖拽平移、惯性滑动、响应式布局与点击回调。核心是一个可复用的组件库(src/components/InfiniteCanvas),src/App.tsx 仅是演示用例

技术栈:React 19 · TypeScript · Vite · Leafer-UI · Tailwind CSS v4 · pnpm

Leafer 文档查询:https://context7.com/leaferjs/ai-docs/llms.txt?tokens=10000

工作约定

  • 每次改动尽量小,大改动需要先与用户确认
  • 不要影响原有逻辑
  • 不要过度封装代码
  • 代码需要加上中文注释
  • 必要时需要更新README.md文件,该文件主要是说明如何使用,不需要说明太多技术细节

常用命令

pnpm install
pnpm dev      # 启动 Vite 开发服务器
pnpm build    # 先 tsc -b 类型检查,再 vite build 打包
pnpm preview  # 预览构建产物
pnpm lint     # ESLint 检查全仓库

注意:pnpm build 会先执行 tsc -b,类型错误会中断构建。没有配置测试框架

架构

组件分三层,关注点分离清晰:

  • InfiniteCanvas.tsx — 薄壳组件。只创建容器 div(默认填满父级、cursor: grabtouchAction: none)并把 containerRef 与 props 交给 hook。所有逻辑都不在这里
  • useInfiniteSlide.ts — 全部滑动引擎逻辑所在。这是改动行为时唯一需要重点理解的文件
  • types.tsInfiniteCanvasProps<T>Responsive<V> 类型定义。组件是泛型的,与卡片数据类型 T 强绑定

滑动引擎的关键设计(useInfiniteSlide

理解这些约束后再改动,否则容易引入画布重建或闪烁问题:

  1. 双 effect + ref 模式:结构性 effect 的依赖只有 [containerRef, items, renderCard, background],这四者变化才会销毁并重建整个 Leafer 画布。其余易变的配置(onCardClick / draggable / inertia / friction / 布局配置)都存在 ref 中,由一个无依赖的 passive effect 持续同步最新值,避免触发画布重建。新增 prop 时务必判断它该进依赖数组还是 ref
  2. renderCard 必须引用稳定:它在结构性 effect 依赖里,每次引用变化都会重建画布。调用方必须用 useCallback 包裹
  3. 签名去重layout() 会把解析后的布局值拼成 sig 字符串,与 lastSig 比对,无变化时直接 return,零开销。ResizeObserver 和布局配置变化都通过 layoutRef.current?.() 触发 layout(),靠签名去重避免无谓重建
  4. 无限环绕用取模 wrap:把每个节点坐标规整到 [-size, span - size) 区间,使屏幕任意位置都被卡片覆盖。平铺份数(repX/repY)保证总跨度 ≥ 视口 + 一个步距。连续 wrap(而非阈值跳变)消除 1px 闪烁
  5. Leafer 内置交互全部禁用move / zoom / wheeldisabled),改用自定义 PointerEvent.DOWN/MOVE/UP 实现拖拽与无限环绕
  6. 惯性滑动:松手时记录最近帧速度 vx/vy,用 requestAnimationFrame 每帧按 frictionRef 衰减,低于阈值(0.1)停止
  7. 点击命中用 PointerEvent.TAP:Leafer 自带拖拽阈值,平移后不会误触发点击
  8. 清理兼容 StrictMode 双调用:return 的清理函数会断开 ResizeObserver、停止惯性、解绑事件、销毁画布并清空 layoutRef

重要不变量

cardWidth / cardHeight 同时用于网格定位、无限环绕命中判定传给 renderCardsizerenderCard 实际绘制的尺寸必须与传入的 size 一致,否则环绕命中与视觉会错位。响应式场景下务必使用第三个参数 size 来绘制

响应式

布局类 propscolumns / cardWidth / cardHeight / gapX / gapY)均为 Responsive<V> 类型,即「固定值」或 (size) => V 函数。函数依容器宽高动态计算,由 hook 内的 resolve() 解析(返回非正数或非 number 时回退默认值)