refactor: 收口 BangDream 渲染主题资源
This commit is contained in:
parent
9f0293a977
commit
31e0da78cf
@ -339,6 +339,13 @@ export interface TsuguHook {
|
||||
- 关键图片命令输出非空,尺寸在既有范围。
|
||||
- 视觉 token 修改只改 theme/spec 文件。
|
||||
|
||||
当前进度:
|
||||
|
||||
- 已新增 `render-blocks/theme.ts`,先收口公共文字颜色、分割线颜色、简易背景色、图表背景/文字色和默认字体名。
|
||||
- 已新增 `render-blocks/layout-spec.ts`,统一横向/纵向虚线分割规格,歌曲列表、活动列表、活动详情和通用列表框架不再重复维护分割线宽高/颜色。
|
||||
- 已新增 `runtime/asset-manifest.ts`,收口本地 `BG`、`Card`、`Skill`、`SongChart`、字体和标题资源路径;`canvas/text.ts`、`canvas/rect.ts`、`canvas/output.ts`、`canvas/background.ts`、`card-art.ts`、`list-rarity.ts`、`skill-text.ts`、`title.ts`、`song-chart-preview.ts` 已改走 manifest。
|
||||
- 已生成查曲、查活动和查谱面 smoke 图片,验证 theme/spec/manifest 第一段迁移后本地图片输出非空,谱面预览资源路径未断。
|
||||
|
||||
### Phase 6:策略 policy 和时间/档线规则
|
||||
|
||||
目标:服务器优先级、国服预估、档位、时区和活动状态从工具函数变成可测试规则。
|
||||
|
||||
@ -2,9 +2,9 @@ import { createBlurredTrianglePattern } from '@/qqbot/plugins/bangDream/tsugu/ca
|
||||
import { scatterImages } from '@/qqbot/plugins/bangDream/tsugu/canvas/background-star-scatter';
|
||||
import { drawTextOnCanvas } from '@/qqbot/plugins/bangDream/tsugu/canvas/background-text';
|
||||
import { loadImage, Image, Canvas } from 'skia-canvas';
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import * as path from 'path';
|
||||
import { loadImageFromPath } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
|
||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
|
||||
interface BackgroundOptions {
|
||||
image?: Image | Canvas | any;
|
||||
@ -118,14 +118,10 @@ let defaultBGTexture: Image;
|
||||
* 在底层绘图工具层中加载图片Once。
|
||||
*/
|
||||
async function loadImageOnce() {
|
||||
star.push(
|
||||
await loadImageFromPath(path.join(assetsRootPath, '/BG/star1.png')),
|
||||
);
|
||||
star.push(
|
||||
await loadImageFromPath(path.join(assetsRootPath, '/BG/star2.png')),
|
||||
);
|
||||
star.push(await loadImageFromPath(getBangDreamAssetPath('backgroundStar1')));
|
||||
star.push(await loadImageFromPath(getBangDreamAssetPath('backgroundStar2')));
|
||||
defaultBGTexture = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/BG/bg_object_big.png'),
|
||||
getBangDreamAssetPath('backgroundObjectBig'),
|
||||
);
|
||||
}
|
||||
loadImageOnce();
|
||||
@ -136,7 +132,7 @@ loadImageOnce();
|
||||
* @param options1 - options1参数。
|
||||
*/
|
||||
export async function createEasyBackground({ width, height }) {
|
||||
const bgColor = '#fef3ef';
|
||||
const bgColor = BANGDREAM_RENDER_THEME.color.backgroundEasy;
|
||||
const canvas: Canvas = new Canvas(width, height);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = bgColor;
|
||||
|
||||
@ -4,9 +4,8 @@ import {
|
||||
createEasyBackground,
|
||||
createImageBackground,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/canvas/background';
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import * as path from 'path';
|
||||
import { loadImageFromPath } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
|
||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||
|
||||
let BGDefaultImage: Image;
|
||||
/**
|
||||
@ -14,7 +13,7 @@ let BGDefaultImage: Image;
|
||||
*/
|
||||
async function loadImageOnce() {
|
||||
BGDefaultImage = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/BG/live.png'),
|
||||
getBangDreamAssetPath('backgroundLive'),
|
||||
);
|
||||
}
|
||||
loadImageOnce();
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { Canvas, FontLibrary } from 'skia-canvas';
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { getTextWidth } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
|
||||
FontLibrary.use('old', [`${assetsRootPath}/Fonts/old.ttf`]);
|
||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
|
||||
FontLibrary.use('old', [getBangDreamAssetPath('fontOld')]);
|
||||
|
||||
interface RoundedRect {
|
||||
width: number;
|
||||
@ -24,7 +26,7 @@ export function drawRoundedRect({
|
||||
width,
|
||||
height,
|
||||
radius = 25,
|
||||
color = '#ffffff',
|
||||
color = BANGDREAM_RENDER_THEME.color.surface,
|
||||
opacity = 0.9,
|
||||
strokeColor = '#bbbbbb',
|
||||
strokeWidth = 0,
|
||||
@ -119,14 +121,14 @@ interface RoundedRectWithText {
|
||||
*/
|
||||
export function drawRoundedRectWithText({
|
||||
text,
|
||||
font = 'old',
|
||||
textColor = '#ffffff',
|
||||
font = BANGDREAM_RENDER_THEME.font.body,
|
||||
textColor = BANGDREAM_RENDER_THEME.color.surface,
|
||||
textSize,
|
||||
textAlign = 'center',
|
||||
height = (textSize * 4) / 3,
|
||||
width = getTextWidth(text, textSize, font) + height,
|
||||
radius = height / 2,
|
||||
color = '#5b5b5b',
|
||||
color = BANGDREAM_RENDER_THEME.color.labelBackground,
|
||||
opacity = 1,
|
||||
strokeColor = color,
|
||||
strokeWidth = 0,
|
||||
@ -144,7 +146,7 @@ export function drawRoundedRectWithText({
|
||||
|
||||
ctx.fillStyle = textColor;
|
||||
ctx.textBaseline = 'alphabetic';
|
||||
ctx.font = `${textSize}px old,Microsoft Yahei`;
|
||||
ctx.font = `${textSize}px ${font},${BANGDREAM_RENDER_THEME.font.fallback}`;
|
||||
|
||||
let x = 0,
|
||||
y = 0;
|
||||
|
||||
@ -4,10 +4,12 @@ import {
|
||||
Canvas,
|
||||
CanvasRenderingContext2D,
|
||||
} from 'skia-canvas';
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
FontLibrary.use('old', [`${assetsRootPath}/Fonts/old.ttf`]);
|
||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
|
||||
FontLibrary.use('old', [getBangDreamAssetPath('fontOld')]);
|
||||
FontLibrary.use('FangZhengHeiTi', [
|
||||
`${assetsRootPath}/Fonts/FangZhengHeiTi_GBK.ttf`,
|
||||
getBangDreamAssetPath('fontFangZhengHeiTi'),
|
||||
]);
|
||||
|
||||
interface WrapTextOptions {
|
||||
@ -31,8 +33,8 @@ export function drawText({
|
||||
textSize = 40,
|
||||
maxWidth,
|
||||
lineHeight = (textSize * 4) / 3,
|
||||
color = '#505050',
|
||||
font = 'old',
|
||||
color = BANGDREAM_RENDER_THEME.color.primaryText,
|
||||
font = BANGDREAM_RENDER_THEME.font.body,
|
||||
}: WrapTextOptions): Canvas {
|
||||
const wrappedTextData = wrapText({ text, maxWidth, lineHeight, textSize });
|
||||
let canvas: Canvas;
|
||||
@ -131,8 +133,8 @@ export function drawTextWithImages({
|
||||
lineHeight = (textSize * 4) / 3,
|
||||
content,
|
||||
spacing = textSize / 3,
|
||||
color = '#505050',
|
||||
font = 'old',
|
||||
color = BANGDREAM_RENDER_THEME.color.primaryText,
|
||||
font = BANGDREAM_RENDER_THEME.font.body,
|
||||
}: TextWithImagesOptions) {
|
||||
const wrappedTextData = wrapTextWithImages({
|
||||
textSize,
|
||||
@ -305,5 +307,5 @@ export const setFontStyle = function (
|
||||
font: string,
|
||||
) {
|
||||
//设置字体大小
|
||||
ctx.font = textSize + 'px ' + font + ',Microsoft Yahei';
|
||||
ctx.font = `${textSize}px ${font},${BANGDREAM_RENDER_THEME.font.fallback}`;
|
||||
};
|
||||
|
||||
@ -36,18 +36,12 @@ import {
|
||||
drawSongListInList,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-song';
|
||||
import { drawDottedLine } from '@/qqbot/plugins/bangDream/tsugu/canvas/dotted-line';
|
||||
import { createHorizontalSeparatorSpec } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/layout-spec';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
|
||||
const songSeparatorLine = drawDottedLine({
|
||||
width: 800,
|
||||
height: 10,
|
||||
startX: 5,
|
||||
startY: 5,
|
||||
endX: 795,
|
||||
endY: 5,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: '#a8a8a8',
|
||||
});
|
||||
const songSeparatorLine = drawDottedLine(
|
||||
createHorizontalSeparatorSpec({ height: 10 }),
|
||||
);
|
||||
|
||||
/**
|
||||
* 在QQBot 图片视图层中绘制歌曲列表数据块。
|
||||
@ -393,7 +387,7 @@ export async function drawEventDetail(
|
||||
const eventBannerImage = await event.getBannerImage();
|
||||
const eventBannerImageCanvas = drawBannerImageCanvas(eventBannerImage);
|
||||
list.push(eventBannerImageCanvas);
|
||||
list.push(new Canvas(800, 30));
|
||||
list.push(new Canvas(BANGDREAM_RENDER_THEME.layout.contentWidth, 30));
|
||||
|
||||
//标题
|
||||
list.push(
|
||||
|
||||
@ -38,22 +38,15 @@ import { statConfig } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-s
|
||||
import { globalDefaultServer } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { createTsuguEntityMatcher } from '@/qqbot/plugins/bangDream/tsugu/search/entity-list-matcher';
|
||||
import { eventRepository } from '@/qqbot/plugins/bangDream/tsugu/models/event-repository';
|
||||
import { createVerticalSeparatorSpec } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/layout-spec';
|
||||
|
||||
const maxHeight = 7000;
|
||||
const maxColumns = 7;
|
||||
|
||||
//表格用默认虚线
|
||||
export const line2: Canvas = drawDottedLine({
|
||||
width: 30,
|
||||
height: 7000,
|
||||
startX: 5,
|
||||
startY: 0,
|
||||
endX: 15,
|
||||
endY: 6995,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: '#a8a8a8',
|
||||
});
|
||||
export const line2: Canvas = drawDottedLine(
|
||||
createVerticalSeparatorSpec(7000, { startX: 5 }),
|
||||
);
|
||||
|
||||
/**
|
||||
* 在QQBot 图片视图层中绘制活动列表。
|
||||
|
||||
@ -15,32 +15,16 @@ import { globalDefaultServer } from '@/qqbot/plugins/bangDream/tsugu/runtime/con
|
||||
import { drawSongDetail } from './song-detail';
|
||||
import { createTsuguEntityMatcher } from '@/qqbot/plugins/bangDream/tsugu/search/entity-list-matcher';
|
||||
import { songRepository } from '@/qqbot/plugins/bangDream/tsugu/models/song-repository';
|
||||
import {
|
||||
createHorizontalSeparatorSpec,
|
||||
createVerticalSeparatorSpec,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/layout-spec';
|
||||
|
||||
// 紧凑化虚线分割
|
||||
const line = drawDottedLine({
|
||||
width: 800,
|
||||
height: 10,
|
||||
startX: 5,
|
||||
startY: 5,
|
||||
endX: 795,
|
||||
endY: 5,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: '#a8a8a8',
|
||||
});
|
||||
const line = drawDottedLine(createHorizontalSeparatorSpec({ height: 10 }));
|
||||
|
||||
//表格用默认竖向虚线
|
||||
const line2: Canvas = drawDottedLine({
|
||||
width: 30,
|
||||
height: 6000,
|
||||
startX: 10,
|
||||
startY: 0,
|
||||
endX: 15,
|
||||
endY: 5990,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: '#a8a8a8',
|
||||
});
|
||||
const line2: Canvas = drawDottedLine(createVerticalSeparatorSpec(6000));
|
||||
|
||||
/**
|
||||
* 在QQBot 图片视图层中绘制歌曲列表。
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { setFontStyle } from '@/qqbot/plugins/bangDream/tsugu/canvas/text';
|
||||
import { Band } from '@/qqbot/plugins/bangDream/tsugu/models/band';
|
||||
import { Attribute } from '@/qqbot/plugins/bangDream/tsugu/models/attribute';
|
||||
@ -6,10 +5,11 @@ import { Card } from '@/qqbot/plugins/bangDream/tsugu/models/card';
|
||||
import { Image, Canvas, loadImage } from 'skia-canvas';
|
||||
import { downloadFileCache } from '@/qqbot/plugins/bangDream/tsugu/data-clients/asset-cache-client';
|
||||
import { drawCardIconSkill } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/skill-text';
|
||||
import * as path from 'path';
|
||||
import { Skill } from '@/qqbot/plugins/bangDream/tsugu/models/skill';
|
||||
import { bestdoriUrl } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { loadImageFromPath } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
|
||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
|
||||
const cardTypeIconList: { [type: string]: Image } = {};
|
||||
const starList: { [type: string]: Image } = {};
|
||||
@ -20,25 +20,23 @@ let limitBreakIcon: Image;
|
||||
*/
|
||||
async function loadImageOnce() {
|
||||
cardTypeIconList.limited = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/L.png'),
|
||||
getBangDreamAssetPath('cardLimited'),
|
||||
);
|
||||
cardTypeIconList.dreamfes = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/D.png'),
|
||||
getBangDreamAssetPath('cardDreamfes'),
|
||||
);
|
||||
cardTypeIconList.kirafes = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/K.png'),
|
||||
getBangDreamAssetPath('cardKirafes'),
|
||||
);
|
||||
cardTypeIconList.birthday = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/B.png'),
|
||||
);
|
||||
starList.normal = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/star.png'),
|
||||
getBangDreamAssetPath('cardBirthday'),
|
||||
);
|
||||
starList.normal = await loadImageFromPath(getBangDreamAssetPath('cardStar'));
|
||||
starList.trained = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/star_trained.png'),
|
||||
getBangDreamAssetPath('cardStarTrained'),
|
||||
);
|
||||
limitBreakIcon = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/limitBreakRank.png'),
|
||||
getBangDreamAssetPath('cardLimitBreakRank'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -136,17 +134,17 @@ export async function drawCardIcon({
|
||||
if (cardIdVisible) {
|
||||
ctx.textAlign = 'start';
|
||||
ctx.textBaseline = 'middle';
|
||||
setFontStyle(ctx, 30, 'old');
|
||||
ctx.fillStyle = '#a7a7a7';
|
||||
setFontStyle(ctx, 30, BANGDREAM_RENDER_THEME.font.body);
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.mutedText;
|
||||
ctx.fillText(`ID:${card.cardId}`, 4, 195);
|
||||
}
|
||||
//如果显示技能类型,在右上显示
|
||||
if (skillLevel != undefined) {
|
||||
ctx.fillStyle = '#ff0000';
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.skillLevelBackground;
|
||||
ctx.fillRect(138, 91, 35, 39);
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.surface;
|
||||
ctx.textAlign = 'center';
|
||||
setFontStyle(ctx, 35, 'old');
|
||||
setFontStyle(ctx, 35, BANGDREAM_RENDER_THEME.font.body);
|
||||
ctx.fillText(skillLevel.toString(), 155.5, 107.5);
|
||||
}
|
||||
//如果显示技能类型,在右上显示
|
||||
@ -169,8 +167,8 @@ export async function drawCardIcon({
|
||||
ctx.drawImage(bandIcon, 0, 0, 45, 45);
|
||||
if (limitBreakRank != 0) {
|
||||
ctx.drawImage(limitBreakIcon, 137, 51, 39, 39);
|
||||
setFontStyle(ctx, 25, 'old');
|
||||
ctx.fillStyle = '#ffffff';
|
||||
setFontStyle(ctx, 25, BANGDREAM_RENDER_THEME.font.body);
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.surface;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(limitBreakRank.toString(), 155, 70);
|
||||
|
||||
@ -47,30 +47,22 @@ import {
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { Canvas, Image } from 'skia-canvas';
|
||||
import { Band } from '@/qqbot/plugins/bangDream/tsugu/models/band';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
import { createHorizontalSeparatorSpec } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/layout-spec';
|
||||
|
||||
const songDetailSeparator = drawDottedLine({
|
||||
width: 365,
|
||||
height: 20,
|
||||
startX: 5,
|
||||
startY: 10,
|
||||
endX: 360,
|
||||
endY: 10,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: '#a8a8a8',
|
||||
});
|
||||
const songDetailSeparator = drawDottedLine(
|
||||
createHorizontalSeparatorSpec({
|
||||
width: 365,
|
||||
height: 20,
|
||||
endX: 360,
|
||||
}),
|
||||
);
|
||||
|
||||
const songMetaSeparator = drawDottedLine({
|
||||
width: 800,
|
||||
height: 10,
|
||||
startX: 5,
|
||||
startY: 5,
|
||||
endX: 795,
|
||||
endY: 5,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: '#a8a8a8',
|
||||
});
|
||||
const songMetaSeparator = drawDottedLine(
|
||||
createHorizontalSeparatorSpec({
|
||||
height: 10,
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* 在图片布局层中绘制横幅Info块。
|
||||
@ -327,7 +319,7 @@ export async function drawCharacterHalfBlock(
|
||||
await character.initFull(false);
|
||||
const color = character.colorCode
|
||||
? character.colorCode.toLowerCase()
|
||||
: '#ffffff';
|
||||
: BANGDREAM_RENDER_THEME.color.surface;
|
||||
ctx.drawImage(
|
||||
drawRoundedRect({
|
||||
width,
|
||||
@ -382,14 +374,14 @@ export async function drawCharacterHalfBlock(
|
||||
const nameTextImage = drawText({
|
||||
text: character.characterName[server],
|
||||
textSize: 40,
|
||||
color: '#ffffff',
|
||||
color: BANGDREAM_RENDER_THEME.color.surface,
|
||||
maxWidth: width,
|
||||
});
|
||||
list.push(drawImageListCenter([nameTextImage], width));
|
||||
const idTextImage = drawText({
|
||||
text: `ID: ${character.characterId}`,
|
||||
textSize: 30,
|
||||
color: '#ffffff',
|
||||
color: BANGDREAM_RENDER_THEME.color.surface,
|
||||
maxWidth: width,
|
||||
});
|
||||
list.push(drawImageListCenter([idTextImage], width));
|
||||
@ -409,13 +401,13 @@ export async function drawPlayerDetailBlockWithIllustration(
|
||||
const list: Array<Canvas | Image> = [];
|
||||
const playerText = drawText({
|
||||
text: player.profile.userName,
|
||||
maxWidth: 800,
|
||||
maxWidth: BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
textSize: 75,
|
||||
});
|
||||
list.push(drawImageListCenter([playerText]));
|
||||
const levelText = drawText({
|
||||
text: `等级 ${player.profile.rank}`,
|
||||
maxWidth: 800,
|
||||
maxWidth: BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
textSize: 35,
|
||||
});
|
||||
list.push(drawImageListCenter([levelText]));
|
||||
@ -436,7 +428,7 @@ export async function drawPlayerDetailBlockWithIllustration(
|
||||
|
||||
const introductionText = drawText({
|
||||
text: player.profile.introduction,
|
||||
maxWidth: 800,
|
||||
maxWidth: BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
textSize: 35,
|
||||
});
|
||||
list.push(drawImageListCenter([introductionText]));
|
||||
@ -447,7 +439,7 @@ export async function drawPlayerDetailBlockWithIllustration(
|
||||
: 'ID未公开';
|
||||
const idText = drawTextWithImages({
|
||||
content: [await getIcon(player.server), userId],
|
||||
maxWidth: 800,
|
||||
maxWidth: BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
textSize: 35,
|
||||
});
|
||||
list.push(drawImageListCenter([idText]));
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
|
||||
interface SeparatorSpecOptions {
|
||||
width?: number;
|
||||
height?: number;
|
||||
startX?: number;
|
||||
endX?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建横向虚线分割规格。
|
||||
*
|
||||
* @param options - 分割线宽高和横向端点。
|
||||
*/
|
||||
export function createHorizontalSeparatorSpec({
|
||||
width = BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
height = 30,
|
||||
startX = 5,
|
||||
endX = width - 5,
|
||||
}: SeparatorSpecOptions = {}) {
|
||||
const y = height / 2;
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
startX,
|
||||
startY: y,
|
||||
endX,
|
||||
endY: y,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: BANGDREAM_RENDER_THEME.color.separator,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建纵向虚线分割规格。
|
||||
*
|
||||
* @param height - 分割线高度。
|
||||
* @param options - 纵向端点配置。
|
||||
*/
|
||||
export function createVerticalSeparatorSpec(
|
||||
height: number,
|
||||
options: Pick<SeparatorSpecOptions, 'startX' | 'endX'> = {},
|
||||
) {
|
||||
const { startX = 10, endX = 15 } = options;
|
||||
return {
|
||||
width: 30,
|
||||
height,
|
||||
startX,
|
||||
startY: 0,
|
||||
endX,
|
||||
endY: height - 10,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: BANGDREAM_RENDER_THEME.color.separator,
|
||||
};
|
||||
}
|
||||
@ -12,19 +12,14 @@ import {
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/models/server';
|
||||
import { stackImageHorizontal } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/image-stack';
|
||||
import { globalDefaultServer } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
import {
|
||||
createHorizontalSeparatorSpec,
|
||||
createVerticalSeparatorSpec,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/layout-spec';
|
||||
|
||||
//表格用默认虚线
|
||||
export const line: Canvas = drawDottedLine({
|
||||
width: 800,
|
||||
height: 30,
|
||||
startX: 5,
|
||||
startY: 15,
|
||||
endX: 795,
|
||||
endY: 15,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: '#a8a8a8',
|
||||
});
|
||||
export const line: Canvas = drawDottedLine(createHorizontalSeparatorSpec());
|
||||
|
||||
interface ListOptions {
|
||||
key?: string;
|
||||
@ -51,8 +46,8 @@ export function drawList({
|
||||
textSize = 40,
|
||||
lineHeight = textSize * 1.5,
|
||||
spacing = textSize / 3,
|
||||
color = '#505050',
|
||||
maxWidth = 800,
|
||||
color = BANGDREAM_RENDER_THEME.color.primaryText,
|
||||
maxWidth = BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
}: ListOptions): Canvas {
|
||||
const xmax = maxWidth - 40;
|
||||
const keyImage = drawRoundedRectWithText({
|
||||
@ -76,14 +71,21 @@ export function drawList({
|
||||
textImage = new Canvas(0, 0);
|
||||
}
|
||||
if (key == undefined) {
|
||||
return stackImageHorizontal([new Canvas(20, 1), textImage]);
|
||||
return stackImageHorizontal([
|
||||
new Canvas(BANGDREAM_RENDER_THEME.layout.listIndent, 1),
|
||||
textImage,
|
||||
]);
|
||||
}
|
||||
const ymax = textImage.height + keyImage.height + 10;
|
||||
const canvas = new Canvas(maxWidth, ymax);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(keyImage, 0, 0);
|
||||
if (textImage.height != 0) {
|
||||
ctx.drawImage(textImage, 20, keyImage.height + 10);
|
||||
ctx.drawImage(
|
||||
textImage,
|
||||
BANGDREAM_RENDER_THEME.layout.listIndent,
|
||||
keyImage.height + 10,
|
||||
);
|
||||
}
|
||||
return canvas;
|
||||
}
|
||||
@ -107,7 +109,9 @@ export function drawTipsInList({
|
||||
lineHeight = textSize * 1.5,
|
||||
spacing = textSize / 3,
|
||||
}: tipsOptions) {
|
||||
const xmax = 760;
|
||||
const xmax =
|
||||
BANGDREAM_RENDER_THEME.layout.contentWidth -
|
||||
BANGDREAM_RENDER_THEME.layout.listIndent * 2;
|
||||
let textImage: Canvas;
|
||||
if (typeof text == 'string') {
|
||||
textImage = drawText({ text, textSize, maxWidth: xmax, lineHeight });
|
||||
@ -122,11 +126,19 @@ export function drawTipsInList({
|
||||
} else {
|
||||
textImage = new Canvas(1, 1);
|
||||
}
|
||||
const canvas = new Canvas(800, textImage.height + 10);
|
||||
const canvas = new Canvas(
|
||||
BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
textImage.height + 10,
|
||||
);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#f1f1f1';
|
||||
ctx.fillRect(0, 10, 800, textImage.height);
|
||||
ctx.drawImage(textImage, 20, 10);
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.subtlePanel;
|
||||
ctx.fillRect(
|
||||
0,
|
||||
10,
|
||||
BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
textImage.height,
|
||||
);
|
||||
ctx.drawImage(textImage, BANGDREAM_RENDER_THEME.layout.listIndent, 10);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
@ -142,7 +154,7 @@ export async function drawListByServerList(
|
||||
content: Array<string | null>,
|
||||
key?: string,
|
||||
serverList: Server[] = globalDefaultServer,
|
||||
maxWidth = 800,
|
||||
maxWidth: number = BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
) {
|
||||
const tempcontent: Array<string | Image | Canvas> = [];
|
||||
|
||||
@ -211,13 +223,16 @@ export function drawListMerge(imageList: Array<Canvas | Image>): Canvas {
|
||||
maxHeight = element.height;
|
||||
}
|
||||
}
|
||||
const canvas = new Canvas(800, maxHeight);
|
||||
const canvas = new Canvas(
|
||||
BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
maxHeight,
|
||||
);
|
||||
const ctx = canvas.getContext('2d');
|
||||
let x = 0;
|
||||
for (let i = 0; i < imageList.length; i++) {
|
||||
const element = imageList[i];
|
||||
ctx.drawImage(element, x, 0);
|
||||
x += 800 / imageList.length;
|
||||
x += BANGDREAM_RENDER_THEME.layout.contentWidth / imageList.length;
|
||||
}
|
||||
return canvas;
|
||||
}
|
||||
@ -232,7 +247,7 @@ export function drawListMerge(imageList: Array<Canvas | Image>): Canvas {
|
||||
*/
|
||||
export function drawImageListCenter(
|
||||
imageList: Array<Canvas | Image>,
|
||||
maxWidth = 800,
|
||||
maxWidth: number = BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
): Canvas {
|
||||
interface imageLine {
|
||||
imageList: Array<Canvas | Image>;
|
||||
@ -320,10 +335,14 @@ export function drawListWithLine(textImageList: Array<Canvas | Image>): Canvas {
|
||||
const element = textImageList[i];
|
||||
height += element.height;
|
||||
}
|
||||
const canvas = new Canvas(800, height + 10);
|
||||
const canvas = new Canvas(
|
||||
BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
height + 10,
|
||||
);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#a8a8a8';
|
||||
ctx.fillRect(10, 10, 5, height + 20);
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.separator;
|
||||
const lineSpec = createVerticalSeparatorSpec(height + 20);
|
||||
ctx.fillRect(lineSpec.startX, 10, 5, height + 20);
|
||||
for (let i = 0; i < textImageList.length; i++) {
|
||||
const element = textImageList[i];
|
||||
ctx.drawImage(element, x, y);
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { Canvas, Image } from 'skia-canvas';
|
||||
import { drawList } from './list-frame';
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import * as path from 'path';
|
||||
import { loadImageFromPath } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
|
||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||
|
||||
interface RarityInListOptions {
|
||||
key?: string;
|
||||
@ -16,11 +15,9 @@ export const starList: { [type: string]: Image } = {};
|
||||
* 在图片布局层中加载图片Once。
|
||||
*/
|
||||
async function loadImageOnce() {
|
||||
starList.normal = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/star.png'),
|
||||
);
|
||||
starList.normal = await loadImageFromPath(getBangDreamAssetPath('cardStar'));
|
||||
starList.trained = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/star_trained.png'),
|
||||
getBangDreamAssetPath('cardStarTrained'),
|
||||
);
|
||||
}
|
||||
loadImageOnce();
|
||||
|
||||
@ -11,6 +11,8 @@ import { drawDifficultyList, drawDifficulty } from './list-difficulty';
|
||||
import { globalDefaultServer } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { drawList } from './list-frame';
|
||||
import { drawDottedLine } from '@/qqbot/plugins/bangDream/tsugu/canvas/dotted-line';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
import { createHorizontalSeparatorSpec } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/layout-spec';
|
||||
|
||||
/**
|
||||
* 在图片布局层中绘制歌曲In列表。
|
||||
@ -34,7 +36,7 @@ export async function drawSongInList(
|
||||
heightMax: 80,
|
||||
});
|
||||
|
||||
const canvas = new Canvas(800, 75);
|
||||
const canvas = new Canvas(BANGDREAM_RENDER_THEME.layout.contentWidth, 75);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(songImage, 50, 5, 65, 65);
|
||||
//id
|
||||
@ -42,7 +44,7 @@ export async function drawSongInList(
|
||||
text: song.songId.toString(),
|
||||
textSize: 23,
|
||||
lineHeight: 37.5,
|
||||
maxWidth: 800,
|
||||
maxWidth: BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
});
|
||||
ctx.drawImage(idImage, 0, 0);
|
||||
//曲名与乐队名
|
||||
@ -58,7 +60,7 @@ export async function drawSongInList(
|
||||
text: fullText,
|
||||
textSize: 23,
|
||||
lineHeight: 37.5,
|
||||
maxWidth: 800,
|
||||
maxWidth: BANGDREAM_RENDER_THEME.layout.contentWidth,
|
||||
});
|
||||
ctx.drawImage(textImage, 120, 0);
|
||||
|
||||
@ -69,7 +71,7 @@ export async function drawSongInList(
|
||||
: drawDifficulty(difficulty, song.difficulty[difficulty].playLevel, 45);
|
||||
ctx.drawImage(
|
||||
difficultyImage,
|
||||
800 - difficultyImage.width,
|
||||
BANGDREAM_RENDER_THEME.layout.contentWidth - difficultyImage.width,
|
||||
75 / 2 - difficultyImage.height / 2,
|
||||
);
|
||||
return canvas;
|
||||
@ -96,17 +98,7 @@ export async function drawSongListInList(
|
||||
const x = 0;
|
||||
let y = 0;
|
||||
const views: Canvas[] = [];
|
||||
const line = drawDottedLine({
|
||||
width: 800,
|
||||
height: 10,
|
||||
startX: 5,
|
||||
startY: 5,
|
||||
endX: 795,
|
||||
endY: 5,
|
||||
radius: 2,
|
||||
gap: 10,
|
||||
color: '#a8a8a8',
|
||||
});
|
||||
const line = drawDottedLine(createHorizontalSeparatorSpec({ height: 10 }));
|
||||
for (let i = 0; i < songs.length; i++) {
|
||||
views.push(
|
||||
resizeImage({
|
||||
|
||||
@ -1,23 +1,21 @@
|
||||
import { Skill } from '@/qqbot/plugins/bangDream/tsugu/models/skill';
|
||||
import { Image, Canvas } from 'skia-canvas';
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { drawTextWithImages } from '@/qqbot/plugins/bangDream/tsugu/canvas/text';
|
||||
import * as path from 'path';
|
||||
import { loadImageFromPath } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
|
||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
|
||||
const skillIcon: { [skillType: string]: Image } = {};
|
||||
/**
|
||||
* 在图片布局层中加载图片Once。
|
||||
*/
|
||||
async function loadImageOnce() {
|
||||
skillIcon.life = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Skill/life.png'),
|
||||
);
|
||||
skillIcon.life = await loadImageFromPath(getBangDreamAssetPath('skillLife'));
|
||||
skillIcon.judge = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Skill/judge.png'),
|
||||
getBangDreamAssetPath('skillJudge'),
|
||||
);
|
||||
skillIcon.damage = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Skill/damage.png'),
|
||||
getBangDreamAssetPath('skillDamage'),
|
||||
);
|
||||
}
|
||||
loadImageOnce();
|
||||
@ -70,11 +68,11 @@ export async function drawCardIconSkill(skill: Skill): Promise<Canvas> {
|
||||
textSize: 27,
|
||||
lineHeight: 30,
|
||||
spacing: 3,
|
||||
color: '#ffffff',
|
||||
font: 'old',
|
||||
color: BANGDREAM_RENDER_THEME.color.surface,
|
||||
font: BANGDREAM_RENDER_THEME.font.body,
|
||||
});
|
||||
const textbase = await loadImageFromPath(
|
||||
path.join(assetsRootPath, '/Card/text.png'),
|
||||
getBangDreamAssetPath('cardSkillTextBase'),
|
||||
);
|
||||
const canvas = new Canvas(stringWithImage.width + 15, 45);
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import { Canvas, Image, loadImage } from 'skia-canvas';
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { loadImageFromPath } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
|
||||
import {
|
||||
BangDreamLocalAssetKey,
|
||||
getBangDreamAssetPath,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
|
||||
interface BestdoriPreviewPayload {
|
||||
id: number;
|
||||
@ -70,6 +74,23 @@ const NOTE_IMAGE_KEYS = [
|
||||
'RightArrow',
|
||||
'RightArrowEnd',
|
||||
] as const;
|
||||
const NOTE_IMAGE_ASSET_KEYS: Record<
|
||||
(typeof NOTE_IMAGE_KEYS)[number],
|
||||
BangDreamLocalAssetKey
|
||||
> = {
|
||||
Flick: 'songChartNoteFlick',
|
||||
FlickTop: 'songChartNoteFlickTop',
|
||||
LeftArrow: 'songChartNoteLeftArrow',
|
||||
LeftArrowEnd: 'songChartNoteLeftArrowEnd',
|
||||
Long: 'songChartNoteLong',
|
||||
RightArrow: 'songChartNoteRightArrow',
|
||||
RightArrowEnd: 'songChartNoteRightArrowEnd',
|
||||
Sim: 'songChartNoteSim',
|
||||
Single: 'songChartNoteSingle',
|
||||
SingleOff: 'songChartNoteSingleOff',
|
||||
Skill: 'songChartNoteSkill',
|
||||
Tick: 'songChartNoteTick',
|
||||
};
|
||||
const DISPLAY_NOTE_TYPES = [
|
||||
'Single',
|
||||
'SingleOff',
|
||||
@ -451,7 +472,9 @@ async function loadNoteImages(): Promise<Record<string, Image>> {
|
||||
const entries = await Promise.all(
|
||||
NOTE_IMAGE_KEYS.map(async (key) => [
|
||||
key,
|
||||
await loadImageFromPath(`${assetsRootPath}/SongChart/note/${key}.png`),
|
||||
await loadImageFromPath(
|
||||
getBangDreamAssetPath(NOTE_IMAGE_ASSET_KEYS[key]),
|
||||
),
|
||||
]),
|
||||
);
|
||||
return Object.fromEntries(entries);
|
||||
@ -467,7 +490,7 @@ async function loadCoverImage(cover: string | Buffer): Promise<Image> {
|
||||
try {
|
||||
return await loadImage(cover);
|
||||
} catch {
|
||||
return await loadImageFromPath(`${assetsRootPath}/SongChart/jacket.png`);
|
||||
return await loadImageFromPath(getBangDreamAssetPath('songChartJacket'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -526,7 +549,7 @@ function drawBaseInfo(
|
||||
): void {
|
||||
const { id, diff, level } = payload;
|
||||
ctx.save();
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.chartBackground;
|
||||
ctx.fillRect(0, 0, layout.width, layout.height);
|
||||
ctx.restore();
|
||||
|
||||
@ -539,10 +562,10 @@ function drawBaseInfo(
|
||||
);
|
||||
|
||||
ctx.save();
|
||||
ctx.fillStyle = '#1f1e33';
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.chartPanel;
|
||||
ctx.fillRect(OFFSET - 8, OFFSET - 8, 128, 24);
|
||||
ctx.fillStyle = '#FFF';
|
||||
ctx.font = '16px "Arial"';
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.chartText;
|
||||
ctx.font = `16px "${BANGDREAM_RENDER_THEME.font.chart}"`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(`${id}`, OFFSET + 56, OFFSET + 4, 128);
|
||||
@ -550,10 +573,12 @@ function drawBaseInfo(
|
||||
|
||||
const coverWidth = layout.infoAreaWidth - 16;
|
||||
ctx.save();
|
||||
ctx.fillStyle = DIFFICULTY_COLOR_LIST[diff] ?? '#777';
|
||||
ctx.fillStyle =
|
||||
DIFFICULTY_COLOR_LIST[diff] ??
|
||||
BANGDREAM_RENDER_THEME.color.chartDifficultyFallback;
|
||||
ctx.fillRect(8 + coverWidth - 116, 8 + coverWidth - 12, 128, 24);
|
||||
ctx.fillStyle = '#FFF';
|
||||
ctx.font = '16px "Arial"';
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.chartText;
|
||||
ctx.font = `16px "${BANGDREAM_RENDER_THEME.font.chart}"`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(`${diff} ${level}`, 8 + coverWidth - 52, 8 + coverWidth, 128);
|
||||
@ -657,8 +682,8 @@ function drawBeatLines(
|
||||
*/
|
||||
function drawTimeline(ctx: any, layout: PreviewLayout): void {
|
||||
ctx.save();
|
||||
ctx.font = '18px "Arial"';
|
||||
ctx.fillStyle = '#FFF';
|
||||
ctx.font = `18px "${BANGDREAM_RENDER_THEME.font.chart}"`;
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.chartText;
|
||||
ctx.textAlign = 'right';
|
||||
for (let i = 0; i <= layout.chartLength; i += 5) {
|
||||
const { x, y } = getTimePosition(layout, i);
|
||||
@ -692,20 +717,20 @@ function drawCountAndBpmLines(
|
||||
if (count % 50 !== 0) {
|
||||
continue;
|
||||
}
|
||||
ctx.font = '18px "Arial"';
|
||||
ctx.font = `18px "${BANGDREAM_RENDER_THEME.font.chart}"`;
|
||||
ctx.fillStyle = 'rgba(128, 128, 128, 0.5)';
|
||||
ctx.textAlign = 'left';
|
||||
ctx.fillRect(x, y - 1, w, 2);
|
||||
ctx.fillStyle = '#FFF';
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.chartText;
|
||||
setAdaptiveTextBaseline(ctx, layout, 18, y);
|
||||
ctx.fillText(`${count}`, x + w + 8, y);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (note.type === 'BPM') {
|
||||
ctx.fillStyle = '#C34FBB';
|
||||
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.chartBpm;
|
||||
ctx.fillRect(x, y - 1, w, 2);
|
||||
ctx.font = '18px "Arial"';
|
||||
ctx.font = `18px "${BANGDREAM_RENDER_THEME.font.chart}"`;
|
||||
ctx.textAlign = 'left';
|
||||
setAdaptiveTextBaseline(ctx, layout, 18, y);
|
||||
ctx.fillText(`${note.bpm}`, x + w + 8, y);
|
||||
|
||||
31
src/qqbot/plugins/bangDream/tsugu/render-blocks/theme.ts
Normal file
31
src/qqbot/plugins/bangDream/tsugu/render-blocks/theme.ts
Normal file
@ -0,0 +1,31 @@
|
||||
export const BANGDREAM_RENDER_THEME = {
|
||||
color: {
|
||||
backgroundEasy: '#fef3ef',
|
||||
chartBackground: '#000',
|
||||
chartBpm: '#C34FBB',
|
||||
chartDifficultyFallback: '#777',
|
||||
chartPanel: '#1f1e33',
|
||||
chartText: '#FFF',
|
||||
labelBackground: '#5b5b5b',
|
||||
mutedText: '#a7a7a7',
|
||||
primaryText: '#505050',
|
||||
separator: '#a8a8a8',
|
||||
skillLevelBackground: '#ff0000',
|
||||
subtlePanel: '#f1f1f1',
|
||||
surface: '#ffffff',
|
||||
},
|
||||
font: {
|
||||
body: 'old',
|
||||
chart: 'Arial',
|
||||
chinese: 'FangZhengHeiTi',
|
||||
fallback: 'Microsoft Yahei',
|
||||
},
|
||||
layout: {
|
||||
contentWidth: 800,
|
||||
defaultGap: 20,
|
||||
listIndent: 20,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type BangDreamRenderFont =
|
||||
(typeof BANGDREAM_RENDER_THEME.font)[keyof typeof BANGDREAM_RENDER_THEME.font];
|
||||
@ -1,15 +1,15 @@
|
||||
import { Canvas, Image } from 'skia-canvas';
|
||||
import { drawText } from '@/qqbot/plugins/bangDream/tsugu/canvas/text';
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import * as path from 'path';
|
||||
import { loadImageFromPath } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
|
||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||
|
||||
let titleImage: Image;
|
||||
/**
|
||||
* 在图片布局层中加载图片Once。
|
||||
*/
|
||||
async function loadImageOnce() {
|
||||
titleImage = await loadImageFromPath(path.join(assetsRootPath, '/title.png'));
|
||||
titleImage = await loadImageFromPath(getBangDreamAssetPath('title'));
|
||||
}
|
||||
loadImageOnce();
|
||||
|
||||
@ -29,16 +29,16 @@ export function drawTitle(title1: string, title2: string): Canvas {
|
||||
maxWidth: 900,
|
||||
lineHeight: 50,
|
||||
textSize: 30,
|
||||
color: '#ffffff',
|
||||
font: 'old',
|
||||
color: BANGDREAM_RENDER_THEME.color.surface,
|
||||
font: BANGDREAM_RENDER_THEME.font.body,
|
||||
});
|
||||
const text2 = drawText({
|
||||
text: title2,
|
||||
maxWidth: 900,
|
||||
lineHeight: 68,
|
||||
textSize: 40,
|
||||
color: '#5b5b5b',
|
||||
font: 'old',
|
||||
color: BANGDREAM_RENDER_THEME.color.labelBackground,
|
||||
font: BANGDREAM_RENDER_THEME.font.body,
|
||||
});
|
||||
ctx.drawImage(text1, 74, 0);
|
||||
ctx.drawImage(text2, 74, 42);
|
||||
|
||||
49
src/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest.ts
Normal file
49
src/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import * as path from 'path';
|
||||
import { assetsRootPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
|
||||
export const BANGDREAM_LOCAL_ASSETS = {
|
||||
backgroundLive: 'BG/live.png',
|
||||
backgroundObjectBig: 'BG/bg_object_big.png',
|
||||
backgroundStar1: 'BG/star1.png',
|
||||
backgroundStar2: 'BG/star2.png',
|
||||
cardBirthday: 'Card/B.png',
|
||||
cardDreamfes: 'Card/D.png',
|
||||
cardKirafes: 'Card/K.png',
|
||||
cardLimited: 'Card/L.png',
|
||||
cardLimitBreakRank: 'Card/limitBreakRank.png',
|
||||
cardSkillTextBase: 'Card/text.png',
|
||||
cardStar: 'Card/star.png',
|
||||
cardStarTrained: 'Card/star_trained.png',
|
||||
fontFangZhengHeiTi: 'Fonts/FangZhengHeiTi_GBK.ttf',
|
||||
fontOld: 'Fonts/old.ttf',
|
||||
skillDamage: 'Skill/damage.png',
|
||||
skillJudge: 'Skill/judge.png',
|
||||
skillLife: 'Skill/life.png',
|
||||
songChartJacket: 'SongChart/jacket.png',
|
||||
songChartNoteBar: 'SongChart/note/Bar.png',
|
||||
songChartNoteFlick: 'SongChart/note/Flick.png',
|
||||
songChartNoteFlickTop: 'SongChart/note/FlickTop.png',
|
||||
songChartNoteLeftArrow: 'SongChart/note/LeftArrow.png',
|
||||
songChartNoteLeftArrowEnd: 'SongChart/note/LeftArrowEnd.png',
|
||||
songChartNoteLong: 'SongChart/note/Long.png',
|
||||
songChartNoteRightArrow: 'SongChart/note/RightArrow.png',
|
||||
songChartNoteRightArrowEnd: 'SongChart/note/RightArrowEnd.png',
|
||||
songChartNoteSim: 'SongChart/note/Sim.png',
|
||||
songChartNoteSingle: 'SongChart/note/Single.png',
|
||||
songChartNoteSingleOff: 'SongChart/note/SingleOff.png',
|
||||
songChartNoteSkill: 'SongChart/note/Skill.png',
|
||||
songChartNoteTick: 'SongChart/note/Tick.png',
|
||||
title: 'title.png',
|
||||
twServerIcon: 'tw.png',
|
||||
} as const;
|
||||
|
||||
export type BangDreamLocalAssetKey = keyof typeof BANGDREAM_LOCAL_ASSETS;
|
||||
|
||||
/**
|
||||
* 解析 Tsugu 本地资源路径。
|
||||
*
|
||||
* @param key - 本地资源 manifest 键名。
|
||||
*/
|
||||
export function getBangDreamAssetPath(key: BangDreamLocalAssetKey): string {
|
||||
return path.join(assetsRootPath, BANGDREAM_LOCAL_ASSETS[key]);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user