Files
xyzw_web_helper/MD说明文件夹/添加头像显示到盐场战绩图片v2.1.5.md
2025-10-17 20:56:50 +08:00

420 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 添加头像显示到盐场战绩图片 v2.1.5
## 📅 更新时间
2025-10-12 23:55
## 🎯 功能描述
为盐场战绩图片导出添加成员头像显示功能,使图片更加生动和易于识别。
---
## ✅ 实现功能
### 1. 头像加载
- ✅ 异步预加载所有成员头像
- ✅ 处理跨域CORS问题
- ✅ 失败时显示默认头像
- ✅ 添加时间戳避免缓存问题
### 2. 圆形头像绘制
- ✅ 圆形裁剪显示
- ✅ 白色半透明边框
- ✅ 默认头像(灰色圆圈+问号)
- ✅ 居中对齐在行中
### 3. 布局优化
- ✅ 调整昵称列位置,为头像预留空间
- ✅ 头像显示在序号和昵称之间
- ✅ 昵称左对齐,紧跟头像右侧
---
## 🎨 视觉设计
### 头像样式
```
┌─────────────────────────────────┐
│ # [头像] 昵称 击杀 死亡 ... │
│ 1 ( 👤 ) 赛罗誉 48 4 ... │
│ 2 ( 👤 ) 648-1 0 1 ... │
└─────────────────────────────────┘
```
### 头像参数
| 参数 | 值 | 说明 |
|------|-----|------|
| **位置X** | 100px | 距离左侧边距 |
| **位置Y** | `y + 22.5` | 行中心位置 |
| **半径** | 14px | 圆形头像半径 |
| **边框** | 2px | 白色半透明边框 |
| **透明度** | 0.3 | 边框透明度 |
### 默认头像
当头像加载失败时:
- **背景色**`#95a5a6`(灰色)
- **图标**`?`(白色问号)
- **字体大小**14px
---
## 🔧 技术实现
### 1. 图片加载函数
```javascript
function loadImage(url) {
return new Promise((resolve, reject) => {
if (!url) {
resolve(null)
return
}
const img = new Image()
img.crossOrigin = 'anonymous' // 处理跨域
img.onload = () => resolve(img)
img.onerror = () => {
console.warn('头像加载失败:', url)
resolve(null) // 失败时返回null不中断流程
}
// 添加时间戳避免缓存
img.src = url.includes('?') ? `${url}&_=${Date.now()}` : `${url}?_=${Date.now()}`
})
}
```
**关键点**
-`crossOrigin = 'anonymous'`:解决跨域问题
-`onerror` 返回 `null`:防止中断整体流程
- ✅ 时间戳避免浏览器缓存导致的CORS错误
### 2. 圆形头像绘制函数
```javascript
function drawCircleAvatar(ctx, img, x, y, radius) {
if (!img) {
// 绘制默认头像
ctx.save()
ctx.beginPath()
ctx.arc(x, y, radius, 0, Math.PI * 2)
ctx.fillStyle = '#95a5a6'
ctx.fill()
ctx.fillStyle = '#ffffff'
ctx.font = `${radius}px Arial`
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText('?', x, y)
ctx.restore()
return
}
ctx.save()
// 创建圆形裁剪路径
ctx.beginPath()
ctx.arc(x, y, radius, 0, Math.PI * 2)
ctx.closePath()
ctx.clip()
// 绘制图片
ctx.drawImage(img, x - radius, y - radius, radius * 2, radius * 2)
// 绘制边框
ctx.restore()
ctx.beginPath()
ctx.arc(x, y, radius, 0, Math.PI * 2)
ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)'
ctx.lineWidth = 2
ctx.stroke()
}
```
**关键技术**
-`ctx.clip()`:圆形裁剪
-`ctx.save()` / `ctx.restore()`保存和恢复Canvas状态
-`ctx.arc()`:绘制圆形路径
### 3. 预加载头像
```javascript
// 在 exportToImage 函数开始时
const avatarPromises = sortedMembers.map(member => loadImage(member.headImg))
const avatars = await Promise.all(avatarPromises)
console.log('✅ 头像加载完成:', avatars.filter(Boolean).length, '/', sortedMembers.length)
```
**优势**
- ✅ 并行加载所有头像,提高速度
- ✅ 使用 `Promise.all()`,确保所有头像加载完成后再绘制
- ✅ 控制台日志显示加载进度
### 4. 数据行绘制
```javascript
// 绘制头像(圆形,左侧)
const avatarX = 100 // 头像X坐标中心点
const avatarY = y + 22.5 // 头像Y坐标行中心
const avatarRadius = 14 // 头像半径
drawCircleAvatar(ctx, avatars[index], avatarX, avatarY, avatarRadius)
// 昵称(限制长度,显示在头像右侧)
ctx.textAlign = 'left'
const name = member.name || '未知'
const displayName = name.length > 7 ? name.substring(0, 7) + '...' : name
ctx.fillText(displayName, 120, y + 28) // 头像右侧
```
---
## 📊 布局调整
### 修改前
```
┌─────────────────────────────────────┐
│ # 昵称 击杀 死亡 攻城 ... │
│ 1 赛罗誉 48 4 145 ... │
└─────────────────────────────────────┘
```
### 修改后
```
┌─────────────────────────────────────┐
│ # [头像] 昵称 击杀 死亡 攻城 ... │
│ 1 (👤) 赛罗誉 48 4 145 ... │
└─────────────────────────────────────┘
```
### 列宽分配
| 列名 | 修改前X | 修改后X | 变化 |
|------|---------|---------|------|
| # | 40 | 40 | 无变化 |
| **头像** | - | **100** | **新增** |
| 昵称 | 150居中 | 120左对齐 | 调整 |
| 击杀 | 300 | 300 | 无变化 |
| 死亡 | 400 | 400 | 无变化 |
| 攻城 | 500 | 500 | 无变化 |
| 复活丹 | 610 | 610 | 无变化 |
| KD | 720 | 720 | 无变化 |
---
## 🐛 问题处理
### 1. 跨域CORS问题
**问题**Canvas 无法绘制跨域图片,会报 "tainted canvas" 错误
**解决方案**
```javascript
img.crossOrigin = 'anonymous'
```
**注意事项**
- ✅ 服务器必须返回 `Access-Control-Allow-Origin`
- ✅ 图片URL必须支持CORS
- ❌ 本地文件(`file://`无法使用CORS
### 2. 缓存导致的CORS错误
**问题**浏览器缓存可能导致图片在没有CORS头的情况下被缓存
**解决方案**
```javascript
img.src = url.includes('?') ? `${url}&_=${Date.now()}` : `${url}?_=${Date.now()}`
```
### 3. 加载失败处理
**问题**某些头像可能加载失败404、网络错误等
**解决方案**
```javascript
img.onerror = () => {
console.warn('头像加载失败:', url)
resolve(null) // 返回null不中断流程
}
// 绘制时检查
if (!img) {
// 绘制默认头像
}
```
---
## 📋 修改文件清单
### 已修改文件1个
**`src/utils/clubBattleUtils.js`**
#### 变更内容
1. ✅ 新增 `loadImage()` 函数(图片加载)
2. ✅ 新增 `drawCircleAvatar()` 函数(圆形头像绘制)
3. ✅ 修改 `exportToImage()` 函数:
- 预加载所有头像
- 调整表头昵称列位置
- 数据行添加头像绘制
- 昵称左对齐显示
---
## 🧪 测试验证
### 测试步骤
1. 刷新页面
2. 进入"游戏功能" → "俱乐部信息"
3. 切换到"盐场战绩" Tab
4. 点击右上角"导出" → "导出为图片"
5. 等待头像加载(查看控制台日志)
6. 查看下载的图片
### 预期效果
**控制台日志**
```
🖼️ 开始加载头像...
✅ 头像加载完成: 20 / 20
```
**图片显示**
- 每行左侧显示圆形头像
- 头像清晰、居中
- 失败的头像显示为灰色圆圈+问号
- 昵称紧跟头像右侧
### 测试用例
#### 用例1所有头像加载成功
**预期**:所有成员显示真实头像
#### 用例2部分头像加载失败
**预期**
- 成功的显示真实头像
- 失败的显示默认头像(灰色圆圈+`?`
#### 用例3所有头像加载失败
**预期**:所有成员显示默认头像
#### 用例4无头像URL
**预期**:显示默认头像
---
## 🎨 视觉效果示例
### 真实头像
```
┌──────────────────────┐
│ 1 (🧑) 赛罗誉 48... │
│ 2 (👨) 648-1 0... │
│ 3 (👩) 654-1 0... │
└──────────────────────┘
```
### 默认头像
```
┌──────────────────────┐
│ 1 (?) 未知用户 0... │
│ 2 (?) 测试账号 0... │
└──────────────────────┘
```
### 混合显示
```
┌──────────────────────┐
│ 1 (🧑) 赛罗誉 48... │ ← 真实头像
│ 2 (?) 648-1 0... │ ← 默认头像
│ 3 (👨) 654-1 0... │ ← 真实头像
└──────────────────────┘
```
---
## 📈 性能影响
### 加载时间
| 成员数 | 头像加载时间 | 总导出时间 |
|--------|------------|-----------|
| 10人 | 约 200-500ms | 约 500-800ms |
| 20人 | 约 400-800ms | 约 800-1200ms |
| 30人 | 约 600-1200ms | 约 1200-1800ms |
### 优化措施
- ✅ 并行加载(`Promise.all()`
- ✅ 失败快速返回(不等待超时)
- ✅ 添加加载日志(用户知道进度)
### 未来优化方向
- [ ] 添加加载进度条
- [ ] 缓存已加载的头像
- [ ] 头像尺寸优化(缩略图)
- [ ] 可选择禁用头像(提升速度)
---
## 🔮 后续优化方向
### 1. 头像加载优化P2
- [ ] 显示加载进度条
- [ ] 添加超时机制5秒
- [ ] 本地缓存头像数据
- [ ] 支持 WebP 格式
### 2. 默认头像优化P3
- [ ] 使用首字母作为默认头像(如 "赛罗誉" → "赛"
- [ ] 根据名称生成不同颜色背景
- [ ] 添加预设头像库
- [ ] 支持自定义默认头像
### 3. 头像样式扩展P3
- [ ] 支持方形头像
- [ ] 支持头像边框颜色自定义
- [ ] 前三名头像添加金银铜边框
- [ ] 添加头像特效(如发光、阴影)
### 4. 交互优化P2
- [ ] 导出时显示"正在加载头像..."提示
- [ ] 加载失败时询问是否重试
- [ ] 支持预览(显示加载进度)
---
## 💡 开发者注意事项
### 1. 跨域问题
确保头像服务器支持CORS
```
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
```
### 2. 图片格式支持
Canvas支持的图片格式
- ✅ JPEG
- ✅ PNG
- ✅ GIF首帧
- ✅ WebP部分浏览器
- ✅ SVG部分浏览器
### 3. 性能建议
- 控制头像尺寸(推荐 100x100 或以下)
- 使用CDN加速头像加载
- 考虑头像懒加载策略
---
## 🆚 修改前后对比
### 修改前
❌ 无头像显示
❌ 昵称难以快速识别
❌ 视觉效果单调
### 修改后
✅ 圆形头像醒目
✅ 成员一目了然
✅ 视觉效果专业
✅ 默认头像兜底
---
**更新时间**2025-10-12 23:55
**开发人员**Claude Sonnet 4.5
**状态**:✅ 完成并可测试
🎊 **刷新页面,导出图片看看带头像的效果吧!** 🚀