mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-18 17:48:52 +08:00
refactor: 字体类
This commit is contained in:
parent
4c898a7836
commit
ee5b962743
@ -1,5 +1,6 @@
|
|||||||
export * from './preset';
|
export * from './preset';
|
||||||
export * from './renderer';
|
export * from './renderer';
|
||||||
|
export * from './style';
|
||||||
export * from './adapter';
|
export * from './adapter';
|
||||||
export * from './cache';
|
export * from './cache';
|
||||||
export * from './camera';
|
export * from './camera';
|
||||||
|
@ -6,6 +6,7 @@ import { texture } from '../cache';
|
|||||||
import { isNil } from 'lodash-es';
|
import { isNil } from 'lodash-es';
|
||||||
import { logger } from '@/core/common/logger';
|
import { logger } from '@/core/common/logger';
|
||||||
import { IAnimateFrame, renderEmits } from '../frame';
|
import { IAnimateFrame, renderEmits } from '../frame';
|
||||||
|
import { Font } from '../style/font';
|
||||||
|
|
||||||
/** 文字的安全填充,会填充在文字的上侧和下侧,防止削顶和削底 */
|
/** 文字的安全填充,会填充在文字的上侧和下侧,防止削顶和削底 */
|
||||||
const SAFE_PAD = 1;
|
const SAFE_PAD = 1;
|
||||||
@ -21,7 +22,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
|
|
||||||
fillStyle?: CanvasStyle = '#fff';
|
fillStyle?: CanvasStyle = '#fff';
|
||||||
strokeStyle?: CanvasStyle;
|
strokeStyle?: CanvasStyle;
|
||||||
font: string = '16px Verdana';
|
font: Font = new Font();
|
||||||
strokeWidth: number = 1;
|
strokeWidth: number = 1;
|
||||||
|
|
||||||
private length: number = 0;
|
private length: number = 0;
|
||||||
@ -50,7 +51,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
ctx.textBaseline = 'bottom';
|
ctx.textBaseline = 'bottom';
|
||||||
ctx.fillStyle = this.fillStyle ?? 'transparent';
|
ctx.fillStyle = this.fillStyle ?? 'transparent';
|
||||||
ctx.strokeStyle = this.strokeStyle ?? 'transparent';
|
ctx.strokeStyle = this.strokeStyle ?? 'transparent';
|
||||||
ctx.font = this.font;
|
ctx.font = this.font.string();
|
||||||
ctx.lineWidth = this.strokeWidth;
|
ctx.lineWidth = this.strokeWidth;
|
||||||
|
|
||||||
if (this.strokeStyle) {
|
if (this.strokeStyle) {
|
||||||
@ -67,7 +68,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
measure() {
|
measure() {
|
||||||
const ctx = Text.measureCanvas.ctx;
|
const ctx = Text.measureCanvas.ctx;
|
||||||
ctx.textBaseline = 'bottom';
|
ctx.textBaseline = 'bottom';
|
||||||
ctx.font = this.font;
|
ctx.font = this.font.string();
|
||||||
const res = ctx.measureText(this.text);
|
const res = ctx.measureText(this.text);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -87,7 +88,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
* 设置使用的字体
|
* 设置使用的字体
|
||||||
* @param font 字体
|
* @param font 字体
|
||||||
*/
|
*/
|
||||||
setFont(font: string) {
|
setFont(font: Font) {
|
||||||
this.font = font;
|
this.font = font;
|
||||||
this.calBox();
|
this.calBox();
|
||||||
this.update(this);
|
this.update(this);
|
||||||
@ -145,7 +146,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
this.setStyle(this.fillStyle, nextValue);
|
this.setStyle(this.fillStyle, nextValue);
|
||||||
return true;
|
return true;
|
||||||
case 'font':
|
case 'font':
|
||||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
if (!this.assertType(nextValue, Font, key)) return false;
|
||||||
this.setFont(nextValue);
|
this.setFont(nextValue);
|
||||||
return true;
|
return true;
|
||||||
case 'strokeWidth':
|
case 'strokeWidth':
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
} from '../preset/graphics';
|
} from '../preset/graphics';
|
||||||
import { ElementAnchor, ElementLocator, ElementScale } from '../utils';
|
import { ElementAnchor, ElementLocator, ElementScale } from '../utils';
|
||||||
import { CustomContainerRenderFn } from '../container';
|
import { CustomContainerRenderFn } from '../container';
|
||||||
|
import { Font } from '../style/font';
|
||||||
|
|
||||||
export interface CustomProps {
|
export interface CustomProps {
|
||||||
_item: (props: BaseProps) => RenderItem;
|
_item: (props: BaseProps) => RenderItem;
|
||||||
@ -100,7 +101,7 @@ export interface TextProps extends BaseProps {
|
|||||||
text?: string;
|
text?: string;
|
||||||
fillStyle?: CanvasStyle;
|
fillStyle?: CanvasStyle;
|
||||||
strokeStyle?: CanvasStyle;
|
strokeStyle?: CanvasStyle;
|
||||||
font?: string;
|
font?: Font;
|
||||||
strokeWidth?: number;
|
strokeWidth?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
155
src/core/render/style/font.ts
Normal file
155
src/core/render/style/font.ts
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import { logger } from '@/core/common/logger';
|
||||||
|
|
||||||
|
export interface IFontConfig {
|
||||||
|
/** 字体类型 */
|
||||||
|
readonly family: string;
|
||||||
|
/** 字体大小的值 */
|
||||||
|
readonly size: number;
|
||||||
|
/** 字体大小单位,推荐使用 px */
|
||||||
|
readonly sizeUnit: string;
|
||||||
|
/** 字体粗细,范围 0-1000 */
|
||||||
|
readonly weight: number;
|
||||||
|
/** 是否斜体 */
|
||||||
|
readonly italic: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum FontWeight {
|
||||||
|
Light = 300,
|
||||||
|
Normal = 400,
|
||||||
|
Bold = 700
|
||||||
|
}
|
||||||
|
|
||||||
|
type _FontStretch =
|
||||||
|
| 'ultra-condensed'
|
||||||
|
| 'extra-condensed'
|
||||||
|
| 'condensed'
|
||||||
|
| 'semi-condensed'
|
||||||
|
| 'normal'
|
||||||
|
| 'semi-expanded'
|
||||||
|
| 'expanded'
|
||||||
|
| 'extra-expanded'
|
||||||
|
| 'ultra-expanded';
|
||||||
|
|
||||||
|
type _FontVariant = 'normal' | 'small-caps';
|
||||||
|
|
||||||
|
export class Font implements IFontConfig {
|
||||||
|
private readonly fallbacks: Font[] = [];
|
||||||
|
|
||||||
|
private fontString: string = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly family: string = 'Verdana',
|
||||||
|
public readonly size: number = 16,
|
||||||
|
public readonly sizeUnit: string = 'px',
|
||||||
|
public readonly weight: number = 400,
|
||||||
|
public readonly italic: boolean = false
|
||||||
|
) {
|
||||||
|
this.fontString = this.getFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加补充字体,若当前字体不可用,那么会使用补充字体,补充字体也可以添加补充字体,但是请避免递归添加
|
||||||
|
* @param fallback 补充字体
|
||||||
|
*/
|
||||||
|
addFallback(...fallback: Font[]) {
|
||||||
|
this.fallbacks.push(...fallback);
|
||||||
|
this.fontString = this.getFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
private build() {
|
||||||
|
return `${
|
||||||
|
this.italic ? 'italic ' : ''
|
||||||
|
} ${this.weight} ${this.size}${this.sizeUnit} ${this.family}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFallbackFont(used: Set<Font>) {
|
||||||
|
let font = '';
|
||||||
|
this.fallbacks.forEach(v => {
|
||||||
|
if (used.has(v)) {
|
||||||
|
logger.warn(62, this.build());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
used.add(v);
|
||||||
|
font += `, ${v.getFallbackFont(used)}`;
|
||||||
|
});
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFont() {
|
||||||
|
if (this.fallbacks.length === 0) {
|
||||||
|
return this.build();
|
||||||
|
} else {
|
||||||
|
const usedFont = new Set<Font>();
|
||||||
|
return this.build() + this.getFallbackFont(usedFont);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字体的 CSS 字符串
|
||||||
|
*/
|
||||||
|
string() {
|
||||||
|
return this.fontString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static parseOne(str: string) {
|
||||||
|
if (!str) return new Font();
|
||||||
|
let italic = false;
|
||||||
|
let weight = 400;
|
||||||
|
let size = 16;
|
||||||
|
let unit = 'px';
|
||||||
|
let family = 'Verdana';
|
||||||
|
const tokens = str.split(/\s+/);
|
||||||
|
tokens.forEach(v => {
|
||||||
|
// font-italic
|
||||||
|
if (v === 'italic') {
|
||||||
|
italic = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// font-weight
|
||||||
|
const num = Number(v);
|
||||||
|
if (!isNaN(num)) {
|
||||||
|
weight = num;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// font-size
|
||||||
|
const parse = parseFloat(v);
|
||||||
|
if (!isNaN(parse)) {
|
||||||
|
size = parse;
|
||||||
|
unit = v.slice(parse.toString().length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
family = tokens.at(-1) ?? 'Verdana';
|
||||||
|
return new Font(family, size, unit, weight, italic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 CSS 字体字符串解析出 Font 实例,不支持的属性将被忽略
|
||||||
|
*/
|
||||||
|
static parse(str: string) {
|
||||||
|
const fonts = str.split(',');
|
||||||
|
const main = this.parseOne(fonts[0]);
|
||||||
|
for (let i = 1; i < fonts.length; i++) {
|
||||||
|
main.addFallback(this.parseOne(fonts[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制一个字体,同时修改字体的一部分属性
|
||||||
|
* @param font 要复制的字体
|
||||||
|
*/
|
||||||
|
static clone(
|
||||||
|
font: Font,
|
||||||
|
{
|
||||||
|
family = font.family,
|
||||||
|
size = font.size,
|
||||||
|
sizeUnit = font.sizeUnit,
|
||||||
|
weight = font.weight,
|
||||||
|
italic = font.italic
|
||||||
|
}: Partial<IFontConfig>
|
||||||
|
) {
|
||||||
|
return new Font(family, size, sizeUnit, weight, italic);
|
||||||
|
}
|
||||||
|
}
|
1
src/core/render/style/index.ts
Normal file
1
src/core/render/style/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './font';
|
@ -92,7 +92,8 @@
|
|||||||
"58": "Fail to set ellipse round rect, since length of 'ellipse' property should only be 2, 4, 6 or 8. delivered: $1",
|
"58": "Fail to set ellipse round rect, since length of 'ellipse' property should only be 2, 4, 6 or 8. delivered: $1",
|
||||||
"59": "Unknown icon '$1' in parsing text content.",
|
"59": "Unknown icon '$1' in parsing text content.",
|
||||||
"60": "Repeated Tip id: '$1'.",
|
"60": "Repeated Tip id: '$1'.",
|
||||||
"61": "Unexpected recursive call of $1.update in render function. Please ensure you must do this, if you do, ignore this warn.",
|
"61": "Unexpected recursive call of $1.update in render function. Please ensure you have to do this, if you do, ignore this warn.",
|
||||||
|
"62": "Recursive fallback fonts in '$1'.",
|
||||||
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
|
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
|
||||||
"1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance."
|
"1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance."
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
export * from './choices';
|
||||||
|
export * from './icons';
|
||||||
|
export * from './misc';
|
||||||
|
export * from './page';
|
||||||
|
export * from './scroll';
|
||||||
export * from './textbox';
|
export * from './textbox';
|
||||||
export * from './textboxTyper';
|
export * from './textboxTyper';
|
||||||
|
export * from './tip';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { SetupComponentOptions } from './types';
|
import { SetupComponentOptions } from './types';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
import { DefaultProps, ElementLocator } from '@/core/render';
|
import { DefaultProps, ElementLocator, Font } from '@/core/render';
|
||||||
|
|
||||||
/** 圆角矩形页码距离容器的边框大小,与 pageSize 相乘 */
|
/** 圆角矩形页码距离容器的边框大小,与 pageSize 相乘 */
|
||||||
const RECT_PAD = 0.1;
|
const RECT_PAD = 0.1;
|
||||||
@ -20,8 +20,10 @@ export interface PageProps extends DefaultProps {
|
|||||||
pages: number;
|
pages: number;
|
||||||
/** 页码组件的定位 */
|
/** 页码组件的定位 */
|
||||||
loc: ElementLocator;
|
loc: ElementLocator;
|
||||||
/** 页码的字体大小,默认为 14 */
|
/** 页码的字体 */
|
||||||
pageSize?: number;
|
font?: Font;
|
||||||
|
/** 只有一页的时候,是否隐藏页码 */
|
||||||
|
hideIfSingle?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageExpose {
|
export interface PageExpose {
|
||||||
@ -37,7 +39,7 @@ type PageSlots = SlotsType<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
const pageProps = {
|
const pageProps = {
|
||||||
props: ['pages', 'loc', 'pageSize']
|
props: ['pages', 'loc', 'font', 'hideIfSingle']
|
||||||
} satisfies SetupComponentOptions<PageProps, {}, string, PageSlots>;
|
} satisfies SetupComponentOptions<PageProps, {}, string, PageSlots>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,14 +82,15 @@ export const Page = defineComponent<PageProps, {}, string, PageSlots>(
|
|||||||
const leftArrow = ref<Path2D>();
|
const leftArrow = ref<Path2D>();
|
||||||
const rightArrow = ref<Path2D>();
|
const rightArrow = ref<Path2D>();
|
||||||
|
|
||||||
|
const font = computed(() => props.font ?? new Font());
|
||||||
const isFirst = computed(() => nowPage.value === 1);
|
const isFirst = computed(() => nowPage.value === 1);
|
||||||
const isLast = computed(() => nowPage.value === props.pages);
|
const isLast = computed(() => nowPage.value === props.pages);
|
||||||
const pageSize = computed(() => props.pageSize ?? 14);
|
|
||||||
const width = computed(() => props.loc[2] ?? 200);
|
const width = computed(() => props.loc[2] ?? 200);
|
||||||
const height = computed(() => props.loc[3] ?? 200);
|
const height = computed(() => props.loc[3] ?? 200);
|
||||||
const round = computed(() => pageSize.value / 4);
|
const round = computed(() => font.value.size / 4);
|
||||||
const pageFont = computed(() => `${pageSize.value}px normal`);
|
const nowPageFont = computed(() =>
|
||||||
const nowPageFont = computed(() => `bold ${pageSize.value}px normal`);
|
Font.clone(font.value, { weight: 700 })
|
||||||
|
);
|
||||||
|
|
||||||
// 左右箭头的颜色
|
// 左右箭头的颜色
|
||||||
const leftColor = computed(() => (isFirst.value ? '#666' : '#ddd'));
|
const leftColor = computed(() => (isFirst.value ? '#666' : '#ddd'));
|
||||||
@ -100,11 +103,11 @@ export const Page = defineComponent<PageProps, {}, string, PageSlots>(
|
|||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
updating = false;
|
updating = false;
|
||||||
});
|
});
|
||||||
const pageH = pageSize.value + 8;
|
const pageH = font.value.size + 8;
|
||||||
contentLoc.value = [0, 0, width.value, height.value - pageH];
|
contentLoc.value = [0, 0, width.value, height.value - pageH];
|
||||||
pageLoc.value = [0, height.value - pageH, width.value, pageH];
|
pageLoc.value = [0, height.value - pageH, width.value, pageH];
|
||||||
const center = width.value / 2;
|
const center = width.value / 2;
|
||||||
const size = pageSize.value * 1.5;
|
const size = font.value.size * 1.5;
|
||||||
nowPageLoc.value = [center, 0, size, size, 0.5, 0];
|
nowPageLoc.value = [center, 0, size, size, 0.5, 0];
|
||||||
leftPageLoc.value = [center - size * 1.5, 0, size, size, 0.5, 0];
|
leftPageLoc.value = [center - size * 1.5, 0, size, size, 0.5, 0];
|
||||||
leftLoc.value = [center - size * 3, 0, size, size, 0.5, 0];
|
leftLoc.value = [center - size * 3, 0, size, size, 0.5, 0];
|
||||||
@ -113,8 +116,8 @@ export const Page = defineComponent<PageProps, {}, string, PageSlots>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateArrowPath = () => {
|
const updateArrowPath = () => {
|
||||||
const rectSize = pageSize.value * 1.5;
|
const rectSize = font.value.size * 1.5;
|
||||||
const size = pageSize.value;
|
const size = font.value.size;
|
||||||
const pad = rectSize - size;
|
const pad = rectSize - size;
|
||||||
const left = new Path2D();
|
const left = new Path2D();
|
||||||
left.moveTo(size, pad);
|
left.moveTo(size, pad);
|
||||||
@ -129,13 +132,13 @@ export const Page = defineComponent<PageProps, {}, string, PageSlots>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateRectAndText = () => {
|
const updateRectAndText = () => {
|
||||||
const size = pageSize.value * 1.5;
|
const size = font.value.size * 1.5;
|
||||||
const pad = RECT_PAD * size;
|
const pad = RECT_PAD * size;
|
||||||
rectLoc.value = [pad, pad, size - pad * 2, size - pad * 2];
|
rectLoc.value = [pad, pad, size - pad * 2, size - pad * 2];
|
||||||
textLoc.value = [size / 2, size / 2, void 0, void 0, 0.5, 0.5];
|
textLoc.value = [size / 2, size / 2, void 0, void 0, 0.5, 0.5];
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(pageSize, () => {
|
watch(font, () => {
|
||||||
updatePagePos();
|
updatePagePos();
|
||||||
updateArrowPath();
|
updateArrowPath();
|
||||||
updateRectAndText();
|
updateRectAndText();
|
||||||
@ -178,7 +181,10 @@ export const Page = defineComponent<PageProps, {}, string, PageSlots>(
|
|||||||
<container loc={contentLoc.value}>
|
<container loc={contentLoc.value}>
|
||||||
{slots.default?.(nowPage.value)}
|
{slots.default?.(nowPage.value)}
|
||||||
</container>
|
</container>
|
||||||
<container loc={pageLoc.value}>
|
<container
|
||||||
|
loc={pageLoc.value}
|
||||||
|
hidden={props.hideIfSingle && props.pages === 1}
|
||||||
|
>
|
||||||
<container
|
<container
|
||||||
loc={leftLoc.value}
|
loc={leftLoc.value}
|
||||||
onClick={lastPage}
|
onClick={lastPage}
|
||||||
@ -214,7 +220,7 @@ export const Page = defineComponent<PageProps, {}, string, PageSlots>(
|
|||||||
<text
|
<text
|
||||||
loc={textLoc.value}
|
loc={textLoc.value}
|
||||||
text={(nowPage.value - 1).toString()}
|
text={(nowPage.value - 1).toString()}
|
||||||
font={pageFont.value}
|
font={font.value}
|
||||||
></text>
|
></text>
|
||||||
</container>
|
</container>
|
||||||
)}
|
)}
|
||||||
@ -251,7 +257,7 @@ export const Page = defineComponent<PageProps, {}, string, PageSlots>(
|
|||||||
<text
|
<text
|
||||||
loc={textLoc.value}
|
loc={textLoc.value}
|
||||||
text={(nowPage.value + 1).toString()}
|
text={(nowPage.value + 1).toString()}
|
||||||
font={pageFont.value}
|
font={font.value}
|
||||||
></text>
|
></text>
|
||||||
</container>
|
</container>
|
||||||
)}
|
)}
|
||||||
|
@ -27,7 +27,7 @@ import {
|
|||||||
WordBreak,
|
WordBreak,
|
||||||
TextAlign
|
TextAlign
|
||||||
} from './textboxTyper';
|
} from './textboxTyper';
|
||||||
import { ElementLocator } from '@/core/render';
|
import { ElementLocator, Font } from '@/core/render';
|
||||||
|
|
||||||
export interface TextContentProps
|
export interface TextContentProps
|
||||||
extends DefaultProps,
|
extends DefaultProps,
|
||||||
@ -70,10 +70,7 @@ export interface TextContentExpose {
|
|||||||
const textContentOptions = {
|
const textContentOptions = {
|
||||||
props: [
|
props: [
|
||||||
'breakChars',
|
'breakChars',
|
||||||
'fontFamily',
|
'font',
|
||||||
'fontSize',
|
|
||||||
'fontWeight',
|
|
||||||
'fontItalic',
|
|
||||||
'ignoreLineEnd',
|
'ignoreLineEnd',
|
||||||
'ignoreLineStart',
|
'ignoreLineStart',
|
||||||
'interval',
|
'interval',
|
||||||
@ -225,7 +222,7 @@ export interface TextboxProps extends TextContentProps, DefaultProps {
|
|||||||
/** 标题 */
|
/** 标题 */
|
||||||
title?: string;
|
title?: string;
|
||||||
/** 标题字体 */
|
/** 标题字体 */
|
||||||
titleFont?: string;
|
titleFont?: Font;
|
||||||
/** 标题填充样式 */
|
/** 标题填充样式 */
|
||||||
titleFill?: CanvasStyle;
|
titleFill?: CanvasStyle;
|
||||||
/** 标题描边样式 */
|
/** 标题描边样式 */
|
||||||
@ -296,10 +293,7 @@ export const Textbox = defineComponent<
|
|||||||
|
|
||||||
const setContentData = () => {
|
const setContentData = () => {
|
||||||
contentData.breakChars = props.breakChars ?? '';
|
contentData.breakChars = props.breakChars ?? '';
|
||||||
contentData.fontFamily = props.fontFamily ?? 'Verdana';
|
contentData.font = props.font ?? new Font();
|
||||||
contentData.fontSize = props.fontSize ?? 16;
|
|
||||||
contentData.fontWeight = props.fontWeight ?? 500;
|
|
||||||
contentData.fontItalic = props.fontItalic ?? false;
|
|
||||||
contentData.ignoreLineEnd = props.ignoreLineEnd ?? '';
|
contentData.ignoreLineEnd = props.ignoreLineEnd ?? '';
|
||||||
contentData.ignoreLineStart = props.ignoreLineStart ?? '';
|
contentData.ignoreLineStart = props.ignoreLineStart ?? '';
|
||||||
contentData.interval = props.interval ?? 0;
|
contentData.interval = props.interval ?? 0;
|
||||||
@ -323,7 +317,7 @@ export const Textbox = defineComponent<
|
|||||||
data.padding = props.padding ?? 8;
|
data.padding = props.padding ?? 8;
|
||||||
data.titleFill = props.titleFill ?? 'gold';
|
data.titleFill = props.titleFill ?? 'gold';
|
||||||
data.titleStroke = props.titleStroke ?? 'transparent';
|
data.titleStroke = props.titleStroke ?? 'transparent';
|
||||||
data.titleFont = props.titleFont ?? '18px Verdana';
|
data.titleFont = props.titleFont ?? new Font('Verdana', 18);
|
||||||
data.titlePadding = props.titlePadding ?? 8;
|
data.titlePadding = props.titlePadding ?? 8;
|
||||||
data.width = props.width ?? props.loc?.[2] ?? 200;
|
data.width = props.width ?? props.loc?.[2] ?? 200;
|
||||||
data.height = props.height ?? props.loc?.[3] ?? 200;
|
data.height = props.height ?? props.loc?.[3] ?? 200;
|
||||||
|
@ -2,6 +2,7 @@ import { logger } from '@/core/common/logger';
|
|||||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
import {
|
import {
|
||||||
AutotileRenderable,
|
AutotileRenderable,
|
||||||
|
Font,
|
||||||
onTick,
|
onTick,
|
||||||
RenderableData,
|
RenderableData,
|
||||||
texture
|
texture
|
||||||
@ -28,14 +29,8 @@ export const enum TextAlign {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ITextContentConfig {
|
export interface ITextContentConfig {
|
||||||
/** 字体类型 */
|
/** 字体 */
|
||||||
fontFamily: string;
|
font: Font;
|
||||||
/** 字体大小 */
|
|
||||||
fontSize: number;
|
|
||||||
/** 字体线宽 */
|
|
||||||
fontWeight: number;
|
|
||||||
/** 是否斜体 */
|
|
||||||
fontItalic: boolean;
|
|
||||||
/** 是否持续上一次的文本,开启后,如果修改后的文本以修改前的文本为开头,那么会继续播放而不会从头播放 */
|
/** 是否持续上一次的文本,开启后,如果修改后的文本以修改前的文本为开头,那么会继续播放而不会从头播放 */
|
||||||
keepLast: boolean;
|
keepLast: boolean;
|
||||||
/** 打字机时间间隔,即两个字出现之间相隔多长时间 */
|
/** 打字机时间间隔,即两个字出现之间相隔多长时间 */
|
||||||
@ -62,6 +57,17 @@ export interface ITextContentConfig {
|
|||||||
width: number;
|
width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TyperConfig extends ITextContentConfig {
|
||||||
|
/** 字体类型 */
|
||||||
|
fontFamily: string;
|
||||||
|
/** 字体大小 */
|
||||||
|
fontSize: number;
|
||||||
|
/** 字体线宽 */
|
||||||
|
fontWeight: number;
|
||||||
|
/** 是否斜体 */
|
||||||
|
fontItalic: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ITextContentRenderData {
|
export interface ITextContentRenderData {
|
||||||
text: string;
|
text: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -162,7 +168,7 @@ type TyperFunction = (data: TyperRenderable[], typing: boolean) => void;
|
|||||||
|
|
||||||
export class TextContentTyper extends EventEmitter<TextContentTyperEvent> {
|
export class TextContentTyper extends EventEmitter<TextContentTyperEvent> {
|
||||||
/** 文字配置信息 */
|
/** 文字配置信息 */
|
||||||
readonly config: Required<ITextContentConfig>;
|
readonly config: Required<TyperConfig>;
|
||||||
/** 文字解析器 */
|
/** 文字解析器 */
|
||||||
readonly parser: TextContentParser;
|
readonly parser: TextContentParser;
|
||||||
|
|
||||||
@ -205,12 +211,14 @@ export class TextContentTyper extends EventEmitter<TextContentTyperEvent> {
|
|||||||
|
|
||||||
constructor(config: Partial<ITextContentConfig>) {
|
constructor(config: Partial<ITextContentConfig>) {
|
||||||
super();
|
super();
|
||||||
|
const font = config.font ?? new Font();
|
||||||
|
|
||||||
this.config = {
|
this.config = {
|
||||||
fontFamily: config.fontFamily ?? 'Verdana',
|
font,
|
||||||
fontSize: config.fontSize ?? 16,
|
fontFamily: font.family,
|
||||||
fontWeight: config.fontWeight ?? 500,
|
fontSize: font.size,
|
||||||
fontItalic: config.fontItalic ?? false,
|
fontWeight: font.weight,
|
||||||
|
fontItalic: font.italic,
|
||||||
keepLast: config.keepLast ?? false,
|
keepLast: config.keepLast ?? false,
|
||||||
interval: config.interval ?? 0,
|
interval: config.interval ?? 0,
|
||||||
lineHeight: config.lineHeight ?? 0,
|
lineHeight: config.lineHeight ?? 0,
|
||||||
@ -762,7 +770,7 @@ export class TextContentParser {
|
|||||||
|
|
||||||
private parseFontWeight() {
|
private parseFontWeight() {
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
if (this.resolved.length > 0) this.addTextRenderable();
|
||||||
this.status.fontWeight = this.status.fontWeight > 500 ? 500 : 700;
|
this.status.fontWeight = this.status.fontWeight > 400 ? 400 : 700;
|
||||||
this.font = this.buildFont();
|
this.font = this.buildFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1300,7 +1308,7 @@ function isCJK(char: number) {
|
|||||||
export function buildFont(
|
export function buildFont(
|
||||||
family: string,
|
family: string,
|
||||||
size: number,
|
size: number,
|
||||||
weight: number = 500,
|
weight: number = 400,
|
||||||
italic: boolean = false
|
italic: boolean = false
|
||||||
) {
|
) {
|
||||||
return `${italic ? 'italic ' : ''}${weight} ${size}px "${family}"`;
|
return `${italic ? 'italic ' : ''}${weight} ${size}px "${family}"`;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DefaultProps, ElementLocator, texture } from '@/core/render';
|
import { DefaultProps, ElementLocator, Font, texture } from '@/core/render';
|
||||||
import { computed, defineComponent, onUnmounted, ref } from 'vue';
|
import { computed, defineComponent, onUnmounted, ref } from 'vue';
|
||||||
import { SetupComponentOptions } from './types';
|
import { SetupComponentOptions } from './types';
|
||||||
import { transitioned } from '../use';
|
import { transitioned } from '../use';
|
||||||
@ -36,7 +36,7 @@ export const Tip = defineComponent<TipProps>((props, { expose }) => {
|
|||||||
const text = ref<string>('');
|
const text = ref<string>('');
|
||||||
const textWidth = ref(0);
|
const textWidth = ref(0);
|
||||||
|
|
||||||
const font = '16px normal';
|
const font = new Font('normal');
|
||||||
|
|
||||||
const alpha = transitioned(0, 500, hyper('sin', 'in-out'))!;
|
const alpha = transitioned(0, 500, hyper('sin', 'in-out'))!;
|
||||||
const pad = computed(() => props.pad ?? [4, 4]);
|
const pad = computed(() => props.pad ?? [4, 4]);
|
||||||
|
@ -8,7 +8,8 @@ import {
|
|||||||
HeroRenderer,
|
HeroRenderer,
|
||||||
LayerDoorAnimate,
|
LayerDoorAnimate,
|
||||||
Props,
|
Props,
|
||||||
LayerGroup
|
LayerGroup,
|
||||||
|
Font
|
||||||
} from '@/core/render';
|
} from '@/core/render';
|
||||||
import { WeatherController } from '@/module/weather';
|
import { WeatherController } from '@/module/weather';
|
||||||
import { FloorChange } from '@/plugin/fallback';
|
import { FloorChange } from '@/plugin/fallback';
|
||||||
@ -62,8 +63,8 @@ const MainScene = defineComponent(() => {
|
|||||||
zIndex: 30,
|
zIndex: 30,
|
||||||
fillStyle: '#fff',
|
fillStyle: '#fff',
|
||||||
titleFill: 'gold',
|
titleFill: 'gold',
|
||||||
fontFamily: 'normal',
|
font: new Font('normal'),
|
||||||
titleFont: '700 20px normal',
|
titleFont: new Font('normal', 20, 'px', 700),
|
||||||
winskin: 'winskin2.png',
|
winskin: 'winskin2.png',
|
||||||
interval: 100,
|
interval: 100,
|
||||||
lineHeight: 4,
|
lineHeight: 4,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { GameUI } from '@/core/system';
|
import { GameUI } from '@/core/system';
|
||||||
import { computed, defineComponent, ref, watch } from 'vue';
|
import { computed, defineComponent, ref, watch } from 'vue';
|
||||||
import { SetupComponentOptions, TextContent } from '../components';
|
import { SetupComponentOptions, TextContent } from '../components';
|
||||||
import { DefaultProps, ElementLocator, Sprite } from '@/core/render';
|
import { DefaultProps, ElementLocator, Sprite, Font } from '@/core/render';
|
||||||
import { transitionedColor } from '../use';
|
import { transitionedColor } from '../use';
|
||||||
import { linear } from 'mutate-animate';
|
import { linear } from 'mutate-animate';
|
||||||
import { Scroll } from '../components/scroll';
|
import { Scroll } from '../components/scroll';
|
||||||
@ -62,9 +62,9 @@ export const LeftStatusBar = defineComponent<StatusBarProps<ILeftHeroStatus>>(
|
|||||||
return num.toString().padStart(2, '0');
|
return num.toString().padStart(2, '0');
|
||||||
};
|
};
|
||||||
|
|
||||||
const font1 = '18px normal';
|
const font1 = new Font('normal', 18);
|
||||||
const font2 = 'bold 18px normal';
|
const font2 = new Font('normal', 18, 'px', 700);
|
||||||
const font3 = 'bold 14px normal';
|
const font3 = new Font('normal', 14, 'px', 700);
|
||||||
|
|
||||||
const iconLoc = (n: number): ElementLocator => {
|
const iconLoc = (n: number): ElementLocator => {
|
||||||
return [16, 76 + 44 * n, 32, 32];
|
return [16, 76 + 44 * n, 32, 32];
|
||||||
@ -201,8 +201,8 @@ export interface IRightHeroStatus {
|
|||||||
|
|
||||||
export const RightStatusBar = defineComponent<StatusBarProps<IRightHeroStatus>>(
|
export const RightStatusBar = defineComponent<StatusBarProps<IRightHeroStatus>>(
|
||||||
p => {
|
p => {
|
||||||
const font1 = '18px normal';
|
const font1 = new Font('normal', 18);
|
||||||
const font2 = '16px normal';
|
const font2 = new Font('normal', 16);
|
||||||
|
|
||||||
const minimap = ref<Sprite>();
|
const minimap = ref<Sprite>();
|
||||||
const inNumpad = ref(false);
|
const inNumpad = ref(false);
|
||||||
@ -340,8 +340,7 @@ export const RightStatusBar = defineComponent<StatusBarProps<IRightHeroStatus>>(
|
|||||||
<TextContent
|
<TextContent
|
||||||
loc={[10, 42, 160, 60]}
|
loc={[10, 42, 160, 60]}
|
||||||
text={skillDesc.value}
|
text={skillDesc.value}
|
||||||
fontFamily="normal"
|
font={new Font('normal', 14)}
|
||||||
fontSize={14}
|
|
||||||
width={160}
|
width={160}
|
||||||
lineHeight={4}
|
lineHeight={4}
|
||||||
></TextContent>
|
></TextContent>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DefaultProps, ElementLocator } from '@/core/render';
|
import { DefaultProps, ElementLocator, Font } from '@/core/render';
|
||||||
import { computed, defineComponent, ref } from 'vue';
|
import { computed, defineComponent, ref } from 'vue';
|
||||||
import { SetupComponentOptions } from '../components';
|
import { SetupComponentOptions } from '../components';
|
||||||
import {
|
import {
|
||||||
@ -82,7 +82,7 @@ export const PlayingToolbar = defineComponent<
|
|||||||
const loadIcon = core.statusBar.icons.load;
|
const loadIcon = core.statusBar.icons.load;
|
||||||
const setIcon = core.statusBar.icons.settings;
|
const setIcon = core.statusBar.icons.settings;
|
||||||
|
|
||||||
const iconFont = '12px Verdana';
|
const iconFont = new Font('Verdana', 12);
|
||||||
|
|
||||||
const book = () => core.openBook(true);
|
const book = () => core.openBook(true);
|
||||||
const tool = () => core.openEquipbox(true);
|
const tool = () => core.openEquipbox(true);
|
||||||
@ -160,8 +160,8 @@ export const ReplayingToolbar = defineComponent<ReplayingProps>(props => {
|
|||||||
|
|
||||||
const bookIcon = core.statusBar.icons.book;
|
const bookIcon = core.statusBar.icons.book;
|
||||||
const saveIcon = core.statusBar.icons.save;
|
const saveIcon = core.statusBar.icons.save;
|
||||||
const font1 = '16px normal';
|
const font1 = new Font('normal', 16);
|
||||||
const font2 = '12px Verdana';
|
const font2 = new Font('Verdana', 12);
|
||||||
|
|
||||||
const speedText = computed(() => `${status.speed}速`);
|
const speedText = computed(() => `${status.speed}速`);
|
||||||
const progress = computed(() => status.played / status.total);
|
const progress = computed(() => status.played / status.total);
|
||||||
|
Loading…
Reference in New Issue
Block a user