Skip to content

Commit f56cd22

Browse files
committed
feat: 书籍列表
1 parent eff4db6 commit f56cd22

7 files changed

Lines changed: 827 additions & 2 deletions

File tree

src/components/stateless/InteractiveBook/InteractiveBook.mdx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,22 @@ import * as InteractiveBookStories from './InteractiveBook.stories'
8787
<Story of={InteractiveBookStories.MinimalMode} />
8888
</Canvas>
8989

90+
## 翻页音效(开启)
91+
92+
默认开启翻页音效(`enableSound={true}`),翻页时会播放纸张翻动的声音。点击、拖拽、键盘翻页均触发音效。
93+
94+
<Canvas>
95+
<Story of={InteractiveBookStories.SoundEnabled} />
96+
</Canvas>
97+
98+
## 翻页音效(禁用)
99+
100+
设置 `enableSound={false}` 禁用翻页音效,适合需要安静环境的场景。
101+
102+
<Canvas>
103+
<Story of={InteractiveBookStories.SoundDisabled} />
104+
</Canvas>
105+
90106
## Props(可调参数)
91107

92108
<ArgTypes />
@@ -222,6 +238,32 @@ function Example() {
222238
}
223239
```
224240

241+
### 翻页音效配置
242+
243+
```tsx
244+
import InteractiveBook from '@stateless/InteractiveBook'
245+
246+
function Example() {
247+
return (
248+
<>
249+
{/* 默认开启音效 */}
250+
<InteractiveBook
251+
coverImage="/cover.jpg"
252+
pages={pages}
253+
enableSound={true}
254+
/>
255+
256+
{/* 禁用音效 */}
257+
<InteractiveBook
258+
coverImage="/cover.jpg"
259+
pages={pages}
260+
enableSound={false}
261+
/>
262+
</>
263+
)
264+
}
265+
```
266+
225267
## 页面数据结构
226268

227269
```tsx
@@ -255,6 +297,7 @@ interface BookPage {
255297
| 页角点击 | 鼠标悬停在页面右下角/左下角,出现卷起效果后点击翻页 | `showCornerFlip` |
256298
| 导航栏按钮 | 底部导航栏的前后翻页按钮 | `showNavigation` |
257299
| 键盘快捷键 | 方向键、Home、End、Escape | `enableKeyboard` |
300+
| 翻页音效 | 翻页时播放纸张翻动音效 | `enableSound` |
258301
| 封面点击/拖拽 | 点击或向左拖拽封面打开书籍 | 始终可用 |
259302

260303
> **注意**:最后一页(结束页)禁用拖拽,鼠标光标恢复为普通箭头。

src/components/stateless/InteractiveBook/InteractiveBook.stories.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ const meta: Meta<typeof InteractiveBook> = {
231231
enableKeyboard: { control: 'boolean', description: '是否启用键盘导航' },
232232
showNavigation: { control: 'boolean', description: '是否显示底部导航栏' },
233233
showCornerFlip: { control: 'boolean', description: '是否显示页角翻页热区(海浪呼吸效果)' },
234+
enableSound: { control: 'boolean', description: '是否启用翻页音效' },
234235
pageImages: { control: 'object', description: '图片模式:图片 URL 数组,每张图片对应书的一面' },
235236
pdfUrl: { control: 'text', description: 'PDF 模式:PDF 文件 URL' },
236237
},
@@ -465,3 +466,49 @@ export const MinimalMode: Story = {
465466
showCornerFlip: false,
466467
},
467468
}
469+
470+
// ─── 翻页音效(开启)─────────────────────────
471+
export const SoundEnabled: Story = {
472+
name: '翻页音效(开启)',
473+
render: (args: InteractiveBookProps) => (
474+
<div className="flex h-[800px] w-full flex-col items-center justify-center gap-4 bg-neutral-100 p-10">
475+
<p style={{ color: '#6b7280', fontSize: '0.875rem', textAlign: 'center', maxWidth: 460 }}>
476+
默认开启翻页音效(<code>enableSound=true</code>),翻页时会播放纸张翻动的声音。 点击、拖拽、键盘翻页均触发音效。
477+
</p>
478+
<InteractiveBook {...args} />
479+
</div>
480+
),
481+
args: {
482+
coverImage: AiCover,
483+
bookTitle: 'AI Agent 完全指南',
484+
bookAuthor: 'AI 专家',
485+
pages: bookPages,
486+
width: 350,
487+
height: 500,
488+
enableKeyboard: true,
489+
enableSound: true,
490+
},
491+
}
492+
493+
// ─── 翻页音效(禁用)─────────────────────────
494+
export const SoundDisabled: Story = {
495+
name: '翻页音效(禁用)',
496+
render: (args: InteractiveBookProps) => (
497+
<div className="flex h-[800px] w-full flex-col items-center justify-center gap-4 bg-neutral-100 p-10">
498+
<p style={{ color: '#6b7280', fontSize: '0.875rem', textAlign: 'center', maxWidth: 460 }}>
499+
设置 <code>enableSound=false</code> 禁用翻页音效,适合需要安静环境的场景。
500+
</p>
501+
<InteractiveBook {...args} />
502+
</div>
503+
),
504+
args: {
505+
coverImage: AiCover,
506+
bookTitle: 'AI Agent 完全指南',
507+
bookAuthor: 'AI 专家',
508+
pages: bookPages,
509+
width: 350,
510+
height: 500,
511+
enableKeyboard: true,
512+
enableSound: false,
513+
},
514+
}

