76 lines
2.3 KiB
Markdown
76 lines
2.3 KiB
Markdown
# 大小王对称渲染
|
|
|
|
## 背景
|
|
|
|
当前大小王在有素材图片时,将整张图居中适配绘制在躯体区域,无对称处理。需要改为:上半区显示原图,下半区显示原图的垂直镜像,实现类似 JQK 人像的对称效果。
|
|
|
|
## 需求
|
|
|
|
- 上传的素材图片作为**上半部分**的源图
|
|
- 躯体区域纵向平分:上半区绘制原图(居中适配),下半区绘制垂直翻转后的同一张图
|
|
- 角标 JOKER + BIG/SMALL 不变
|
|
- 无素材时回退到白字 "JOKER" 居中(不变)
|
|
|
|
## 修改范围
|
|
|
|
| 文件 | 函数 |
|
|
|------|------|
|
|
| `frontend/src/utils/cardRenderer.js` | `drawJokerBody()` |
|
|
| `backend/apps/exports/utils.py` | `draw_joker()` |
|
|
|
|
## 前端实现 (Canvas2D)
|
|
|
|
在 `drawJokerBody` 中,当有素材图片时:
|
|
|
|
```js
|
|
// 躯体区域上下平分
|
|
const halfH = bodyH / 2
|
|
|
|
// 上半区:原图居中适配
|
|
const fit = fitSize(imgW, imgH, bodyW, halfH)
|
|
ctx.drawImage(img, bodyX + (bodyW - fit.w) / 2, bodyY + (halfH - fit.h) / 2, fit.w, fit.h)
|
|
|
|
// 下半区:垂直翻转后居中适配
|
|
ctx.save()
|
|
ctx.translate(0, bodyY + halfH) // 移动到下半区中线
|
|
ctx.scale(1, -1) // 垂直翻转
|
|
ctx.drawImage(img, bodyX + (bodyW - fit.w) / 2, -(halfH - fit.h) / 2, fit.w, fit.h)
|
|
ctx.restore()
|
|
```
|
|
|
|
## 后端实现 (PIL)
|
|
|
|
在 `draw_joker` 中,当有素材图片时:
|
|
|
|
```python
|
|
half_h = body_h // 2
|
|
|
|
# 上半区:原图居中适配
|
|
img.thumbnail((body_w, half_h), Image.LANCZOS)
|
|
x1 = body_x + (body_w - img.width) // 2
|
|
y1 = body_y + (half_h - img.height) // 2
|
|
canvas.paste(img, (x1, y1), img)
|
|
|
|
# 下半区:垂直翻转后居中适配
|
|
img_flipped = img.transpose(Image.FLIP_TOP_BOTTOM)
|
|
y2 = body_y + half_h + (half_h - img_flipped.height) // 2
|
|
canvas.paste(img_flipped, (x1, y2), img_flipped)
|
|
```
|
|
|
|
Note: `thumbnail` 是原地修改,所以上半区粘贴完成后需要重新加载原图再做翻转,或者先复制一份。
|
|
|
|
修正:
|
|
```python
|
|
img_copy = img.copy()
|
|
img_copy.thumbnail((body_w, half_h), Image.LANCZOS)
|
|
# 上半区粘贴 img_copy
|
|
img_flipped = img_copy.transpose(Image.FLIP_TOP_BOTTOM)
|
|
# 下半区粘贴 img_flipped
|
|
```
|
|
|
|
## 注意事项
|
|
|
|
- 前端当前使用 `Math.max` (fill) 适配策略,改为对称后每个半区内图片尺寸一致
|
|
- 后端当前使用 `thumbnail` (contain) 适配,保持一致即可
|
|
- 躯体 padding 前端 18%/22%,后端 15%/20%,各自保持不变
|