🚀 高性能 React Markdown 打字动画组件,完美复刻 DeepSeek 聊天界面效果
🇨🇳 中文 | 🇺🇸 English
一个专为现代 AI 应用设计的 React 组件,提供流畅的实时打字动画和完整的 Markdown 渲染能力。
如果你想要一个纯净的react markdown
打字组件,可以使用react-markdown-typer
-
流式数据打字卡顿问题
传统打字机组件在处理 AI 后端流式数据时,由于每个 chunk 包含多个字符,会出现卡顿、跳字等问题。ds-markdown 智能拆分每个 chunk,确保每个字符都流畅打字。 -
Markdown 渲染与打字动画割裂
大多数打字机组件只支持纯文本,无法在打字过程中实时渲染 Markdown 语法、数学公式、图表等富媒体内容。
-
提升用户沉浸感
提供专业级 AI 聊天体验,让用户感受到真实的 AI 交互过程,极大提升产品专业度和用户满意度。 -
开箱即用,降低开发复杂度
完整的解决方案,无需额外配置即可支持流式数据、Markdown 渲染、数学公式、图表等复杂功能。 -
适配多种应用场景
从 AI 聊天机器人到教育内容展示,从技术文档到产品演示,一个组件满足多种需求。
- 专业级 AI 聊天响应效果,媲美主流 AI 平台体验
- 支持思考过程 (
thinking
) 和回答内容 (answer
) 双模式 - 流式数据完美适配,零延迟响应用户输入
- 完整 Markdown 语法支持,包括代码高亮、表格、列表等
- 数学公式渲染 (KaTeX),支持
$...$
和\[...\]
语法 - Mermaid 图表支持,包括流程图、序列图、甘特图、类图等 🆕
- 支持亮色/暗色主题,适配不同产品风格
- 插件化架构,支持 remark/rehype 插件扩展
- 代码块、图表增强功能:复制、下载、语言等
- 内置丰富的UI组件:Button、IconButton、ToolTip、Segmented等
- 完整的交互体验和无障碍支持
- 丰富的命令式API:
start
、stop
、resume
、restart
等 - 支持打字中断与继续,灵活控制动画状态
- 支持打字关闭与开启,满足不同场景需求
- 完整的TypeScript类型支持
- 双模式定时器优化,支持
requestAnimationFrame
和setTimeout
模式 - 高频打字支持(
requestAnimationFrame
模式下打字间隔最低可接近于0ms
) - 帧同步渲染,与浏览器刷新完美配合
- 智能字符批量处理,视觉效果更自然
- 动态速度控制 🆕:根据剩余字符数量自动调整打字速度,流式数据场景下提供更自然的阅读体验
- 轻量级设计,体积小、性能优
- 核心依赖 react-markdown,无重量级依赖
# npm
npm install ds-markdown
# yarn
yarn add ds-markdown
# pnpm
pnpm add ds-markdown
无需安装,直接在浏览器中使用:
<!-- 导入样式, 必须 -->
<link rel="stylesheet" href="https://esm.sh/ds-markdown/dist/style.css" />
<!-- 导入katex数学公式样式, 非不要不引入 -->
<link rel="stylesheet" href="https://esm.sh/ds-markdown/dist/katex.css" />
<!-- 导入组件 -->
<script type="module">
import Markdown from 'https://esm.sh/ds-markdown';
</script>
import DsMarkdown from 'ds-markdown';
import 'ds-markdown/style.css';
function App() {
return (
<DsMarkdown interval={20} answerType="answer">
# Hello ds-markdown 这是一个**高性能**的打字动画组件! ## 特性 - ⚡ 零延迟流式处理 - 🎬 流畅打字动画 - 🎯 完美语法支持
</DsMarkdown>
);
}
import DsMarkdown from 'ds-markdown';
import 'ds-markdown/style.css';
function StaticDemo() {
const [disableTyping, setDisableTyping] = useState(false);
return (
<div>
<button onClick={() => setDisableTyping(!disableTyping)}>{disableTyping ? '开启' : '关闭'}打字机效果</button>
<DsMarkdown interval={20} answerType="answer" disableTyping={disableTyping}>
# 静态展示模式 当 `disableTyping` 为 `true` 时,内容会立即全部显示,无打字动画效果。 这在某些场景下非常有用: - 📄 静态文档展示 - 🔄 切换显示模式 - ⚡ 快速预览内容
</DsMarkdown>
</div>
);
}
import DsMarkdown from 'ds-markdown';
// 如果需要展示公式,则需要引入公式转换插件
import { katexPlugin } from 'ds-markdown/plugins';
import 'ds-markdown/style.css';
// 如果需要展示公式,则需要引入数学公式样式
import 'ds-markdown/katex.css';
function MathDemo() {
return (
<DsMarkdown interval={20} answerType="answer" plugins={[katexPlugin]} math={{ splitSymbol: 'dollar' }}>
# 勾股定理 在直角三角形中,斜边的平方等于两条直角边的平方和: $a^2 + b^2 = c^2$ 其中: - $a$ 和 $b$ 是直角边 - $c$ 是斜边 对于经典的"勾三股四弦五": $c = \sqrt{3 ^ (2 + 4) ^ 2} = \sqrt{25} = 5$
</DsMarkdown>
);
}
function ChatDemo() {
const [thinking, setThinking] = useState('');
const [answer, setAnswer] = useState('');
const handleAsk = () => {
setThinking('🤔 正在思考您的问题...');
setTimeout(() => {
setAnswer(`# 关于 React 19
React 19 带来了许多激动人心的新特性:
## 🚀 主要更新
1. **React Compiler** - 自动优化性能
2. **Actions** - 简化表单处理
3. **Document Metadata** - 内置 SEO 支持
让我们一起探索这些新功能!`);
}, 2000);
};
return (
<div>
<button onClick={handleAsk}>询问 AI</button>
{thinking && (
<DsMarkdown answerType="thinking" interval={30}>
{thinking}
</DsMarkdown>
)}
{answer && (
<DsMarkdown answerType="answer" interval={15}>
{answer}
</DsMarkdown>
)}
</div>
);
}
import DsMarkdown from 'ds-markdown';
import 'ds-markdown/style.css';
function CodeBlockDemo() {
const codeContent = `# Hello World
\`\`\`javascript
function greet(name) {
console.log(\`Hello, \${name}!\`);
}
greet('ds-markdown');
\`\`\`
支持代码高亮、复制和下载功能!`;
return (
<DsMarkdown
interval={20}
answerType="answer"
codeBlock={{
headerActions: true, // 启用代码块头部操作按钮
}}
>
{codeContent}
</DsMarkdown>
);
}
import DsMarkdown from 'ds-markdown';
import { ConfigProvider } from 'ds-markdown';
import mermaidPlugin from 'ds-markdown-mermaid-plugin';
import 'ds-markdown/style.css';
function MermaidDemo() {
const chartContent = `以下是简化版的学习开车流程图,仅保留 **最核心步骤**,适合快速掌握关键节点:
\`\`\`mermaid
graph TD
A[开始] --> B[科目一: 理论考试]
B --> C[科目二: 场地五项]
C --> D[科目三: 路考]
D --> E[科目四: 安全笔试]
E --> F[拿驾照]
F --> G[实际驾驶练习]
\`\`\`
### 极简说明:
1. **理论先行**:先通过交通规则笔试(科目一)。
2. **场地基础**:练习倒车、坡起等(科目二)。
3. **上路实战**:实际道路驾驶考试(科目三)。
4. **安全收尾**:通过科目四即可领证。
5. **持续熟练**:拿证后继续练习适应真实路况。
### 可视化建议:
- 用手机备忘录或白纸手绘时,按 **箭头顺序** 写步骤即可。
- 想更直观?用圆形便签贴出每个科目,连线成流程。`;
return (
<ConfigProvider>
<DsMarkdown interval={20} answerType="answer" plugins={[mermaidPlugin]}>
{chartContent}
</DsMarkdown>
</ConfigProvider>
);
}
import DsMarkdown, { MarkdownCMD } from 'ds-markdown';
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
interval |
number | IntervalConfig |
打字间隔配置,支持固定间隔或动态速度控制 | 30 |
timerType |
'setTimeout' | 'requestAnimationFrame' |
定时器类型,不支持动态修改 | 当前默认值是setTimeout ,后期会改为requestAnimationFrame |
answerType |
'thinking' | 'answer' |
内容类型 (影响样式主题),不支持动态修改 | 'answer' |
theme |
'light' | 'dark' |
主题类型 | 'light' |
plugins |
IMarkdownPlugin[] |
插件配置 | [] |
math |
IMarkdownMath | 数学公式配置 | { splitSymbol: 'dollar' } |
onEnd |
(data: EndData) => void |
打字结束回调 | - |
onStart |
(data: StartData) => void |
打字开始回调 | - |
onBeforeTypedChar |
(data: IBeforeTypedChar) => Promise<void> |
字符打字前的回调,支持异步操作,会阻塞之后的打字 | - |
onTypedChar |
(data: ITypedChar) => void |
每字符打字后的回调 | - |
disableTyping |
boolean |
禁用打字动画效果 | false |
autoStartTyping |
boolean |
是否自动开始打字动画,设为 false 时需手动触发,不支持动态修改 | true |
codeBlock |
IMarkdownCode |
代码块配置 | {headerActions: true} |
注意: 如果当在打字中
disableTyping
从true
变为false
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
currentIndex |
number |
当前字符在整个字符串中的索引 | 0 |
currentChar |
string |
当前即将打字的字符 | - |
answerType |
AnswerType |
内容类型 (thinking/answer) | - |
prevStr |
string |
当前类型内容的前缀字符串 | - |
percent |
number |
打字进度百分比 (0-100) | 0 |
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
currentIndex |
number |
当前字符在整个字符串中的索引 | 0 |
currentChar |
string |
当前已打字的字符 | - |
answerType |
AnswerType |
内容类型 (thinking/answer) | - |
prevStr |
string |
当前类型内容的前缀字符串 | - |
currentStr |
string |
当前类型内容的完整字符串 | - |
percent |
number |
打字进度百分比 (0-100) | 0 |
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
max |
number |
最大间隔时间 (毫秒),剩余字符多时使用 | - |
min |
number |
最小间隔时间 (毫秒),剩余字符少时使用 | - |
curveFn |
(x: number) => number |
自定义曲线函数,x 为剩余字符占比 [0,1] | - |
curve |
'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear' | 'step-start' | 'step-end' |
预设曲线函数,curveFn 存在时无效 | 'ease' |
动态速度控制说明:
- 剩余字符越多,打字越快:当后端流式推送大量字符时,组件会自动加快打字速度
- 剩余字符越少,打字越慢:接近完成时,打字速度会逐渐放缓,提供更好的阅读体验
- 流式数据适配:自动适应流式场景中字符数量的动态变化
- 曲线函数:通过
curve
或curveFn
控制速度变化曲线
使用示例:
// 固定间隔
<DsMarkdown interval={20}>内容</DsMarkdown>
// 动态速度控制
<DsMarkdown
interval={{
min: 10, // 最快 10ms
max: 50, // 最慢 50ms
curve: 'ease-out' // 减速曲线
}}
>
内容
</DsMarkdown>
// 自定义曲线
<DsMarkdown
interval={{
min: 5,
max: 100,
curveFn: (x) => x * x // 二次函数曲线
}}
>
内容
</DsMarkdown>
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
splitSymbol |
'dollar' | 'bracket' |
数学公式分隔符类型 | 'dollar' |
分隔符说明:
'dollar'
:使用$...$
和$$...$$
语法'bracket'
:使用\(...\)
和\[...\]
语法
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
headerActions |
boolean |
是否显示头部操作按钮 | true |
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
remarkPlugin |
Pluggable |
remark 插件 | - |
rehypePlugin |
Pluggable |
rehype 插件 | - |
type |
'buildIn' | 'custom' |
插件类型 | - |
id |
any |
插件唯一标识 | - |
components |
Record<string, React.ComponentType<unknown>> |
自定义组件映射 🆕 | - |
方法 | 参数 | 说明 |
---|---|---|
start |
- | 开始打字动画 |
stop |
- | 暂停打字动画 |
resume |
- | 恢复打字动画 |
restart |
- | 重新开始打字动画,从头开始播放当前内容 |
方法 | 参数 | 说明 |
---|---|---|
push |
(content: string, answerType: AnswerType) |
添加内容并开始打字 |
clear |
- | 清空所有内容和状态 |
triggerWholeEnd |
- | 手动触发完成回调 |
start |
- | 开始打字动画 |
stop |
- | 暂停打字动画 |
resume |
- | 恢复打字动画 |
restart |
- | 重新开始打字动画,从头开始播放当前内容 |
用法示例:
markdownRef.current?.start(); // 开始动画
markdownRef.current?.stop(); // 暂停动画
markdownRef.current?.resume(); // 恢复动画
markdownRef.current?.restart(); // 重新开始动画
import { katexPlugin } from 'ds-markdown/plugins';
// 启用数学公式支持
<DsMarkdown plugins={[katexPlugin]}>数学公式:$E = mc^2$</DsMarkdown>;
安装 Mermaid 插件:
npm install ds-markdown-mermaid-plugin
基础用法:
import { ConfigProvider, Markdown } from 'ds-markdown';
import mermaidPlugin from 'ds-markdown-mermaid-plugin';
function App() {
const content = `
# 流程图示例
\`\`\`mermaid
flowchart TD
A[开始] --> B{判断条件}
B -->|是| C[处理A]
B -->|否| D[处理B]
C --> E[结束]
D --> E
\`\`\`
`;
return (
<ConfigProvider>
<Markdown plugins={[mermaidPlugin]}>{content}</Markdown>
</ConfigProvider>
);
}
支持的图表类型:
- 🔄 流程图 (Flowchart) - 展示流程和决策路径
- 📋 序列图 (Sequence Diagram) - 展示对象间的交互时序
- 📅 甘特图 (Gantt Chart) - 项目计划和时间线
- 🏗️ 类图 (Class Diagram) - 面向对象设计
- 🥧 饼图 (Pie Chart) - 数据比例展示
- 🔀 状态图 (State Diagram) - 状态转换流程
- 📊 Git图 (Git Graph) - 代码分支历史
- 🗺️ 用户旅程图 (User Journey) - 用户体验流程
配置 Mermaid:
import { ConfigProvider } from 'ds-markdown';
const mermaidConfig = {
theme: 'default', // 主题:default, neutral, dark, forest
flowchart: {
useMaxWidth: true,
htmlLabels: true,
},
sequence: {
diagramMarginX: 50,
diagramMarginY: 10,
},
};
return (
<ConfigProvider mermaidConfig={mermaidConfig}>
<Markdown plugins={[mermaidPlugin]}>{chartContent}</Markdown>
</ConfigProvider>
);
相关链接:
import { createBuildInPlugin } from 'ds-markdown/plugins';
// 创建自定义插件
const customPlugin = createBuildInPlugin({
remarkPlugin: yourRemarkPlugin,
rehypePlugin: yourRehypePlugin,
id: Symbol('custom-plugin'),
components: {
// 自定义组件映射 🆕
CustomComponent: MyCustomComponent,
},
});
// 使用自定义插件
<DsMarkdown plugins={[katexPlugin, customPlugin]}>内容</DsMarkdown>;
ds-markdown 提供了丰富的UI组件,可以单独使用或与markdown组件配合。
import {
Button,
IconButton,
ToolTip,
Segmented,
CopyButton,
DownloadButton
} from 'ds-markdown';
// 按钮组件
<Button icon={<span>📄</span>} onClick={() => {}}>
点击按钮
</Button>
// 工具提示
<ToolTip title="提示信息">
<IconButton icon={<span>📋</span>} onClick={() => {}} />
</ToolTip>
// 分段控制器
<Segmented
Segmented={[
{ label: '图表', value: 'diagram' },
{ label: '代码', value: 'code' }
]}
value={value}
onChange={setValue}
/>
// 代码块操作
<CopyButton codeContent="console.log('Hello')" />
<DownloadButton codeContent="console.log('Hello')" language="javascript" />
:root {
--ds-button-bg-color: #f5f5f5;
--ds-button-hover-color: #e0e0e0;
--ds-tooltip-bg-color: rgba(0, 0, 0, 0.8);
}
import { ConfigProvider } from 'ds-markdown';
import zhCN from 'ds-markdown/i18n/zh';
import enUS from 'ds-markdown/i18n/en';
// 中文
<ConfigProvider locale={zhCN}>
<DsMarkdown {...props} />
</ConfigProvider>
// 英文
<ConfigProvider locale={enUS}>
<DsMarkdown {...props} />
</ConfigProvider>
ConfigProvider
是一个全局配置提供者,用于为 ds-markdown 组件提供多语言、Mermaid 图表和 KaTeX 数学公式的配置。
interface ConfigProviderProps {
locale?: Locale; // 语言包配置
mermaidConfig?: IMarkdownMermaidConfig; // Mermaid 图表配置
katexConfig?: IMarkdownKatexConfig; // KaTeX 数学公式配置
children: React.ReactNode; // 子组件
}
import { ConfigProvider } from 'ds-markdown';
import zhCN from 'ds-markdown/i18n/zh';
import enUS from 'ds-markdown/i18n/en';
<ConfigProvider locale={zhCN}>
<DsMarkdown {...props} />
</ConfigProvider>;
const mermaidConfig = {
theme: 'dark', // 主题:'default' | 'forest' | 'dark' | 'neutral'
fontFamily: 'Arial', // 字体
logLevel: 'warn', // 日志级别
securityLevel: 'strict', // 安全级别
startOnLoad: true, // 页面加载时自动启动
// ... 更多 Mermaid 配置选项
};
<ConfigProvider mermaidConfig={mermaidConfig}>
<DsMarkdown {...props} />
</ConfigProvider>;
const katexConfig = {
throwOnError: false, // 错误时不抛出异常
errorColor: '#cc0000', // 错误颜色
macros: {
// 自定义宏
'\\RR': '\\mathbb{R}',
'\\NN': '\\mathbb{N}',
},
minRuleThickness: 0.05, // 最小规则厚度
colorIsTextColor: false, // 颜色是否为文本颜色
// ... 更多 KaTeX 配置选项
};
<ConfigProvider katexConfig={katexConfig}>
<DsMarkdown {...props} />
</ConfigProvider>;
获取完整的配置上下文:
import { useConfig } from 'ds-markdown';
function MyComponent() {
const { locale, mermaidConfig, katexConfig } = useConfig();
return (
<div>
<p>当前语言: {locale.language}</p>
<p>Mermaid 主题: {mermaidConfig?.theme}</p>
</div>
);
}
仅获取语言包配置:
import { useLocale } from 'ds-markdown';
function MyComponent() {
const locale = useLocale();
return (
<div>
<p>当前语言: {locale.language}</p>
<p>复制按钮文本: {locale.copyButton}</p>
</div>
);
}
import { ConfigProvider } from 'ds-markdown';
import zhCN from 'ds-markdown/i18n/zh';
const mermaidConfig = {
theme: 'dark',
fontFamily: 'Consolas, monospace',
logLevel: 'warn',
};
const katexConfig = {
throwOnError: false,
errorColor: '#cc0000',
macros: {
'\\RR': '\\mathbb{R}',
},
};
function App() {
return (
<ConfigProvider locale={zhCN} mermaidConfig={mermaidConfig} katexConfig={katexConfig}>
<DsMarkdown content="# Hello World" />
</ConfigProvider>
);
}
import DsMarkdown from 'ds-markdown';
function DynamicSpeedDemo() {
const content = `# 动态速度控制示例
这是一个演示动态速度控制的示例。当剩余字符较多时,打字速度会加快;
当剩余字符较少时,打字速度会放缓,提供更好的阅读体验。
## 流式数据场景
在 AI 流式对话中,后端可能会一次性推送大量文本,使用动态速度控制可以:
- 快速处理大量文本,减少等待时间
- 在接近完成时放缓速度,让用户有时间阅读
- 提供更自然的打字体验`;
return (
<DsMarkdown
interval={{
min: 8, // 最快 8ms(剩余字符多时)
max: 80, // 最慢 80ms(剩余字符少时)
curve: 'ease-out', // 减速曲线
}}
timerType="requestAnimationFrame"
>
{content}
</DsMarkdown>
);
}
import { useRef } from 'react';
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
function StreamingChat() {
const markdownRef = useRef<MarkdownCMDRef>(null);
// 模拟 AI 流式响应
const simulateAIResponse = async () => {
markdownRef.current?.clear();
// 思考阶段
markdownRef.current?.push('🤔 正在分析您的问题...', 'thinking');
await delay(1000);
markdownRef.current?.push('\n\n✅ 分析完成,开始回答', 'thinking');
// 流式回答
const chunks = [
'# React 19 新特性解析\n\n',
'## 🚀 React Compiler\n',
'React 19 最大的亮点是引入了 **React Compiler**:\n\n',
'- 🎯 **自动优化**:无需手动 memo 和 useMemo\n',
'- ⚡ **性能提升**:编译时优化,运行时零开销\n',
'- 🔧 **向后兼容**:现有代码无需修改\n\n',
'希望这个解答对您有帮助!🎉',
];
for (const chunk of chunks) {
await delay(100);
markdownRef.current?.push(chunk, 'answer');
}
};
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
return (
<div className="chat-container">
<button onClick={simulateAIResponse}>🤖 询问 React 19 新特性</button>
<MarkdownCMD ref={markdownRef} interval={10} timerType="requestAnimationFrame" />
</div>
);
}
// ✅ 推荐配置
<DsMarkdown
timerType="requestAnimationFrame"
interval={15} // 15-30ms 为最佳体验
/>
// ✅ 流式数据推荐配置
<DsMarkdown
timerType="requestAnimationFrame"
interval={{
min: 8, // 快速处理大量文本
max: 60, // 接近完成时放缓
curve: 'ease-out' // 自然减速
}}
/>
// ✅ 推荐:命令式 API
const ref = useRef<MarkdownCMDRef>(null);
useEffect(() => {
ref.current?.push(newChunk, 'answer');
}, [newChunk]);
// ✅ 推荐:按需加载
import { katexPlugin } from 'ds-markdown/plugins';
import 'ds-markdown/katex.css'; // 仅在需要时引入
<DsMarkdown plugins={[katexPlugin]}>数学公式内容</DsMarkdown>;
// ✅ 推荐:独立安装插件
npm install ds-markdown-mermaid-plugin
// ✅ 推荐:配置适合的主题
const mermaidConfig = {
theme: 'default', // 根据应用主题选择
flowchart: { useMaxWidth: true },
};
<ConfigProvider mermaidConfig={mermaidConfig}>
<DsMarkdown plugins={[mermaidPlugin]} />
</ConfigProvider>