add some function and fix some problems.

This commit is contained in:
Jiao77
2025-10-01 16:05:43 +08:00
parent 32a850f346
commit f152c8dc5d
14 changed files with 2639 additions and 65 deletions

352
docs/CHART_JITTER_FIX.md Normal file
View File

@@ -0,0 +1,352 @@
# 模态框图表抽搐问题修复
## 📅 修复日期
2025年10月1日
## 🐛 问题描述
### 症状
在点击EDA学术发表指南的场所卡片时弹出的模态框中的图表会出现"抽搐"现象:
- 图表在渲染时会突然变大或变小
- 图表尺寸会快速闪烁调整
- 视觉体验不流畅
### 根本原因
**时序问题**:图表在模态框动画**进行中**就开始渲染,而此时容器的尺寸还在变化。
#### 问题流程(修复前)❌
```
1. 点击卡片
2. 立即渲染图表(容器尺寸 = 卡片大小)
3. GSAP动画开始
4. 容器从卡片大小 → 模态框大小0.5秒动画)
5. 图表被迫在动画中重新计算尺寸
6. 结果:图表"抽搐"
```
#### 问题代码
```typescript
// ❌ 错误:图表在动画之前渲染
modalContent.innerHTML = `...`;
// 渲染图表(此时容器尺寸还在变化)
const ctx = canvas.getContext('2d');
activeChart = new Chart(ctx, {...});
// GSAP动画容器尺寸正在改变
const tl = gsap.timeline({
onComplete: () => { isAnimating = false; }
});
tl.to(modal, {
width: targetW, // 容器宽度变化
height: targetH, // 容器高度变化
duration: 0.5
});
```
## ✅ 修复方案
### 核心思路
**延迟图表渲染**:等待模态框动画完成后再渲染图表,确保容器尺寸已经稳定。
### 修复后的流程 ✅
```
1. 点击卡片
2. 设置模态框内容(但不渲染图表)
3. GSAP动画开始
4. 容器从卡片大小 → 模态框大小0.5秒动画)
5. 动画完成回调触发
6. 渲染图表(容器尺寸已稳定)
7. 结果:图表平滑出现,无抽搐
```
## 🔧 技术实现
### 1. 提取图表渲染为独立函数
#### 之前 ❌
```typescript
// 图表渲染代码直接嵌入在 openModal 中
try {
const Chart = (window as any).Chart;
const ctx = canvas.getContext('2d');
activeChart = new Chart(ctx, {...});
} catch (err) {
console.warn('绘制图表时出错:', err);
}
```
#### 现在 ✅
```typescript
/**
* 渲染图表(独立函数,在动画完成后调用)
*/
function renderVenueChart(venue: VenueData): void {
try {
const Chart = (window as any).Chart;
if (Chart && venue.acceptanceValue !== null) {
const canvas = document.getElementById('venueChart') as HTMLCanvasElement;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (ctx) {
// 使用 requestAnimationFrame 确保画布尺寸已稳定
requestAnimationFrame(() => {
activeChart = new Chart(ctx, {
type: 'bar',
data: {...},
options: {
responsive: true,
maintainAspectRatio: false,
...
}
});
});
}
}
} catch (err) {
console.warn('绘制图表时出错:', err);
}
}
```
### 2. 在动画完成回调中调用
#### 之前 ❌
```typescript
const tl = gsap.timeline({
onComplete: () => {
isAnimating = false;
}
});
```
#### 现在 ✅
```typescript
const tl = gsap.timeline({
onComplete: () => {
isAnimating = false;
// 动画完成后渲染图表
renderVenueChart(venue);
}
});
```
### 3. 添加 requestAnimationFrame 保护
```typescript
requestAnimationFrame(() => {
activeChart = new Chart(ctx, {...});
});
```
**作用**
- 确保在浏览器下一帧渲染前执行
- 给容器一个重绘的机会
- 确保尺寸计算准确
### 4. 简化模式同步修复
```typescript
function openSimpleModal(venueId: string): void {
// ... 设置内容 ...
// 简化模式下也渲染图表
renderVenueChart(venue);
}
```
## 📊 时序对比
### 修复前的时序 ❌
```
时间轴:
0ms - 点击卡片
0ms - 渲染图表(容器 = 300x200
10ms - GSAP动画开始
10ms - 容器尺寸变化开始
100ms - 图表检测到容器变化,重新计算
200ms - 容器继续变化
300ms - 图表再次调整(抽搐)
500ms - 动画结束
510ms - 图表最终稳定
```
### 修复后的时序 ✅
```
时间轴:
0ms - 点击卡片
0ms - 设置内容(图表占位符)
10ms - GSAP动画开始
10ms - 容器尺寸变化
500ms - 动画完成
500ms - onComplete 回调
510ms - renderVenueChart 调用
520ms - requestAnimationFrame
530ms - 图表渲染(容器尺寸稳定 = 800x600
540ms - 图表平滑出现 ✅
```
## 🎯 关键优化点
### 1. 延迟渲染
- ✅ 等待容器尺寸稳定
- ✅ 避免动画中的重新计算
### 2. requestAnimationFrame
- ✅ 浏览器下一帧渲染
- ✅ 确保DOM已更新
- ✅ 尺寸计算准确
### 3. 独立函数
- ✅ 代码复用GSAP模式 + 简化模式)
- ✅ 逻辑清晰
- ✅ 易于维护
### 4. 错误处理
- ✅ try-catch 包裹
- ✅ 优雅降级
- ✅ 控制台警告
## 📝 修改文件
**文件**: `/src/pages/report/ai-eda-paper-report/eda-venues-interactive.ts`
**修改内容**:
1. 新增 `renderVenueChart()` 函数
2. 修改 `openModal()` - 将图表渲染移到 `onComplete`
3. 修改 `openSimpleModal()` - 调用新的图表渲染函数
**代码行数**: ~35行修改
## 🚀 构建验证
```bash
0 errors, 0 warnings
20 pages built
✓ 图表抽搐问题已修复
```
## 🎨 用户体验提升
### 修复前 ❌
- 图表闪烁、跳动
- 尺寸不稳定
- 视觉体验差
- 看起来像bug
### 修复后 ✅
- 图表平滑出现
- 尺寸稳定
- 视觉流畅
- 专业感强
## 💡 技术要点
### Chart.js 响应式原理
```typescript
options: {
responsive: true, // 响应容器尺寸变化
maintainAspectRatio: false // 不保持宽高比,填充容器
}
```
**问题**当容器尺寸变化时Chart.js 会自动调整图表尺寸
**解决**:等待容器尺寸稳定后再渲染图表
### GSAP Timeline onComplete
```typescript
const tl = gsap.timeline({
onComplete: () => {
// 所有动画完成后执行
renderVenueChart(venue);
}
});
```
**优势**
- 精确的时序控制
- 保证动画完成
- 避免中途干扰
### requestAnimationFrame 的作用
```typescript
requestAnimationFrame(() => {
// 浏览器下一帧执行
activeChart = new Chart(ctx, {...});
});
```
**原理**
1. 等待浏览器重绘
2. 确保DOM已更新
3. 尺寸计算准确
4. 避免布局抖动
## 📚 相关知识
### 1. Chart.js 尺寸计算
- Chart.js 基于父容器的 `clientWidth``clientHeight`
- 动画中这些值会不断变化
- 导致 Chart.js 多次重新计算和渲染
### 2. GSAP 动画时序
- `duration: 0.5` = 500ms
- `ease: 'expo.out'` = 先快后慢
- `onComplete` = 动画完成回调
### 3. 浏览器渲染流程
```
JavaScript → Style → Layout → Paint → Composite
requestAnimationFrame 在此执行
```
## 🎯 最佳实践
### 处理动画中的图表渲染
1.**延迟渲染** - 等待容器尺寸稳定
2.**使用回调** - onComplete、onUpdate 等
3.**requestAnimationFrame** - 确保DOM更新
4.**销毁旧图表** - 避免内存泄漏
### 避免的错误
1. ❌ 在动画进行中渲染图表
2. ❌ 不检查容器尺寸
3. ❌ 不使用 requestAnimationFrame
4. ❌ 忘记销毁旧图表实例
## 🔍 调试技巧
### 如何验证修复
1. 打开浏览器开发者工具
2. 切换到 Performance 标签
3. 录制点击卡片的过程
4. 查看渲染时序
5. 确认图表在动画完成后才渲染
### 控制台日志
```typescript
console.log('动画开始');
const tl = gsap.timeline({
onComplete: () => {
console.log('动画完成');
renderVenueChart(venue);
console.log('图表渲染完成');
}
});
```
---
**修复完成!** 🎉
现在模态框弹出时图表会平滑出现,无任何抽搐现象。
**建议测试**
- 点击不同的场所卡片
- 观察图表渲染是否平滑
- 检查控制台无错误
**下一步**
- 运行 `npm run dev` 测试效果
- 或直接 `npm run build && ./deploy-full.sh` 部署