kt-template-online-api/src/qqbot/plugins/bangDream/tsugu/render-blocks/list-frame.ts

329 lines
9.5 KiB
TypeScript
Raw 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.

import { Canvas, Image } from 'skia-canvas';
import { drawRoundedRectWithText } from '@/qqbot/plugins/bangDream/tsugu/canvas/rect';
import {
drawText,
drawTextWithImages,
} from '@/qqbot/plugins/bangDream/tsugu/canvas/text';
import { drawDottedLine } from '@/qqbot/plugins/bangDream/tsugu/canvas/dotted-line';
import {
Server,
getServerByPriority,
getIcon,
} 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';
import {
BANGDREAM_LIST_FRAME_SPEC,
createCenteredImageRows,
createKeyedListFrameLayout,
createListWithLineLayout,
createTipsInListLayout,
getCenteredImageRowsHeight,
getListFrameLineHeight,
getListFrameSpacing,
getListFrameTextMaxWidth,
getMergedListColumnWidth,
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-frame-spec';
//表格用默认虚线
export const line: Canvas = drawDottedLine(createHorizontalSeparatorSpec());
interface ListOptions {
key?: string;
text?: string;
content?: Array<string | Canvas | Image>;
textSize?: number;
lineHeight?: number;
spacing?: number;
color?: string;
maxWidth?: number;
}
//画表格中的一行
/**
* 在图片布局层中绘制列表。
*
* @param options1 - options1参数。
* @returns 渲染或资源结果。
*/
export function drawList({
key,
text,
content,
textSize = BANGDREAM_LIST_FRAME_SPEC.text.defaultSize,
lineHeight = getListFrameLineHeight(textSize),
spacing = getListFrameSpacing(textSize),
color = BANGDREAM_RENDER_THEME.color.primaryText,
maxWidth = BANGDREAM_RENDER_THEME.layout.contentWidth,
}: ListOptions): Canvas {
const xmax = getListFrameTextMaxWidth(maxWidth);
const keyImage = drawRoundedRectWithText({
text: key,
textSize: BANGDREAM_LIST_FRAME_SPEC.text.labelSize,
});
let textImage: Canvas;
if (typeof text == 'string') {
textImage = drawText({ text, maxWidth: xmax, lineHeight });
} else if (content != undefined) {
textImage = drawTextWithImages({
content,
maxWidth: xmax,
lineHeight,
textSize,
spacing,
color,
});
} else {
textImage = new Canvas(
BANGDREAM_LIST_FRAME_SPEC.list.emptyTextWidth,
BANGDREAM_LIST_FRAME_SPEC.list.emptyTextHeight,
);
}
if (key == undefined) {
return stackImageHorizontal([
new Canvas(
BANGDREAM_RENDER_THEME.layout.listIndent,
BANGDREAM_LIST_FRAME_SPEC.list.noKeySpacerHeight,
),
textImage,
]);
}
const layout = createKeyedListFrameLayout({
keyHeight: keyImage.height,
maxWidth,
textHeight: textImage.height,
});
const canvas = new Canvas(layout.width, layout.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(keyImage, layout.keyX, layout.keyY);
if (textImage.height != 0) {
ctx.drawImage(textImage, layout.textX, layout.textY);
}
return canvas;
}
interface tipsOptions {
text?: string;
content?: Array<string | Canvas | Image>;
textSize?: number;
lineHeight?: number;
spacing?: number;
}
/**
* 在图片布局层中绘制TipsIn列表。
*
* @param options1 - options1参数。
*/
export function drawTipsInList({
text,
content,
textSize = BANGDREAM_LIST_FRAME_SPEC.tips.defaultTextSize,
lineHeight = getListFrameLineHeight(textSize),
spacing = getListFrameSpacing(textSize),
}: tipsOptions) {
const layout = createTipsInListLayout(0);
const xmax = layout.textMaxWidth;
let textImage: Canvas;
if (typeof text == 'string') {
textImage = drawText({ text, textSize, maxWidth: xmax, lineHeight });
} else if (content != undefined) {
textImage = drawTextWithImages({
textSize,
content,
maxWidth: xmax,
lineHeight,
spacing,
});
} else {
textImage = new Canvas(
BANGDREAM_LIST_FRAME_SPEC.tips.emptyTextWidth,
BANGDREAM_LIST_FRAME_SPEC.tips.emptyTextHeight,
);
}
const textLayout = createTipsInListLayout(textImage.height);
const canvas = new Canvas(textLayout.width, textLayout.height);
const ctx = canvas.getContext('2d');
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.subtlePanel;
ctx.fillRect(
textLayout.backgroundX,
textLayout.backgroundY,
textLayout.backgroundWidth,
textLayout.backgroundHeight,
);
ctx.drawImage(textImage, textLayout.textX, textLayout.textY);
return canvas;
}
/**
* 在图片布局层中绘制列表By服务器列表。
*
* @param content - content参数。
* @param key - 当前字段键名,未传入时使用默认值。
* @param serverList - 服务器列表参数,未传入时使用默认值。
* @param maxWidth - max宽度参数未传入时使用默认值。
*/
export async function drawListByServerList(
content: Array<string | null>,
key?: string,
serverList: Server[] = globalDefaultServer,
maxWidth: number = BANGDREAM_RENDER_THEME.layout.contentWidth,
) {
const tempcontent: Array<string | Image | Canvas> = [];
// 获取每个服务器的内容对应关系
const contentMap = new Map<string, Server[]>();
// 分组服务器,根据相同的内容将服务器归类
for (let i = 0; i < serverList.length; i++) {
const tempServer = serverList[i];
const serverContent = content[tempServer];
if (serverContent == null) {
continue;
}
if (!contentMap.has(serverContent)) {
contentMap.set(serverContent, []);
}
contentMap.get(serverContent)?.push(tempServer);
}
// 遍历内容分组
for (const [serverContent, servers] of contentMap) {
if (servers.length > 0) {
// 对于同一组内容,只需要绘制一次图标和内容
for (let i = 0; i < servers.length; i++) {
tempcontent.push(await getIcon(servers[i]));
}
// 添加对应的内容
tempcontent.push(serverContent);
tempcontent.push('\n');
}
}
// 如果所有服务器内容都为空,选择优先级最高的服务器
if (tempcontent.length == 0) {
const tempServer = getServerByPriority(content, serverList);
tempcontent.push(await getIcon(tempServer));
tempcontent.push(content[tempServer]);
tempcontent.push('\n');
}
// 去掉最后一个换行符
tempcontent.pop();
const canvas = drawList({
key: key,
content: tempcontent,
maxWidth,
});
return canvas;
}
//横向组合较短list高度为最高的list宽度平分
/**
* 在图片布局层中绘制列表Merge。
*
* @param imageList - 图片列表参数。
* @returns 渲染或资源结果。
*/
export function drawListMerge(imageList: Array<Canvas | Image>): Canvas {
let maxHeight = 0;
for (let i = 0; i < imageList.length; i++) {
const element = imageList[i];
if (element.height > maxHeight) {
maxHeight = element.height;
}
}
const canvas = new Canvas(
BANGDREAM_LIST_FRAME_SPEC.merge.defaultWidth,
maxHeight,
);
const ctx = canvas.getContext('2d');
let x = 0;
const columnWidth = getMergedListColumnWidth(imageList.length);
for (let i = 0; i < imageList.length; i++) {
const element = imageList[i];
ctx.drawImage(element, x, 0);
x += columnWidth;
}
return canvas;
}
//横向组合image/canvas array居中超过宽度则换行
/**
* 在图片布局层中绘制图片列表Center。
*
* @param imageList - 图片列表参数。
* @param maxWidth - max宽度参数未传入时使用默认值。
* @returns 渲染或资源结果。
*/
export function drawImageListCenter(
imageList: Array<Canvas | Image>,
maxWidth: number = BANGDREAM_RENDER_THEME.layout.contentWidth,
): Canvas {
if (imageList.length == 0) {
return new Canvas(
BANGDREAM_LIST_FRAME_SPEC.imageList.emptyWidth,
BANGDREAM_LIST_FRAME_SPEC.imageList.emptyHeight,
);
}
const lineList = createCenteredImageRows(imageList, maxWidth);
const height = getCenteredImageRowsHeight(lineList);
const canvas = new Canvas(maxWidth, height);
const ctx = canvas.getContext('2d');
//画每一行
const middleWidth = maxWidth / 2;
let y = 0;
for (let i = 0; i < lineList.length; i++) {
const element = lineList[i];
let x = middleWidth - element.width / 2;
for (let j = 0; j < element.imageList.length; j++) {
const image = element.imageList[j];
ctx.drawImage(image, x, y);
x += image.width;
}
y += element.height;
}
return canvas;
}
//画左侧有竖线的排版用于画block时展示数据
/**
* 在图片布局层中绘制列表With线条。
*
* @param textImageList - 文本图片列表参数。
* @returns 渲染或资源结果。
*/
export function drawListWithLine(textImageList: Array<Canvas | Image>): Canvas {
let height = 0;
for (let i = 0; i < textImageList.length; i++) {
const element = textImageList[i];
height += element.height;
}
const layout = createListWithLineLayout(height);
const canvas = new Canvas(layout.canvasWidth, layout.canvasHeight);
const ctx = canvas.getContext('2d');
ctx.fillStyle = BANGDREAM_RENDER_THEME.color.separator;
const lineSpec = createVerticalSeparatorSpec(layout.lineHeight);
ctx.fillRect(
lineSpec.startX,
layout.lineY,
layout.lineWidth,
layout.lineHeight,
);
let y = layout.contentY;
for (let i = 0; i < textImageList.length; i++) {
const element = textImageList[i];
ctx.drawImage(element, layout.contentX, y);
y += element.height;
}
return canvas;
}