From 5bdd41159b7839ac91f95692ab66d789604c2cae Mon Sep 17 00:00:00 2001
From: unanmed <1319491857@qq.com>
Date: Sun, 28 Sep 2025 16:01:09 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20=E6=96=87=E6=9C=AC=E6=A1=86=E7=9A=84?=
=?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/render/components/textbox.tsx | 20 ++++---
.../src/render/components/textboxTyper.ts | 33 +++++++----
.../src/render/elements/misc.ts | 2 +-
.../client-modules/src/render/ui/main.tsx | 2 +-
packages/render-elements/src/misc.ts | 1 +
public/_server/MotaAction.g4 | 4 +-
public/libs/events.js | 58 ++++++++++++++++---
src/types/declaration/source.d.ts | 4 +-
8 files changed, 91 insertions(+), 33 deletions(-)
diff --git a/packages-user/client-modules/src/render/components/textbox.tsx b/packages-user/client-modules/src/render/components/textbox.tsx
index d1ec44d..18703f0 100644
--- a/packages-user/client-modules/src/render/components/textbox.tsx
+++ b/packages-user/client-modules/src/render/components/textbox.tsx
@@ -174,6 +174,8 @@ export const TextContent = defineComponent<
const renderContent = (canvas: MotaOffscreenCanvas2D) => {
const ctx = canvas.ctx;
ctx.textBaseline = 'top';
+ ctx.lineWidth = props.strokeWidth ?? 2;
+ ctx.lineJoin = 'round';
for (const data of renderable) {
if (data.cut) break;
switch (data.type) {
@@ -182,14 +184,15 @@ export const TextContent = defineComponent<
ctx.fillStyle = data.fillStyle;
ctx.strokeStyle = data.strokeStyle;
ctx.font = data.font;
+
const text = data.text.slice(0, data.pointer);
- if (props.fill ?? true) {
- ctx.fillText(text, data.x, data.y);
- }
if (props.stroke) {
ctx.strokeText(text, data.x, data.y);
}
+ if (props.fill ?? true) {
+ ctx.fillText(text, data.x, data.y);
+ }
break;
}
case TextContentType.Icon: {
@@ -250,7 +253,7 @@ export interface TextboxProps extends TextContentProps, DefaultProps {
/** 标题文字与边框间的距离,默认为4 */
titlePadding?: number;
/** 图标 */
- icon?: AllIds;
+ icon?: AllIdsWithNone;
/** 最大宽度 */
width: number;
}
@@ -408,8 +411,8 @@ export const Textbox = defineComponent<
titleElement.value?.requestBeforeFrame(() => {
if (titleElement.value) {
const { width, height } = titleElement.value;
- tw.value = width + data.padding! * 2;
- th.value = height + data.padding! * 2;
+ tw.value = width + data.titlePadding! * 2;
+ th.value = height + data.titlePadding! * 2;
}
});
});
@@ -481,7 +484,7 @@ export const Textbox = defineComponent<
id={props.id}
hidden={hidden.value}
alpha={data.alpha}
- loc={props.loc}
+ loc={data.loc}
>
{data.title && (
@@ -502,6 +505,7 @@ export const Textbox = defineComponent<
fillStyle={data.titleFill}
strokeStyle={data.titleStroke}
font={data.titleFont}
+ strokeWidth={2}
>
)}
@@ -520,7 +524,7 @@ export const Textbox = defineComponent<
>
)}
{hasIcon.value && (
-
+
)}
{hasIcon.value && (
{
this.config[key] = value;
}
}
+ if (config.font) {
+ this.config.fontFamily = config.font.family;
+ this.config.fontSize = config.font.size;
+ this.config.fontItalic = config.font.italic;
+ this.config.fontWeight = config.font.weight;
+ }
this.parser.setStatus({
fillStyle: this.config.fillStyle,
fontFamily: this.config.fontFamily,
@@ -528,7 +534,8 @@ export class TextContentTyper extends EventEmitter {
return;
}
this.emit('typeStart');
- this.lastTypeTime = Date.now();
+ // 减去间隔是为了第一个字可以立刻打出来,不然看起来有延迟
+ this.lastTypeTime = Date.now() - this.config.interval - 1;
this.typing = true;
}
@@ -628,11 +635,11 @@ export class TextContentParser {
* @param st 要设置为的状态,不填的表示不变
*/
setStatus(st: Partial) {
- if (!isNil(st.fillStyle)) this.status.fillStyle = st.fillStyle;
- if (!isNil(st.fontSize)) this.status.fontSize = st.fontSize;
- if (!isNil(st.fontFamily)) this.status.fontFamily = st.fontFamily;
- if (!isNil(st.fontItalic)) this.status.fontItalic = st.fontItalic;
- if (!isNil(st.fontWeight)) this.status.fontWeight = st.fontWeight;
+ if (!isNil(st.fillStyle)) this.initStatus.fillStyle = st.fillStyle;
+ if (!isNil(st.fontSize)) this.initStatus.fontSize = st.fontSize;
+ if (!isNil(st.fontFamily)) this.initStatus.fontFamily = st.fontFamily;
+ if (!isNil(st.fontItalic)) this.initStatus.fontItalic = st.fontItalic;
+ if (!isNil(st.fontWeight)) this.initStatus.fontWeight = st.fontWeight;
}
/**
@@ -856,6 +863,7 @@ export class TextContentParser {
this.font = this.buildFont();
this.resolved = '';
this.wordBreak = [0];
+ this.wordBreakRule = this.config.wordBreak;
this.nodePointer = 0;
this.blockPointer = 0;
this.nowNode = 0;
@@ -975,7 +983,6 @@ export class TextContentParser {
}
this.addTextNode(text.length, false);
-
return this.splitLines(width);
}
@@ -1198,7 +1205,11 @@ export class TextContentParser {
this.newLine();
const nextStart = this.wordBreak[index];
const nextEnd = this.wordBreak[end];
- this.bsStart = index;
+ if (index === this.bsStart) {
+ this.bsStart = this.bsStart + 1;
+ } else {
+ this.bsStart = index;
+ }
this.bsEnd = end;
const metrics = this.measure(node, nextStart, nextEnd);
if (metrics.width < width) {
@@ -1338,11 +1349,9 @@ export class TextContentParser {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.type === TextContentType.Text) {
- this.wordBreak = [node.text.length];
+ this.wordBreak = [0, node.text.length];
}
- const pointer =
- node.type === TextContentType.Text ? node.text.length : 1;
- const block = this.generateBlock(node, pointer);
+ const block = this.generateBlock(node, 1);
this.pushBlock(block, 1);
}
this.newLine();
diff --git a/packages-user/client-modules/src/render/elements/misc.ts b/packages-user/client-modules/src/render/elements/misc.ts
index 548062f..811537d 100644
--- a/packages-user/client-modules/src/render/elements/misc.ts
+++ b/packages-user/client-modules/src/render/elements/misc.ts
@@ -66,7 +66,7 @@ export class Icon extends RenderItem implements IAnimateFrame {
* 设置图标
* @param id 图标id
*/
- setIcon(id: AllIds | AllNumbers) {
+ setIcon(id: AllIdsWithNone | AllNumbers) {
if (id === 0 || id === 'none') {
this.renderable = void 0;
return;
diff --git a/packages-user/client-modules/src/render/ui/main.tsx b/packages-user/client-modules/src/render/ui/main.tsx
index 0d16b86..055ba98 100644
--- a/packages-user/client-modules/src/render/ui/main.tsx
+++ b/packages-user/client-modules/src/render/ui/main.tsx
@@ -83,7 +83,7 @@ const MainScene = defineComponent(() => {
font: new Font('normal'),
titleFont: new Font('normal', 20, 'px', 700),
winskin: 'winskin2.png',
- interval: 100,
+ interval: 30,
lineHeight: 4,
width: MAP_WIDTH
};
diff --git a/packages/render-elements/src/misc.ts b/packages/render-elements/src/misc.ts
index 89d518f..a6e596e 100644
--- a/packages/render-elements/src/misc.ts
+++ b/packages/render-elements/src/misc.ts
@@ -52,6 +52,7 @@ export class Text extends RenderItem {
ctx.strokeStyle = this.strokeStyle ?? 'transparent';
ctx.font = this.font.string();
ctx.lineWidth = this.strokeWidth;
+ ctx.lineJoin = 'round';
if (this.strokeStyle) {
ctx.strokeText(this.text, stroke, this.descent + stroke + SAFE_PAD);
diff --git a/public/_server/MotaAction.g4 b/public/_server/MotaAction.g4
index 24d9735..17c0243 100644
--- a/public/_server/MotaAction.g4
+++ b/public/_server/MotaAction.g4
@@ -1044,7 +1044,7 @@ setText_s
tooltip : setText:设置文本的属性,颜色为RGB三元组或RGBA四元组,打字间隔为剧情文字添加的时间间隔,为整数或不填,字符间距为字符之间的距离,为整数或不填。
helpUrl : /_docs/#/instruction
previewBlock : true
-default : ["main-textbox","","","","","","","",false,false,"","","",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"",true,false,"",'rgba(255,255,255,1)',"","",true,false,"",'null','null',"","",""]
+default : ["main-textbox","","","","","","","",false,false,"","","",'rgba(255,255,255,1)',"",'rgba(0,0,0,1)',"",true,false,"",'rgba(0,0,0,0.9)',"","",true,false,"",'null','null',"","",""]
Bool_0 = Bool_0 ? (', "fontItalic": '+Bool_0) : '';
Bool_1 = Bool_1 ? (', "keepLast": '+Bool_1) : '';
Bool_2 = !Bool_2 ? (', "fill": '+Bool_2) : '';
@@ -1057,7 +1057,7 @@ IntString_2 = IntString_2 ? (', "width": '+IntString_2) : '';
IntString_3 = IntString_3 ? (', "height": '+IntString_3) : '';
IntString_4 = IntString_4 ? (', "fontSize": '+IntString_4) : '';
IntString_5 = IntString_5? (', "fontWeight": ' + IntString_5) : '';
-IntString_6 = IntString_6 ? (', " interval": '+IntString_6) : '';
+IntString_6 = IntString_6 ? (', "interval": '+IntString_6) : '';
IntString_7 = IntString_7 ? (', "lineHeight": ' + IntString_7) : '';
IntString_8 = IntString_8? (', "strokeWidth": ' + IntString_8) : '';
IntString_9 = IntString_9? (', "padding": ' + IntString_9) : '';
diff --git a/public/libs/events.js b/public/libs/events.js
index 1b77244..3f4052b 100644
--- a/public/libs/events.js
+++ b/public/libs/events.js
@@ -1579,7 +1579,15 @@ events.prototype._action_text = function (data) {
loc[3] ??= 200;
const { x = loc[0], y = loc[1], width = loc[2], height = loc[3] } = data;
store.show();
- store.modify({ title, icon, loc: [x, y, width, height], width });
+ store.modify({
+ title,
+ icon,
+ x,
+ y,
+ width,
+ height,
+ loc: [x, y, width, height]
+ });
store.setText(text);
core.events.nowTextbox = textbox;
};
@@ -1600,7 +1608,15 @@ events.prototype._action_autoText = function (data) {
loc[3] ??= 200;
const { x = loc[0], y = loc[1], width = loc[2], height = loc[3] } = data;
store.show();
- store.modify({ title, icon, loc: [x, y, width, height], width });
+ store.modify({
+ title,
+ icon,
+ x,
+ y,
+ width,
+ height,
+ loc: [x, y, width, height]
+ });
store.setText(text);
setTimeout(() => {
@@ -1623,10 +1639,11 @@ events.prototype._action__label = function (data, x, y, prefix) {
};
events.prototype._action_setText = function (data) {
- const isNil = value => value !== null && value !== void 0;
+ const isNil = value => value === null || value === void 0;
const { textbox = 'main-textbox' } = data;
const Store = Mota.require('@user/client-modules').TextboxStore;
- const Font = Mota.require('@motajs/render-vue').Font;
+ const { TextAlign, WordBreak } = Mota.require('@user/client-modules');
+ const Font = Mota.require('@motajs/render-style').Font;
const store = Store.get(textbox);
if (!store) {
core.doAction();
@@ -1651,6 +1668,10 @@ events.prototype._action_setText = function (data) {
});
// config
const config = {
+ x,
+ y,
+ width,
+ height,
loc: newLoc,
font: newFont,
keepLast: data.keepLast,
@@ -1664,15 +1685,36 @@ events.prototype._action_setText = function (data) {
backColor: data.backColor,
winskin: data.winskin,
padding: data.padding,
- titleFill: isNil(data.titleFill),
- titleStroke: !!data.titleStroke,
+ titleFill: isNil(data.titleFill) ? 'gold' : 'transparent',
+ titleStroke: data.titleStroke ? 'black' : 'transparent',
titlePadding: data.titlePadding,
- textAlign: data.textAlign,
- wordBreak: data.wordBreak,
ignoreLineStart: data.ignoreLineStart,
ignoreLineEnd: data.ignoreLineEnd,
breakChars: data.breakChars
};
+ switch (data.textAlign) {
+ case 'left':
+ config.textAlign = TextAlign.Left;
+ break;
+ case 'center':
+ config.textAlign = TextAlign.Center;
+ break;
+ case 'right':
+ config.textAlign = TextAlign.Right;
+ break;
+ }
+ switch (data.wordBreak) {
+ case 'none':
+ config.wordBreak = WordBreak.None;
+ break;
+ case 'space':
+ config.wordBreak = WordBreak.Space;
+ break;
+ case 'all':
+ config.wordBreak = WordBreak.All;
+ break;
+ }
+
store.modify(config);
core.doAction();
diff --git a/src/types/declaration/source.d.ts b/src/types/declaration/source.d.ts
index 60f71a2..a7ea8dc 100644
--- a/src/types/declaration/source.d.ts
+++ b/src/types/declaration/source.d.ts
@@ -25,7 +25,9 @@ type ItemCls = 'tools' | 'items' | 'equips' | 'constants';
/**
* 所有的道具id
*/
-type AllIds = keyof IdToNumber | 'none';
+type AllIds = keyof IdToNumber;
+
+type AllIdsWithNone = AllIds | 'none';
/**
* 所有的道具数字