refactor: 收口 BangDream 难度详情列表规格
This commit is contained in:
parent
ef0f571ae8
commit
a826f0c5f1
@ -389,6 +389,7 @@ export interface TsuguHook {
|
|||||||
- 已新增 `render-blocks/list-card-icon-spec.ts`,收口通用卡牌图标列表默认行高、文本字号比例、卡牌间距比例、默认可见性标记和历史排序比较器;`list-card-icon-list.ts` 改为消费 spec,卡牌图标列表排序和输出结构保持不变;`list-card-icon-spec.spec.ts` 覆盖稀有度排序、优先卡牌类型排序、普通卡按 ID 排序和字号/间距计算,本地 `/查卡 472` 图片 smoke 输出非空。
|
- 已新增 `render-blocks/list-card-icon-spec.ts`,收口通用卡牌图标列表默认行高、文本字号比例、卡牌间距比例、默认可见性标记和历史排序比较器;`list-card-icon-list.ts` 改为消费 spec,卡牌图标列表排序和输出结构保持不变;`list-card-icon-spec.spec.ts` 覆盖稀有度排序、优先卡牌类型排序、普通卡按 ID 排序和字号/间距计算,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||||
- 已新增 `render-blocks/list-song-spec.ts`,收口歌曲列表单行封面、文本、难度块位置、列表内容宽度、分割线高度和外层行高计算;`list-song.ts` 改为消费 spec,歌曲列表绘制顺序和输出结构保持不变;`list-song-spec.spec.ts` 覆盖单行尺寸、难度块垂直居中和歌曲组列表尺寸,本地 `/查曲 136` 图片 smoke 输出非空。
|
- 已新增 `render-blocks/list-song-spec.ts`,收口歌曲列表单行封面、文本、难度块位置、列表内容宽度、分割线高度和外层行高计算;`list-song.ts` 改为消费 spec,歌曲列表绘制顺序和输出结构保持不变;`list-song-spec.spec.ts` 覆盖单行尺寸、难度块垂直居中和歌曲组列表尺寸,本地 `/查曲 136` 图片 smoke 输出非空。
|
||||||
- 已新增 `render-blocks/list-difficulty-spec.ts`,收口难度列表默认高度/间距、徽章兜底色、圆形布局和等级文字比例/居中计算;`list-difficulty.ts` 改为消费 spec,难度列表绘制顺序和输出结构保持不变;`list-difficulty-spec.spec.ts` 覆盖列表宽度、条目偏移、徽章颜色、圆形布局和文字居中,本地 `/查曲 136` 图片 smoke 输出非空。
|
- 已新增 `render-blocks/list-difficulty-spec.ts`,收口难度列表默认高度/间距、徽章兜底色、圆形布局和等级文字比例/居中计算;`list-difficulty.ts` 改为消费 spec,难度列表绘制顺序和输出结构保持不变;`list-difficulty-spec.spec.ts` 覆盖列表宽度、条目偏移、徽章颜色、圆形布局和文字居中,本地 `/查曲 136` 图片 smoke 输出非空。
|
||||||
|
- 已新增 `render-blocks/list-difficulty-detail-spec.ts`,收口玩家难度详情列表的徽章宽度/字号/圆角、正文宽度/行高、项画布高度和通用列表行高/间距;`list-difficulty-detail.ts` 改为消费 spec,玩家详情里的已通关/Full Combo/All Perfect 难度详情绘制顺序和输出结构保持不变;`list-difficulty-detail-spec.spec.ts` 覆盖徽章参数、正文绘制参数、项布局和列表框架参数,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
|
||||||
- smoke/Jenkins/远程调试卡点继续按已固化规则处理:同一卡点第二次尝试前必须改变可验证变量;Jenkins 查询 URL 含方括号时用 `curl -g` 或改查 Jenkins home;线上图片 smoke 必须查询 `commandId`、拉回图片、展示图片、查日志并清理本轮临时目录;PowerShell 双引号 here-string 中不要写 JS `${...}` 模板字面量,避免被 PowerShell 提前插值;smoke 没有真实图片落盘时必须失败。
|
- smoke/Jenkins/远程调试卡点继续按已固化规则处理:同一卡点第二次尝试前必须改变可验证变量;Jenkins 查询 URL 含方括号时用 `curl -g` 或改查 Jenkins home;线上图片 smoke 必须查询 `commandId`、拉回图片、展示图片、查日志并清理本轮临时目录;PowerShell 双引号 here-string 中不要写 JS `${...}` 模板字面量,避免被 PowerShell 提前插值;smoke 没有真实图片落盘时必须失败。
|
||||||
|
|
||||||
### Phase 6:策略 policy 和时间/档线规则
|
### Phase 6:策略 policy 和时间/档线规则
|
||||||
|
|||||||
@ -0,0 +1,79 @@
|
|||||||
|
interface ImageLike {
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BANGDREAM_DIFFICULTY_DETAIL_LIST_SPEC = {
|
||||||
|
item: {
|
||||||
|
badgeRadius: 5,
|
||||||
|
badgeTextSize: 30,
|
||||||
|
badgeWidth: 140,
|
||||||
|
textLineHeight: 40,
|
||||||
|
textOffsetY: 50,
|
||||||
|
width: 152,
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
spacing: 0,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算难度详情项徽章绘制参数。
|
||||||
|
*
|
||||||
|
* @param difficultyName - 难度名称。
|
||||||
|
* @param color - 难度颜色。
|
||||||
|
*/
|
||||||
|
export function createDifficultyDetailBadgeSpec(
|
||||||
|
difficultyName: string,
|
||||||
|
color: string,
|
||||||
|
) {
|
||||||
|
const item = BANGDREAM_DIFFICULTY_DETAIL_LIST_SPEC.item;
|
||||||
|
return {
|
||||||
|
color,
|
||||||
|
radius: item.badgeRadius,
|
||||||
|
text: difficultyName.toUpperCase(),
|
||||||
|
textSize: item.badgeTextSize,
|
||||||
|
width: item.badgeWidth,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算难度详情正文绘制参数。
|
||||||
|
*/
|
||||||
|
export function createDifficultyDetailTextSpec() {
|
||||||
|
const item = BANGDREAM_DIFFICULTY_DETAIL_LIST_SPEC.item;
|
||||||
|
return {
|
||||||
|
lineHeight: item.textLineHeight,
|
||||||
|
maxWidth: item.width,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算难度详情项画布和内容位置。
|
||||||
|
*
|
||||||
|
* @param contentImage - 正文图片尺寸。
|
||||||
|
*/
|
||||||
|
export function createDifficultyDetailItemLayout(contentImage: ImageLike) {
|
||||||
|
const item = BANGDREAM_DIFFICULTY_DETAIL_LIST_SPEC.item;
|
||||||
|
return {
|
||||||
|
badgeX: (item.width - item.badgeWidth) / 2,
|
||||||
|
badgeY: 0,
|
||||||
|
canvasHeight: contentImage.height + item.textOffsetY,
|
||||||
|
canvasWidth: item.width,
|
||||||
|
textX: item.width / 2 - contentImage.width / 2,
|
||||||
|
textY: item.textOffsetY,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算难度详情列表传给通用列表框架的尺寸。
|
||||||
|
*
|
||||||
|
* @param firstItem - 第一个难度详情项。
|
||||||
|
*/
|
||||||
|
export function createDifficultyDetailListFrameSpec(firstItem?: ImageLike) {
|
||||||
|
return {
|
||||||
|
lineHeight: firstItem?.height,
|
||||||
|
spacing: BANGDREAM_DIFFICULTY_DETAIL_LIST_SPEC.list.spacing,
|
||||||
|
textSize: firstItem?.height,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -7,6 +7,12 @@ import { Canvas, Image } from 'skia-canvas';
|
|||||||
import { drawTextWithImages } from '@/qqbot/plugins/bangDream/tsugu/canvas/text';
|
import { drawTextWithImages } from '@/qqbot/plugins/bangDream/tsugu/canvas/text';
|
||||||
import { drawList } from './list-frame';
|
import { drawList } from './list-frame';
|
||||||
import { Player } from '@/qqbot/plugins/bangDream/tsugu/models/player';
|
import { Player } from '@/qqbot/plugins/bangDream/tsugu/models/player';
|
||||||
|
import {
|
||||||
|
createDifficultyDetailBadgeSpec,
|
||||||
|
createDifficultyDetailItemLayout,
|
||||||
|
createDifficultyDetailListFrameSpec,
|
||||||
|
createDifficultyDetailTextSpec,
|
||||||
|
} from './list-difficulty-detail-spec';
|
||||||
|
|
||||||
interface drawDifficultyDetailInListOptions {
|
interface drawDifficultyDetailInListOptions {
|
||||||
[difficultyId: number]: Array<Canvas | Image | string>;
|
[difficultyId: number]: Array<Canvas | Image | string>;
|
||||||
@ -25,37 +31,35 @@ function DifficultyDetailInList(
|
|||||||
const difficultyAndContentList: Array<Canvas> = [];
|
const difficultyAndContentList: Array<Canvas> = [];
|
||||||
for (const i in DifficultyDetailInListOptions) {
|
for (const i in DifficultyDetailInListOptions) {
|
||||||
const content = DifficultyDetailInListOptions[i];
|
const content = DifficultyDetailInListOptions[i];
|
||||||
const maxWidth = 152;
|
const tempBandIcon = drawRoundedRectWithText(
|
||||||
const logoWidth = 140;
|
createDifficultyDetailBadgeSpec(
|
||||||
const tempBandIcon = drawRoundedRectWithText({
|
difficultyNameList[i],
|
||||||
text: difficultyNameList[i].toUpperCase(),
|
difficultyColorList[i],
|
||||||
width: logoWidth,
|
),
|
||||||
textSize: 30,
|
);
|
||||||
radius: 5,
|
|
||||||
color: difficultyColorList[i],
|
|
||||||
});
|
|
||||||
|
|
||||||
|
const textSpec = createDifficultyDetailTextSpec();
|
||||||
const tempBandRankText = drawTextWithImages({
|
const tempBandRankText = drawTextWithImages({
|
||||||
content,
|
content,
|
||||||
maxWidth: maxWidth,
|
maxWidth: textSpec.maxWidth,
|
||||||
lineHeight: 40,
|
lineHeight: textSpec.lineHeight,
|
||||||
});
|
});
|
||||||
const canvas = new Canvas(maxWidth, tempBandRankText.height + 50);
|
const layout = createDifficultyDetailItemLayout(tempBandRankText);
|
||||||
|
const canvas = new Canvas(layout.canvasWidth, layout.canvasHeight);
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
ctx.drawImage(tempBandIcon, (maxWidth - logoWidth) / 2, 0);
|
ctx.drawImage(tempBandIcon, layout.badgeX, layout.badgeY);
|
||||||
ctx.drawImage(
|
ctx.drawImage(tempBandRankText, layout.textX, layout.textY);
|
||||||
tempBandRankText,
|
|
||||||
maxWidth / 2 - tempBandRankText.width / 2,
|
|
||||||
50,
|
|
||||||
);
|
|
||||||
difficultyAndContentList.push(canvas);
|
difficultyAndContentList.push(canvas);
|
||||||
}
|
}
|
||||||
|
const frameSpec = createDifficultyDetailListFrameSpec(
|
||||||
|
difficultyAndContentList?.[0],
|
||||||
|
);
|
||||||
const difficultyAndContentListImage = drawList({
|
const difficultyAndContentListImage = drawList({
|
||||||
key,
|
key,
|
||||||
content: difficultyAndContentList,
|
content: difficultyAndContentList,
|
||||||
spacing: 0,
|
spacing: frameSpec.spacing,
|
||||||
lineHeight: difficultyAndContentList?.[0].height,
|
lineHeight: frameSpec.lineHeight,
|
||||||
textSize: difficultyAndContentList?.[0].height,
|
textSize: frameSpec.textSize,
|
||||||
});
|
});
|
||||||
return difficultyAndContentListImage;
|
return difficultyAndContentListImage;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,59 @@
|
|||||||
|
import {
|
||||||
|
BANGDREAM_DIFFICULTY_DETAIL_LIST_SPEC,
|
||||||
|
createDifficultyDetailBadgeSpec,
|
||||||
|
createDifficultyDetailItemLayout,
|
||||||
|
createDifficultyDetailListFrameSpec,
|
||||||
|
createDifficultyDetailTextSpec,
|
||||||
|
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-difficulty-detail-spec';
|
||||||
|
|
||||||
|
describe('BangDream difficulty detail list spec', () => {
|
||||||
|
it('keeps difficulty detail item constants stable', () => {
|
||||||
|
expect(BANGDREAM_DIFFICULTY_DETAIL_LIST_SPEC.item).toEqual({
|
||||||
|
badgeRadius: 5,
|
||||||
|
badgeTextSize: 30,
|
||||||
|
badgeWidth: 140,
|
||||||
|
textLineHeight: 40,
|
||||||
|
textOffsetY: 50,
|
||||||
|
width: 152,
|
||||||
|
});
|
||||||
|
expect(BANGDREAM_DIFFICULTY_DETAIL_LIST_SPEC.list.spacing).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates badge spec with historical uppercase text', () => {
|
||||||
|
expect(createDifficultyDetailBadgeSpec('expert', '#ff0000')).toEqual({
|
||||||
|
color: '#ff0000',
|
||||||
|
radius: 5,
|
||||||
|
text: 'EXPERT',
|
||||||
|
textSize: 30,
|
||||||
|
width: 140,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps text drawing spec stable', () => {
|
||||||
|
expect(createDifficultyDetailTextSpec()).toEqual({
|
||||||
|
lineHeight: 40,
|
||||||
|
maxWidth: 152,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps item layout offsets stable', () => {
|
||||||
|
expect(createDifficultyDetailItemLayout({ height: 72, width: 88 })).toEqual({
|
||||||
|
badgeX: 6,
|
||||||
|
badgeY: 0,
|
||||||
|
canvasHeight: 122,
|
||||||
|
canvasWidth: 152,
|
||||||
|
textX: 32,
|
||||||
|
textY: 50,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps list frame values derived from the first item', () => {
|
||||||
|
expect(createDifficultyDetailListFrameSpec({ height: 122, width: 152 })).toEqual(
|
||||||
|
{
|
||||||
|
lineHeight: 122,
|
||||||
|
spacing: 0,
|
||||||
|
textSize: 122,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user