src/components/stateless/InteractiveBook/index.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ export interface InteractiveBookProps {
5353
showNavigation?: boolean
5454
/** 是否显示页角翻页热区(海浪呼吸效果),默认 true */
5555
showCornerFlip?: boolean
56+
/** 是否启用翻页音效,默认 true */
57+
enableSound?: boolean
58+
/** 自定义音效文件 URL,默认使用内置翻页音效 */
59+
soundSrc?: string
60+
/** 初始是否打开书籍(跳过封面动画),默认 false */
61+
defaultOpen?: boolean
62+
/** 受控模式:外部控制书籍开合状态。传入后会覆盖内部 isOpen */
63+
open?: boolean
64+
/** 书籍关闭时的回调(用户点击关闭按钮时触发) */
65+
onClose?: () => void
5666
}
5767

5868
// ─── 图片懒加载组件 ────────────────────────────────
@@ -274,8 +284,25 @@ export default function InteractiveBook({
274284
enableKeyboard = true,
275285
showNavigation = true,
276286
showCornerFlip = true,
287+
enableSound = true,
288+
soundSrc,
289+
defaultOpen = false,
290+
open: controlledOpen,
291+
onClose,
277292
}: InteractiveBookProps) {
278-
const [isOpen, setIsOpen] = useState(false)
293+
const [isOpen, setIsOpen] = useState(controlledOpen ?? defaultOpen)
294+
295+
// 受控模式:外部 open 变化时同步内部状态
296+
useEffect(() => {
297+
if (controlledOpen === undefined) return
298+
if (controlledOpen !== isOpen) {
299+
setIsOpen(controlledOpen)
300+
if (!controlledOpen) {
301+
setCurrentPageIndex(-1)
302+
}
303+
}
304+
// eslint-disable-next-line react-hooks/exhaustive-deps
305+
}, [controlledOpen])
279306
const [currentPageIndex, setCurrentPageIndex] = useState(-1)
280307
const [isHovering, setIsHovering] = useState(false)
281308
const [isDragging, setIsDragging] = useState(false)
@@ -294,6 +321,7 @@ export default function InteractiveBook({
294321
// 使用 DOM <audio> 元素而非 new Audio(),在 Storybook iframe 中更可靠
295322
const audioRef = useRef<HTMLAudioElement | null>(null)
296323
const playPageTurnSound = () => {
324+
if (!enableSound) return
297325
const audio = audioRef.current
298326
if (!audio) return
299327
audio.currentTime = 0
@@ -326,6 +354,7 @@ export default function InteractiveBook({
326354
e.stopPropagation()
327355
setIsOpen(false)
328356
setCurrentPageIndex(-1)
357+
onClose?.()
329358
}
330359

331360
const nextPage = (e: React.MouseEvent) => {
@@ -1280,7 +1309,7 @@ export default function InteractiveBook({
12801309
)}
12811310

12821311
{/* 翻页音效 — 使用 DOM audio 元素,兼容 Storybook iframe */}
1283-
<audio ref={audioRef} src={pageTurnAudio} preload="auto" />
1312+
{enableSound && <audio ref={audioRef} src={soundSrc || pageTurnAudio} preload="auto" />}
12841313
</div>
12851314
)
12861315
}

0 commit comments

Comments
 (0)