refactor: 收口 BangDream 角色详情列表规格

This commit is contained in:
sunlei 2026-06-07 05:11:17 +08:00
parent 97d21767ae
commit c26aeeb921
4 changed files with 138 additions and 16 deletions

View File

@ -391,6 +391,7 @@ export interface TsuguHook {
- 已新增 `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 输出非空。
- 已新增 `render-blocks/list-band-detail-spec.ts`,收口玩家详情乐队等级、舞台挑战达成情况和乐队编成等级列表的 Logo 缩放、正文宽度/行高、项画布、通用列表框架和 Rank 等级图片布局;`list-band-detail.ts` 改为消费 spec玩家详情里的乐队详情绘制顺序、资源 repository 和输出结构保持不变;`list-band-detail-spec.spec.ts` 覆盖 Logo/正文参数、项布局、列表框架、Rank 画布/图片位置和等级图片 Rank ID 封顶,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
- 已新增 `render-blocks/list-character-detail-spec.ts`,收口玩家详情角色等级列表的头像缩放、正文宽度/行高、项画布和通用列表框架;`list-character-detail.ts` 改为消费 spec玩家详情角色等级绘制顺序、角色数据读取和输出结构保持不变`list-character-detail-spec.spec.ts` 覆盖头像/正文参数、项布局和列表框架参数,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
- smoke/Jenkins/远程调试卡点继续按已固化规则处理同一卡点第二次尝试前必须改变可验证变量Jenkins 查询 URL 含方括号时用 `curl -g` 或改查 Jenkins home线上图片 smoke 必须查询 `commandId`、拉回图片、展示图片、查日志并清理本轮临时目录PowerShell 双引号 here-string 中不要写 JS `${...}` 模板字面量,避免被 PowerShell 提前插值smoke 没有真实图片落盘时必须失败。
### Phase 6策略 policy 和时间/档线规则

View File

@ -0,0 +1,67 @@
interface ImageLike {
height: number;
width: number;
}
export const BANGDREAM_CHARACTER_DETAIL_LIST_SPEC = {
item: {
height: 100,
iconWidth: 50,
textLineHeight: 40,
textOffsetY: 50,
width: 76,
},
list: {
spacing: 0,
},
} as const;
/**
*
*/
export function createCharacterDetailIconSpec() {
return {
widthMax: BANGDREAM_CHARACTER_DETAIL_LIST_SPEC.item.iconWidth,
};
}
/**
*
*/
export function createCharacterDetailTextSpec() {
const item = BANGDREAM_CHARACTER_DETAIL_LIST_SPEC.item;
return {
lineHeight: item.textLineHeight,
maxWidth: item.width,
};
}
/**
*
*
* @param contentImage -
*/
export function createCharacterDetailItemLayout(contentImage: ImageLike) {
const item = BANGDREAM_CHARACTER_DETAIL_LIST_SPEC.item;
return {
canvasHeight: item.height,
canvasWidth: item.width,
iconX: (item.width - item.iconWidth) / 2,
iconY: 0,
textX: item.width / 2 - contentImage.width / 2,
textY: item.textOffsetY,
};
}
/**
*
*
* @param firstItem -
*/
export function createCharacterDetailListFrameSpec(firstItem?: ImageLike) {
return {
lineHeight: firstItem?.height,
spacing: BANGDREAM_CHARACTER_DETAIL_LIST_SPEC.list.spacing,
textSize: firstItem?.height,
};
}

View File

@ -5,6 +5,12 @@ import { resizeImage } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/image
import { drawTextWithImages } from '@/qqbot/plugins/bangDream/tsugu/canvas/text';
import { Character } from '@/qqbot/plugins/bangDream/tsugu/models/character';
import mainAPI from '@/qqbot/plugins/bangDream/tsugu/models/main-data-store';
import {
createCharacterDetailIconSpec,
createCharacterDetailItemLayout,
createCharacterDetailListFrameSpec,
createCharacterDetailTextSpec,
} from './list-character-detail-spec';
interface drawBandDetailsInListOptions {
[characterId: number]: Array<Canvas | Image | string>;
@ -24,33 +30,32 @@ async function drawCharacterInList(
for (const i in CharacterDetailsInListOptions) {
const tempCharacter = new Character(parseInt(i));
const content = CharacterDetailsInListOptions[i];
const maxWidth = 76;
const logoWidth = 50;
const tempCharacterIcon = resizeImage({
image: await tempCharacter.getIcon(),
widthMax: logoWidth,
...createCharacterDetailIconSpec(),
});
const canvas = new Canvas(maxWidth, 100);
const ctx = canvas.getContext('2d');
ctx.drawImage(tempCharacterIcon, (maxWidth - logoWidth) / 2, 0);
const textSpec = createCharacterDetailTextSpec();
const tempCharacterRankText = drawTextWithImages({
content,
maxWidth: maxWidth,
lineHeight: 40,
maxWidth: textSpec.maxWidth,
lineHeight: textSpec.lineHeight,
});
ctx.drawImage(
tempCharacterRankText,
maxWidth / 2 - tempCharacterRankText.width / 2,
50,
);
const layout = createCharacterDetailItemLayout(tempCharacterRankText);
const canvas = new Canvas(layout.canvasWidth, layout.canvasHeight);
const ctx = canvas.getContext('2d');
ctx.drawImage(tempCharacterIcon, layout.iconX, layout.iconY);
ctx.drawImage(tempCharacterRankText, layout.textX, layout.textY);
characterAndContentList.push(canvas);
}
const frameSpec = createCharacterDetailListFrameSpec(
characterAndContentList?.[0],
);
const characterAndContentListImage = drawList({
key,
content: characterAndContentList,
spacing: 0,
lineHeight: characterAndContentList?.[0].height,
textSize: characterAndContentList?.[0].height,
spacing: frameSpec.spacing,
lineHeight: frameSpec.lineHeight,
textSize: frameSpec.textSize,
});
return characterAndContentListImage;
}

View File

@ -0,0 +1,49 @@
import {
BANGDREAM_CHARACTER_DETAIL_LIST_SPEC,
createCharacterDetailIconSpec,
createCharacterDetailItemLayout,
createCharacterDetailListFrameSpec,
createCharacterDetailTextSpec,
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-character-detail-spec';
describe('BangDream character detail list spec', () => {
it('keeps character detail item constants stable', () => {
expect(BANGDREAM_CHARACTER_DETAIL_LIST_SPEC.item).toEqual({
height: 100,
iconWidth: 50,
textLineHeight: 40,
textOffsetY: 50,
width: 76,
});
expect(BANGDREAM_CHARACTER_DETAIL_LIST_SPEC.list.spacing).toBe(0);
});
it('creates icon and text drawing specs', () => {
expect(createCharacterDetailIconSpec()).toEqual({ widthMax: 50 });
expect(createCharacterDetailTextSpec()).toEqual({
lineHeight: 40,
maxWidth: 76,
});
});
it('keeps character detail item layout stable', () => {
expect(createCharacterDetailItemLayout({ height: 36, width: 32 })).toEqual({
canvasHeight: 100,
canvasWidth: 76,
iconX: 13,
iconY: 0,
textX: 22,
textY: 50,
});
});
it('keeps list frame values derived from the first item', () => {
expect(
createCharacterDetailListFrameSpec({ height: 100, width: 76 }),
).toEqual({
lineHeight: 100,
spacing: 0,
textSize: 100,
});
});
});