mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-25 00:23:25 +08:00
Compare commits
No commits in common. "d0dae40a5ac5bf63dc1bbbf77e05b6792427c521" and "b0e420c167c4eb76dab2c26049782536eb1aa1ff" have entirely different histories.
d0dae40a5a
...
b0e420c167
@ -1117,7 +1117,7 @@ actions.prototype._clickAction_text = function () {
|
|||||||
// 正在淡入淡出的话不执行
|
// 正在淡入淡出的话不执行
|
||||||
if (core.status.event.animateUI) return;
|
if (core.status.event.animateUI) return;
|
||||||
|
|
||||||
const Store = Mota.require('module', 'MainUI').TextboxStore;
|
const Store = Mota.require('module', 'Render').TextboxStore;
|
||||||
const store = Store.get('main-textbox');
|
const store = Store.get('main-textbox');
|
||||||
|
|
||||||
// var data = core.clone(core.status.event.data.current);
|
// var data = core.clone(core.status.event.data.current);
|
||||||
|
@ -1553,7 +1553,7 @@ events.prototype.__action_doAsyncFunc = function (isAsync, func) {
|
|||||||
|
|
||||||
events.prototype._action_text = function (data, x, y, prefix) {
|
events.prototype._action_text = function (data, x, y, prefix) {
|
||||||
if (this.__action_checkReplaying()) return;
|
if (this.__action_checkReplaying()) return;
|
||||||
const Store = Mota.require('module', 'MainUI').TextboxStore;
|
const Store = Mota.require('module', 'Render').TextboxStore;
|
||||||
const store = Store.get('main-textbox');
|
const store = Store.get('main-textbox');
|
||||||
const { text } = data;
|
const { text } = data;
|
||||||
let title = '';
|
let title = '';
|
||||||
@ -1594,10 +1594,10 @@ events.prototype._action_text = function (data, x, y, prefix) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showText = text.slice(0, titleStartIndex) + text.slice(titleEndIndex);
|
const showTitle =
|
||||||
|
text.slice(0, titleStartIndex) + text.slice(titleEndIndex);
|
||||||
store.show();
|
store.show();
|
||||||
store.modify({ title });
|
store.modify({ text: showTitle, title });
|
||||||
store.setText(showText);
|
|
||||||
|
|
||||||
// data.text = core.replaceText(data.text, prefix);
|
// data.text = core.replaceText(data.text, prefix);
|
||||||
// var ctx = data.code ? '__text__' + data.code : null;
|
// var ctx = data.code ? '__text__' + data.code : null;
|
||||||
|
@ -13,7 +13,7 @@ function checkSupport() {
|
|||||||
sleep(3000).then(() => {
|
sleep(3000).then(() => {
|
||||||
tip(
|
tip(
|
||||||
'warning',
|
'warning',
|
||||||
`您的浏览器不支持WebGL,大部分效果将会无法显示,请更新你的浏览器`
|
`您的浏览器不支持WebGL,大部分特效将会无法显示,建议使用新版浏览器`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@ function checkSupport() {
|
|||||||
sleep(3000).then(() => {
|
sleep(3000).then(() => {
|
||||||
tip(
|
tip(
|
||||||
'warning',
|
'warning',
|
||||||
`您的浏览器不支持WebGL2,大部分效果将会无法显示,请更新你的浏览器`
|
`您的浏览器不支持WebGL2,一部分特效将会无法显示,建议使用新版浏览器`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -54,10 +54,10 @@ type UniformFunc<
|
|||||||
type UniformBinderValue<N extends UniformBinderNum> = N extends 1
|
type UniformBinderValue<N extends UniformBinderNum> = N extends 1
|
||||||
? number
|
? number
|
||||||
: N extends 2
|
: N extends 2
|
||||||
? [number, number]
|
? [number, number]
|
||||||
: N extends 3
|
: N extends 3
|
||||||
? [number, number, number]
|
? [number, number, number]
|
||||||
: [number, number, number, number];
|
: [number, number, number, number];
|
||||||
|
|
||||||
interface UniformBinder<
|
interface UniformBinder<
|
||||||
N extends UniformBinderNum,
|
N extends UniformBinderNum,
|
||||||
|
@ -80,9 +80,9 @@ export class Container<E extends EContainerEvent = EContainerEvent>
|
|||||||
append(parent: RenderItem): void {
|
append(parent: RenderItem): void {
|
||||||
super.append(parent);
|
super.append(parent);
|
||||||
if (this.root) {
|
if (this.root) {
|
||||||
const root = this.root;
|
|
||||||
this.forEachChild(ele => {
|
this.forEachChild(ele => {
|
||||||
ele.setRoot(root);
|
ele.checkRoot();
|
||||||
|
this.root?.connect(ele);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +104,6 @@ export class Container<E extends EContainerEvent = EContainerEvent>
|
|||||||
this.sortedChildren = [...this.children].sort(
|
this.sortedChildren = [...this.children].sort(
|
||||||
(a, b) => a.zIndex - b.zIndex
|
(a, b) => a.zIndex - b.zIndex
|
||||||
);
|
);
|
||||||
this.update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected propagateEvent<T extends ActionType>(
|
protected propagateEvent<T extends ActionType>(
|
||||||
|
@ -437,15 +437,6 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除由 `requireCanvas` 申请的画布,当画布不再使用时,可以用该方法删除画布
|
|
||||||
* @param canvas 要删除的画布
|
|
||||||
*/
|
|
||||||
protected deleteCanvas(canvas: MotaOffscreenCanvas2D) {
|
|
||||||
if (!this.canvases.delete(canvas)) return;
|
|
||||||
canvas.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
//#region 修改元素属性
|
//#region 修改元素属性
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -466,8 +457,8 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
* @param y 纵坐标
|
* @param y 纵坐标
|
||||||
*/
|
*/
|
||||||
pos(x: number, y: number) {
|
pos(x: number, y: number) {
|
||||||
// 这个函数会调用 update,因此不再手动调用 update
|
|
||||||
this._transform.setTranslate(x, y);
|
this._transform.setTranslate(x, y);
|
||||||
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -597,15 +588,10 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(item: RenderItem<any> = this): void {
|
update(item: RenderItem<any> = this): void {
|
||||||
if (this._parent) {
|
if (this.cacheDirty) return;
|
||||||
if (this.cacheDirty && this._parent.cacheDirty) return;
|
this.cacheDirty = true;
|
||||||
this.cacheDirty = true;
|
if (this.hidden) return;
|
||||||
if (this.hidden) return;
|
this.parent?.update(item);
|
||||||
this._parent.update(item);
|
|
||||||
} else {
|
|
||||||
if (this.cacheDirty) return;
|
|
||||||
this.cacheDirty = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTransform() {
|
updateTransform() {
|
||||||
@ -664,12 +650,6 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
|
|
||||||
//#region 父子关系
|
//#region 父子关系
|
||||||
|
|
||||||
setRoot(item: RenderItem & IRenderTreeRoot) {
|
|
||||||
this._root?.disconnect(this);
|
|
||||||
this._root = item;
|
|
||||||
item.connect(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkRoot(): RenderItem | null {
|
checkRoot(): RenderItem | null {
|
||||||
if (this._root) return this._root;
|
if (this._root) return this._root;
|
||||||
if (this.isRoot) return this;
|
if (this.isRoot) return this;
|
||||||
|
@ -30,10 +30,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
super(type, false);
|
super(type, false);
|
||||||
|
|
||||||
this.text = text;
|
this.text = text;
|
||||||
if (text.length > 0) {
|
if (text.length > 0) this.calBox();
|
||||||
this.calBox();
|
|
||||||
this.emit('setText', text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(
|
protected render(
|
||||||
@ -73,7 +70,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
setText(text: string) {
|
setText(text: string) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.calBox();
|
this.calBox();
|
||||||
this.update(this);
|
if (this.parent) this.update(this);
|
||||||
this.emit('setText', text);
|
this.emit('setText', text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +81,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
setFont(font: string) {
|
setFont(font: string) {
|
||||||
this.font = font;
|
this.font = font;
|
||||||
this.calBox();
|
this.calBox();
|
||||||
this.update(this);
|
if (this.parent) this.update(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,11 +110,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
this.measure();
|
this.measure();
|
||||||
this.length = width;
|
this.length = width;
|
||||||
this.descent = actualBoundingBoxAscent;
|
this.descent = actualBoundingBoxAscent;
|
||||||
this.size(
|
this.size(width, actualBoundingBoxAscent + actualBoundingBoxDescent);
|
||||||
width,
|
|
||||||
Math.abs(actualBoundingBoxAscent) +
|
|
||||||
Math.abs(actualBoundingBoxDescent)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleProps(
|
protected handleProps(
|
||||||
@ -139,7 +132,7 @@ export class Text extends RenderItem<ETextEvent> {
|
|||||||
case 'font':
|
case 'font':
|
||||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||||
this.setFont(nextValue);
|
this.setFont(nextValue);
|
||||||
return true;
|
break;
|
||||||
case 'strokeWidth':
|
case 'strokeWidth':
|
||||||
this.setStrokeWidth(nextValue);
|
this.setStrokeWidth(nextValue);
|
||||||
return true;
|
return true;
|
||||||
@ -345,27 +338,14 @@ export class Icon extends RenderItem<EIconEvent> implements IAnimateFrame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WinskinPatterns {
|
|
||||||
top: CanvasPattern;
|
|
||||||
left: CanvasPattern;
|
|
||||||
bottom: CanvasPattern;
|
|
||||||
right: CanvasPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EWinskinEvent extends ERenderItemEvent {}
|
export interface EWinskinEvent extends ERenderItemEvent {}
|
||||||
|
|
||||||
export class Winskin extends RenderItem<EWinskinEvent> {
|
export class Winskin extends RenderItem<EWinskinEvent> {
|
||||||
image: SizedCanvasImageSource;
|
image: SizedCanvasImageSource;
|
||||||
/** 边框宽度,32表示原始宽度 */
|
/** 边框宽度 */
|
||||||
borderSize: number = 32;
|
borderSize: number = 32;
|
||||||
/** 图片名称 */
|
|
||||||
imageName?: string;
|
|
||||||
|
|
||||||
private pendingImage?: ImageIds;
|
private pendingImage?: ImageIds;
|
||||||
private patternCache?: WinskinPatterns;
|
|
||||||
private patternTransform: DOMMatrix;
|
|
||||||
|
|
||||||
private static patternMap: Map<string, WinskinPatterns> = new Map();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
image: SizedCanvasImageSource,
|
image: SizedCanvasImageSource,
|
||||||
@ -373,63 +353,6 @@ export class Winskin extends RenderItem<EWinskinEvent> {
|
|||||||
) {
|
) {
|
||||||
super(type, false, false);
|
super(type, false, false);
|
||||||
this.image = image;
|
this.image = image;
|
||||||
this.setAntiAliasing(false);
|
|
||||||
|
|
||||||
if (window.DOMMatrix) {
|
|
||||||
this.patternTransform = new DOMMatrix();
|
|
||||||
} else if (window.WebKitCSSMatrix) {
|
|
||||||
this.patternTransform = new WebKitCSSMatrix();
|
|
||||||
} else {
|
|
||||||
this.patternTransform = new SVGMatrix();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private generatePattern() {
|
|
||||||
const pattern = this.requireCanvas();
|
|
||||||
const img = this.image;
|
|
||||||
pattern.size(32, 16);
|
|
||||||
pattern.withGameScale(false);
|
|
||||||
pattern.setHD(false);
|
|
||||||
pattern.setAntiAliasing(false);
|
|
||||||
const ctx = pattern.ctx;
|
|
||||||
ctx.drawImage(img, 144, 0, 32, 16, 0, 0, 32, 16);
|
|
||||||
const topPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
|
||||||
ctx.clearRect(0, 0, 32, 16);
|
|
||||||
ctx.drawImage(img, 144, 48, 32, 16, 0, 0, 32, 16);
|
|
||||||
const bottomPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
|
||||||
ctx.clearRect(0, 0, 32, 16);
|
|
||||||
pattern.size(16, 32);
|
|
||||||
ctx.drawImage(img, 128, 16, 16, 32, 0, 0, 16, 32);
|
|
||||||
const leftPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
|
||||||
ctx.clearRect(0, 0, 16, 32);
|
|
||||||
ctx.drawImage(img, 176, 16, 16, 32, 0, 0, 16, 32);
|
|
||||||
const rightPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
|
||||||
if (!topPattern || !bottomPattern || !leftPattern || !rightPattern) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const winskinPattern: WinskinPatterns = {
|
|
||||||
top: topPattern,
|
|
||||||
bottom: bottomPattern,
|
|
||||||
left: leftPattern,
|
|
||||||
right: rightPattern
|
|
||||||
};
|
|
||||||
if (this.imageName) {
|
|
||||||
Winskin.patternMap.set(this.imageName, winskinPattern);
|
|
||||||
}
|
|
||||||
this.patternCache = winskinPattern;
|
|
||||||
this.deleteCanvas(pattern);
|
|
||||||
return winskinPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPattern() {
|
|
||||||
if (!this.imageName) {
|
|
||||||
if (this.patternCache) return this.patternCache;
|
|
||||||
return this.generatePattern();
|
|
||||||
} else {
|
|
||||||
const pattern = Winskin.patternMap.get(this.imageName);
|
|
||||||
if (pattern) return pattern;
|
|
||||||
return this.generatePattern();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(
|
protected render(
|
||||||
@ -438,38 +361,143 @@ export class Winskin extends RenderItem<EWinskinEvent> {
|
|||||||
): void {
|
): void {
|
||||||
const ctx = canvas.ctx;
|
const ctx = canvas.ctx;
|
||||||
const img = this.image;
|
const img = this.image;
|
||||||
const w = this.width;
|
const x = 0;
|
||||||
const h = this.height;
|
const y = 0;
|
||||||
const pad = this.borderSize / 2;
|
const w = canvas.width;
|
||||||
// 背景
|
const h = canvas.height;
|
||||||
ctx.drawImage(img, 0, 0, 128, 128, 2, 2, w - 4, h - 4);
|
const sz = this.borderSize / 32;
|
||||||
const pattern = this.getPattern();
|
ctx.drawImage(img, 0, 0, 128, 128, x + 2, y + 2, w - 4, h - 4);
|
||||||
if (!pattern) return;
|
ctx.drawImage(img, 128, 0, 16, 16, x, y, 16 * sz, 16 * sz);
|
||||||
const { top, left, right, bottom } = pattern;
|
let dx;
|
||||||
top.setTransform(this.patternTransform);
|
for (dx = 0; dx < w - 64 * sz; dx += 32 * sz) {
|
||||||
left.setTransform(this.patternTransform);
|
ctx.drawImage(
|
||||||
right.setTransform(this.patternTransform);
|
img,
|
||||||
bottom.setTransform(this.patternTransform);
|
144,
|
||||||
// 上下左右边框
|
0,
|
||||||
ctx.save();
|
32,
|
||||||
ctx.fillStyle = top;
|
16,
|
||||||
ctx.translate(pad, 0);
|
x + dx + 16,
|
||||||
ctx.fillRect(0, 0, w - pad * 2, pad);
|
y,
|
||||||
ctx.fillStyle = bottom;
|
32 * sz,
|
||||||
ctx.translate(0, h - pad);
|
16 * sz
|
||||||
ctx.fillRect(0, 0, w - pad * 2, pad);
|
);
|
||||||
ctx.fillStyle = left;
|
ctx.drawImage(
|
||||||
ctx.translate(-pad, pad * 2 - h);
|
img,
|
||||||
ctx.fillRect(0, 0, pad, h - pad * 2);
|
144,
|
||||||
ctx.fillStyle = right;
|
48,
|
||||||
ctx.translate(w - pad, 0);
|
32,
|
||||||
ctx.fillRect(0, 0, pad, h - pad * 2);
|
16,
|
||||||
ctx.restore();
|
x + dx + 16,
|
||||||
// 四个角的边框
|
y + h - 16 * sz,
|
||||||
ctx.drawImage(img, 128, 0, 16, 16, 0, 0, pad, pad);
|
32 * sz,
|
||||||
ctx.drawImage(img, 176, 0, 16, 16, w - pad, 0, pad, pad);
|
16 * sz
|
||||||
ctx.drawImage(img, 128, 48, 16, 16, 0, h - pad, pad, pad);
|
);
|
||||||
ctx.drawImage(img, 176, 48, 16, 16, w - pad, h - pad, pad, pad);
|
}
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
144,
|
||||||
|
0,
|
||||||
|
w - dx - 32,
|
||||||
|
16,
|
||||||
|
x + dx + 16 * sz,
|
||||||
|
y,
|
||||||
|
w - dx - 32 * sz,
|
||||||
|
16 * sz
|
||||||
|
);
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
144,
|
||||||
|
48,
|
||||||
|
w - dx - 32,
|
||||||
|
16,
|
||||||
|
x + dx + 16 * sz,
|
||||||
|
y + h - 16 * sz,
|
||||||
|
w - dx - 32 * sz,
|
||||||
|
16 * sz
|
||||||
|
);
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
176,
|
||||||
|
0,
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
x + w - 16 * sz,
|
||||||
|
y,
|
||||||
|
16 * sz,
|
||||||
|
16 * sz
|
||||||
|
);
|
||||||
|
// 左右
|
||||||
|
let dy;
|
||||||
|
for (dy = 0; dy < h - 64 * sz; dy += 32 * sz) {
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
128,
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
32,
|
||||||
|
x,
|
||||||
|
y + dy + 16 * sz,
|
||||||
|
16 * sz,
|
||||||
|
32 * sz
|
||||||
|
);
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
176,
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
32,
|
||||||
|
x + w - 16 * sz,
|
||||||
|
y + dy + 16 * sz,
|
||||||
|
16 * sz,
|
||||||
|
32 * sz
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
128,
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
h - dy - 32,
|
||||||
|
x,
|
||||||
|
y + dy + 16 * sz,
|
||||||
|
16 * sz,
|
||||||
|
h - dy - 32 * sz
|
||||||
|
);
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
176,
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
h - dy - 32,
|
||||||
|
x + w - 16 * sz,
|
||||||
|
y + dy + 16 * sz,
|
||||||
|
16 * sz,
|
||||||
|
h - dy - 32 * sz
|
||||||
|
);
|
||||||
|
// 下方
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
128,
|
||||||
|
48,
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
x,
|
||||||
|
y + h - 16 * sz,
|
||||||
|
16 * sz,
|
||||||
|
16 * sz
|
||||||
|
);
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
176,
|
||||||
|
48,
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
x + w - 16 * sz,
|
||||||
|
y + h - 16 * sz,
|
||||||
|
16 * sz,
|
||||||
|
16 * sz
|
||||||
|
);
|
||||||
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -478,7 +506,6 @@ export class Winskin extends RenderItem<EWinskinEvent> {
|
|||||||
*/
|
*/
|
||||||
setImage(image: SizedCanvasImageSource) {
|
setImage(image: SizedCanvasImageSource) {
|
||||||
this.image = image;
|
this.image = image;
|
||||||
this.patternCache = void 0;
|
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,7 +530,6 @@ export class Winskin extends RenderItem<EWinskinEvent> {
|
|||||||
}
|
}
|
||||||
this.pendingImage = name;
|
this.pendingImage = name;
|
||||||
}
|
}
|
||||||
this.imageName = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -512,8 +538,6 @@ export class Winskin extends RenderItem<EWinskinEvent> {
|
|||||||
*/
|
*/
|
||||||
setBorderSize(size: number) {
|
setBorderSize(size: number) {
|
||||||
this.borderSize = size;
|
this.borderSize = size;
|
||||||
this.patternTransform.a = size / 32;
|
|
||||||
this.patternTransform.d = size / 32;
|
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +90,6 @@
|
|||||||
"56": "Method '$1' has been deprecated. Consider using '$2' instead.",
|
"56": "Method '$1' has been deprecated. Consider using '$2' instead.",
|
||||||
"57": "Repeated UI controller on item '$1', new controller will not work.",
|
"57": "Repeated UI controller on item '$1', new controller will not work.",
|
||||||
"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.",
|
|
||||||
"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."
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
|||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
defineComponent,
|
defineComponent,
|
||||||
nextTick,
|
|
||||||
onUnmounted,
|
onUnmounted,
|
||||||
|
onUpdated,
|
||||||
ref,
|
ref,
|
||||||
shallowReactive,
|
shallowReactive,
|
||||||
shallowRef,
|
shallowRef,
|
||||||
@ -13,7 +13,7 @@ import {
|
|||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { logger } from '@/core/common/logger';
|
import { logger } from '@/core/common/logger';
|
||||||
import { Sprite } from '@/core/render/sprite';
|
import { Sprite } from '@/core/render/sprite';
|
||||||
import { DefaultProps } from '@/core/render/renderer';
|
import { ContainerProps, DefaultProps } from '@/core/render/renderer';
|
||||||
import { isNil } from 'lodash-es';
|
import { isNil } from 'lodash-es';
|
||||||
import { SetupComponentOptions } from './types';
|
import { SetupComponentOptions } from './types';
|
||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
@ -22,9 +22,7 @@ import {
|
|||||||
ITextContentConfig,
|
ITextContentConfig,
|
||||||
TextContentTyper,
|
TextContentTyper,
|
||||||
TyperRenderable,
|
TyperRenderable,
|
||||||
TextContentType,
|
TextContentType
|
||||||
WordBreak,
|
|
||||||
TextAlign
|
|
||||||
} from './textboxTyper';
|
} from './textboxTyper';
|
||||||
|
|
||||||
export interface TextContentProps
|
export interface TextContentProps
|
||||||
@ -36,6 +34,8 @@ export interface TextContentProps
|
|||||||
fill?: boolean;
|
fill?: boolean;
|
||||||
/** 是否描边 */
|
/** 是否描边 */
|
||||||
stroke?: boolean;
|
stroke?: boolean;
|
||||||
|
/** 是否忽略打字机,直接显伤全部 */
|
||||||
|
showAll?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TextContentEmits = {
|
export type TextContentEmits = {
|
||||||
@ -43,18 +43,6 @@ export type TextContentEmits = {
|
|||||||
typeStart: () => void;
|
typeStart: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface TextContentExpose {
|
|
||||||
/**
|
|
||||||
* 重新开始打字
|
|
||||||
*/
|
|
||||||
retype(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 立刻显示所有文字
|
|
||||||
*/
|
|
||||||
showAll(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const textContentOptions = {
|
const textContentOptions = {
|
||||||
props: [
|
props: [
|
||||||
'breakChars',
|
'breakChars',
|
||||||
@ -62,6 +50,7 @@ const textContentOptions = {
|
|||||||
'fontSize',
|
'fontSize',
|
||||||
'fontWeight',
|
'fontWeight',
|
||||||
'fontItalic',
|
'fontItalic',
|
||||||
|
'height',
|
||||||
'ignoreLineEnd',
|
'ignoreLineEnd',
|
||||||
'ignoreLineStart',
|
'ignoreLineStart',
|
||||||
'interval',
|
'interval',
|
||||||
@ -69,14 +58,17 @@ const textContentOptions = {
|
|||||||
'lineHeight',
|
'lineHeight',
|
||||||
'text',
|
'text',
|
||||||
'textAlign',
|
'textAlign',
|
||||||
|
'width',
|
||||||
'wordBreak',
|
'wordBreak',
|
||||||
|
'x',
|
||||||
|
'y',
|
||||||
'fill',
|
'fill',
|
||||||
'fillStyle',
|
'fillStyle',
|
||||||
'strokeStyle',
|
'strokeStyle',
|
||||||
'strokeWidth',
|
'strokeWidth',
|
||||||
'stroke',
|
'stroke',
|
||||||
'loc',
|
'showAll',
|
||||||
'width'
|
'loc'
|
||||||
],
|
],
|
||||||
emits: ['typeEnd', 'typeStart']
|
emits: ['typeEnd', 'typeStart']
|
||||||
} satisfies SetupComponentOptions<
|
} satisfies SetupComponentOptions<
|
||||||
@ -89,72 +81,69 @@ export const TextContent = defineComponent<
|
|||||||
TextContentProps,
|
TextContentProps,
|
||||||
TextContentEmits,
|
TextContentEmits,
|
||||||
keyof TextContentEmits
|
keyof TextContentEmits
|
||||||
>((props, { emit, expose }) => {
|
>((props, { emit }) => {
|
||||||
const width = computed(() => props.width ?? props.loc?.[2] ?? 200);
|
if (props.width && props.width <= 0) {
|
||||||
if (width.value < 0) {
|
|
||||||
logger.warn(41, String(props.width));
|
logger.warn(41, String(props.width));
|
||||||
}
|
}
|
||||||
|
|
||||||
const typer = new TextContentTyper(props);
|
const typer = new TextContentTyper(props);
|
||||||
let renderable: TyperRenderable[] = [];
|
let renderable: TyperRenderable[] = [];
|
||||||
let needUpdate = false;
|
let needUpdate = false;
|
||||||
|
let nowText = '';
|
||||||
|
|
||||||
|
watch(props, value => {
|
||||||
|
typer.setConfig(value);
|
||||||
|
});
|
||||||
|
|
||||||
const retype = () => {
|
const retype = () => {
|
||||||
|
if (props.showAll) {
|
||||||
|
typer.typeAll();
|
||||||
|
}
|
||||||
|
if (props.text === nowText) return;
|
||||||
if (needUpdate) return;
|
if (needUpdate) return;
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
if (!spriteElement.value) {
|
if (!spriteElement.value) {
|
||||||
needUpdate = false;
|
needUpdate = false;
|
||||||
}
|
}
|
||||||
|
nowText = props.text ?? '';
|
||||||
renderable = [];
|
renderable = [];
|
||||||
|
|
||||||
spriteElement.value?.requestBeforeFrame(() => {
|
spriteElement.value?.requestBeforeFrame(() => {
|
||||||
typer.setConfig(props);
|
typer.setConfig(props);
|
||||||
typer.setText(props.text ?? '');
|
typer.setText(props.text ?? '');
|
||||||
typer.type();
|
typer.type();
|
||||||
|
if (props.showAll) {
|
||||||
|
typer.typeAll();
|
||||||
|
}
|
||||||
needUpdate = false;
|
needUpdate = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const showAll = () => {
|
onUpdated(retype);
|
||||||
typer.typeAll();
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(props, value => {
|
|
||||||
typer.setConfig(value);
|
|
||||||
retype();
|
|
||||||
});
|
|
||||||
|
|
||||||
expose({ retype, showAll });
|
|
||||||
|
|
||||||
const spriteElement = shallowRef<Sprite>();
|
const spriteElement = shallowRef<Sprite>();
|
||||||
const renderContent = (canvas: MotaOffscreenCanvas2D) => {
|
const renderContent = (canvas: MotaOffscreenCanvas2D) => {
|
||||||
const ctx = canvas.ctx;
|
const ctx = canvas.ctx;
|
||||||
ctx.textBaseline = 'top';
|
ctx.textBaseline = 'top';
|
||||||
renderable.forEach(v => {
|
renderable.forEach(v => {
|
||||||
switch (v.type) {
|
if (v.type === TextContentType.Text) {
|
||||||
case TextContentType.Text: {
|
ctx.fillStyle = v.fillStyle;
|
||||||
if (v.text.length === 0) return;
|
ctx.strokeStyle = v.strokeStyle;
|
||||||
ctx.fillStyle = v.fillStyle;
|
ctx.font = v.font;
|
||||||
ctx.strokeStyle = v.strokeStyle;
|
const text = v.text.slice(0, v.pointer);
|
||||||
ctx.font = v.font;
|
|
||||||
const text = v.text.slice(0, v.pointer);
|
|
||||||
|
|
||||||
if (props.fill ?? true) {
|
if (props.fill ?? true) {
|
||||||
ctx.fillText(text, v.x, v.y);
|
ctx.fillText(text, v.x, v.y);
|
||||||
}
|
|
||||||
if (props.stroke) {
|
|
||||||
ctx.strokeText(text, v.x, v.y);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case TextContentType.Icon: {
|
if (props.stroke) {
|
||||||
const { renderable: r, x: dx, y: dy, width, height } = v;
|
ctx.strokeText(text, v.x, v.y);
|
||||||
const render = r.render;
|
|
||||||
const [x, y, w, h] = render[0];
|
|
||||||
const icon = r.autotile ? r.image[0] : r.image;
|
|
||||||
ctx.drawImage(icon, x, y, w, h, dx, dy, width, height);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const r = v.renderable;
|
||||||
|
const render = r.render;
|
||||||
|
const [x, y, w, h] = render[0];
|
||||||
|
const icon = r.autotile ? r.image[0] : r.image;
|
||||||
|
ctx.drawImage(icon, x, y, w, h, v.x, v.y, v.width, v.height);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -175,15 +164,19 @@ export const TextContent = defineComponent<
|
|||||||
return () => {
|
return () => {
|
||||||
return (
|
return (
|
||||||
<sprite
|
<sprite
|
||||||
loc={props.loc}
|
{...props}
|
||||||
ref={spriteElement}
|
ref={spriteElement}
|
||||||
|
x={props.x}
|
||||||
|
y={props.y}
|
||||||
|
width={props.width}
|
||||||
|
height={props.height}
|
||||||
render={renderContent}
|
render={renderContent}
|
||||||
></sprite>
|
></sprite>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, textContentOptions);
|
}, textContentOptions);
|
||||||
|
|
||||||
export interface TextboxProps extends TextContentProps, DefaultProps {
|
export interface TextboxProps extends TextContentProps, ContainerProps {
|
||||||
/** 背景颜色 */
|
/** 背景颜色 */
|
||||||
backColor?: CanvasStyle;
|
backColor?: CanvasStyle;
|
||||||
/** 背景 winskin */
|
/** 背景 winskin */
|
||||||
@ -202,28 +195,6 @@ export interface TextboxProps extends TextContentProps, DefaultProps {
|
|||||||
titlePadding?: number;
|
titlePadding?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextboxExpose {
|
|
||||||
/**
|
|
||||||
* 显示这个文本框
|
|
||||||
*/
|
|
||||||
show(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏这个文本框
|
|
||||||
*/
|
|
||||||
hide(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重新开始打字
|
|
||||||
*/
|
|
||||||
retype(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 立刻显示所有文字
|
|
||||||
*/
|
|
||||||
showAll(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
type TextboxEmits = TextContentEmits;
|
type TextboxEmits = TextContentEmits;
|
||||||
type TextboxSlots = SlotsType<{
|
type TextboxSlots = SlotsType<{
|
||||||
default: (data: TextboxProps) => VNode[];
|
default: (data: TextboxProps) => VNode[];
|
||||||
@ -234,14 +205,23 @@ const textboxOptions = {
|
|||||||
props: (textContentOptions.props as (keyof TextboxProps)[]).concat([
|
props: (textContentOptions.props as (keyof TextboxProps)[]).concat([
|
||||||
'backColor',
|
'backColor',
|
||||||
'winskin',
|
'winskin',
|
||||||
|
'id',
|
||||||
'padding',
|
'padding',
|
||||||
|
'alpha',
|
||||||
|
'hidden',
|
||||||
|
'anchorX',
|
||||||
|
'anchorY',
|
||||||
|
'anti',
|
||||||
|
'cache',
|
||||||
|
'composite',
|
||||||
|
'fall',
|
||||||
|
'hd',
|
||||||
|
'transform',
|
||||||
|
'type',
|
||||||
|
'zIndex',
|
||||||
'titleFill',
|
'titleFill',
|
||||||
'titleStroke',
|
'titleStroke',
|
||||||
'titleFont',
|
'titleFont'
|
||||||
'titlePadding',
|
|
||||||
'id',
|
|
||||||
'hidden',
|
|
||||||
'title'
|
|
||||||
]),
|
]),
|
||||||
emits: textContentOptions.emits
|
emits: textContentOptions.emits
|
||||||
} satisfies SetupComponentOptions<TextboxProps, {}, string, TextboxSlots>;
|
} satisfies SetupComponentOptions<TextboxProps, {}, string, TextboxSlots>;
|
||||||
@ -256,206 +236,153 @@ export const Textbox = defineComponent<
|
|||||||
TextboxEmits,
|
TextboxEmits,
|
||||||
keyof TextboxEmits,
|
keyof TextboxEmits,
|
||||||
TextboxSlots
|
TextboxSlots
|
||||||
>((props, { slots, expose }) => {
|
>((props, { slots }) => {
|
||||||
const contentData = shallowReactive<TextContentProps>({});
|
const data = shallowReactive({ ...props });
|
||||||
const data = shallowReactive<TextboxProps>({});
|
data.padding ??= 8;
|
||||||
|
data.width ??= 200;
|
||||||
const setContentData = () => {
|
data.height ??= 200;
|
||||||
contentData.breakChars = props.breakChars ?? '';
|
data.id ??= '';
|
||||||
contentData.fontFamily = props.fontFamily ?? 'Verdana';
|
data.alpha ??= 1;
|
||||||
contentData.fontSize = props.fontSize ?? 16;
|
data.titleFill ??= '#000';
|
||||||
contentData.fontWeight = props.fontWeight ?? 500;
|
data.titleStroke ??= 'transparent';
|
||||||
contentData.fontItalic = props.fontItalic ?? false;
|
data.titleFont ??= '16px Verdana';
|
||||||
contentData.ignoreLineEnd = props.ignoreLineEnd ?? '';
|
data.titlePadding ??= 4;
|
||||||
contentData.ignoreLineStart = props.ignoreLineStart ?? '';
|
|
||||||
contentData.interval = props.interval ?? 0;
|
|
||||||
contentData.keepLast = props.keepLast ?? false;
|
|
||||||
contentData.lineHeight = props.lineHeight ?? 0;
|
|
||||||
contentData.text = props.text ?? '';
|
|
||||||
contentData.textAlign = props.textAlign ?? TextAlign.Left;
|
|
||||||
contentData.wordBreak = props.wordBreak ?? WordBreak.Space;
|
|
||||||
contentData.fill = props.fill ?? true;
|
|
||||||
contentData.stroke = props.stroke ?? false;
|
|
||||||
contentData.fillStyle = props.fillStyle ?? '#fff';
|
|
||||||
contentData.strokeStyle = props.strokeStyle ?? '#000';
|
|
||||||
contentData.strokeWidth = props.strokeWidth ?? 2;
|
|
||||||
contentData.loc = props.loc;
|
|
||||||
contentData.width = props.width;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setTextboxData = () => {
|
|
||||||
data.backColor = props.backColor ?? '#222';
|
|
||||||
data.winskin = props.winskin;
|
|
||||||
data.padding = props.padding ?? 8;
|
|
||||||
data.titleFill = props.titleFill ?? 'gold';
|
|
||||||
data.titleStroke = props.titleStroke ?? 'transparent';
|
|
||||||
data.titleFont = props.titleFont ?? '18px Verdana';
|
|
||||||
data.titlePadding = props.titlePadding ?? 8;
|
|
||||||
data.width = props.width ?? props.loc?.[2] ?? 200;
|
|
||||||
data.height = props.height ?? props.loc?.[3] ?? 200;
|
|
||||||
data.title = props.title ?? '';
|
|
||||||
};
|
|
||||||
|
|
||||||
setContentData();
|
|
||||||
setTextboxData();
|
|
||||||
|
|
||||||
watch(props, () => {
|
|
||||||
const needUpdateTitle = data.title !== props.title;
|
|
||||||
setTextboxData();
|
|
||||||
if (needUpdateTitle) {
|
|
||||||
onSetText();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const titleElement = ref<Text>();
|
const titleElement = ref<Text>();
|
||||||
const content = ref<TextContentExpose>();
|
const titleWidth = ref(data.titlePadding * 2);
|
||||||
const hidden = ref(props.hidden);
|
const titleHeight = ref(data.titlePadding * 2);
|
||||||
/** 标题宽度 */
|
|
||||||
const tw = ref(data.titlePadding! * 2);
|
|
||||||
/** 标题高度 */
|
|
||||||
const th = ref(data.titlePadding! * 2);
|
|
||||||
const contentY = computed(() => {
|
const contentY = computed(() => {
|
||||||
const height = th.value;
|
const height = titleHeight.value;
|
||||||
return data.title ? height : 0;
|
return data.title ? height : 0;
|
||||||
});
|
});
|
||||||
const backHeight = computed(() => data.height! - contentY.value);
|
|
||||||
const contentWidth = computed(() => data.width! - data.padding! * 2);
|
const contentWidth = computed(() => data.width! - data.padding! * 2);
|
||||||
const contentHeight = computed(
|
const contentHeight = computed(
|
||||||
() => data.height! - data.padding! * 2 - contentY.value
|
() => data.height! - data.padding! * 2 - contentY.value
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSetText = () => {
|
const calTitleSize = (text: string) => {
|
||||||
nextTick(() => {
|
if (!titleElement.value) return;
|
||||||
titleElement.value?.requestBeforeFrame(() => {
|
const { width, height } = titleElement.value;
|
||||||
if (titleElement.value) {
|
titleWidth.value = width + data.titlePadding! * 2;
|
||||||
const { width, height } = titleElement.value;
|
titleHeight.value = height + data.titlePadding! * 2;
|
||||||
tw.value = width + data.padding! * 2;
|
data.title = text;
|
||||||
th.value = height + data.padding! * 2;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(titleElement, (value, old) => {
|
||||||
|
old?.off('setText', calTitleSize);
|
||||||
|
value?.on('setText', calTitleSize);
|
||||||
|
if (value) calTitleSize(value?.text);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
titleElement.value?.off('setText', calTitleSize);
|
||||||
|
});
|
||||||
|
|
||||||
// ----- store
|
// ----- store
|
||||||
|
|
||||||
/** 结束打字机 */
|
/** 结束打字机 */
|
||||||
const storeEmits: TextboxStoreEmits = {
|
const storeEmits: TextboxStoreEmits = {
|
||||||
endType() {
|
endType() {
|
||||||
content.value?.showAll();
|
data.showAll = true;
|
||||||
},
|
|
||||||
hide() {
|
|
||||||
hidden.value = true;
|
|
||||||
},
|
|
||||||
show() {
|
|
||||||
hidden.value = false;
|
|
||||||
},
|
|
||||||
update(value) {
|
|
||||||
if (data.title !== value.title) {
|
|
||||||
data.title = value.title;
|
|
||||||
onSetText();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setText(text) {
|
|
||||||
if (contentData.text === text) {
|
|
||||||
content.value?.retype();
|
|
||||||
} else {
|
|
||||||
contentData.text = text;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const store = TextboxStore.use(
|
const store = TextboxStore.use(
|
||||||
props.id ?? getNextTextboxId(),
|
props.id ?? getNextTextboxId(),
|
||||||
contentData,
|
data,
|
||||||
storeEmits
|
storeEmits
|
||||||
);
|
);
|
||||||
|
const hidden = ref(data.hidden);
|
||||||
|
store.on('hide', () => (hidden.value = true));
|
||||||
|
store.on('show', () => (hidden.value = false));
|
||||||
|
store.on('update', value => {
|
||||||
|
if (value.title) {
|
||||||
|
titleElement.value?.requestBeforeFrame(() => {
|
||||||
|
const { width, height } = titleElement.value!;
|
||||||
|
titleWidth.value = width + data.padding! * 2;
|
||||||
|
titleHeight.value = height + data.padding! * 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const onTypeStart = () => {
|
const onTypeStart = () => {
|
||||||
store.emitTypeStart();
|
store.emitTypeStart();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTypeEnd = () => {
|
const onTypeEnd = () => {
|
||||||
|
data.showAll = false;
|
||||||
store.emitTypeEnd();
|
store.emitTypeEnd();
|
||||||
};
|
};
|
||||||
|
|
||||||
expose<TextboxExpose>({
|
return () => {
|
||||||
show() {
|
return (
|
||||||
hidden.value = false;
|
<container {...data} hidden={hidden.value} alpha={data.alpha}>
|
||||||
},
|
{data.title ? (
|
||||||
hide() {
|
<container
|
||||||
hidden.value = true;
|
zIndex={10}
|
||||||
},
|
width={titleWidth.value}
|
||||||
retype() {
|
height={titleHeight.value}
|
||||||
content.value?.retype();
|
>
|
||||||
},
|
{slots.title ? (
|
||||||
showAll() {
|
slots.title(data)
|
||||||
content.value?.showAll();
|
) : props.winskin ? (
|
||||||
}
|
<winskin image={props.winskin}></winskin>
|
||||||
});
|
) : (
|
||||||
|
<g-rect
|
||||||
return () => (
|
x={0}
|
||||||
<container
|
y={0}
|
||||||
id={props.id}
|
width={titleWidth.value}
|
||||||
hidden={hidden.value}
|
height={titleHeight.value}
|
||||||
alpha={data.alpha}
|
fillStyle={data.backColor}
|
||||||
loc={props.loc}
|
></g-rect>
|
||||||
>
|
)}
|
||||||
{data.title && (
|
<text
|
||||||
<container zIndex={10} loc={[0, 0, tw.value, th.value]}>
|
ref={titleElement}
|
||||||
{slots.title ? (
|
text={data.title}
|
||||||
slots.title(data)
|
x={data.titlePadding}
|
||||||
) : props.winskin ? (
|
y={data.titlePadding}
|
||||||
<winskin
|
fillStyle={data.titleFill}
|
||||||
image={props.winskin}
|
strokeStyle={data.titleStroke}
|
||||||
loc={[0, 0, tw.value, th.value]}
|
font={data.titleFont}
|
||||||
></winskin>
|
></text>
|
||||||
) : (
|
</container>
|
||||||
<g-rect loc={[0, 0, tw.value, th.value]}></g-rect>
|
) : (
|
||||||
)}
|
''
|
||||||
<text
|
)}
|
||||||
ref={titleElement}
|
{slots.default ? (
|
||||||
text={data.title}
|
slots.default(data)
|
||||||
loc={[data.titlePadding, data.titlePadding]}
|
) : props.winskin ? (
|
||||||
fillStyle={data.titleFill}
|
<winskin image={props.winskin}></winskin>
|
||||||
strokeStyle={data.titleStroke}
|
) : (
|
||||||
font={data.titleFont}
|
<g-rect
|
||||||
></text>
|
x={0}
|
||||||
</container>
|
y={contentY.value}
|
||||||
)}
|
width={data.width ?? 200}
|
||||||
{slots.default ? (
|
height={(data.height ?? 200) - contentY.value}
|
||||||
slots.default(data)
|
fill
|
||||||
) : props.winskin ? (
|
fillStyle={data.backColor}
|
||||||
<winskin
|
></g-rect>
|
||||||
image={props.winskin}
|
)}
|
||||||
loc={[0, contentY.value, data.width!, backHeight.value]}
|
<TextContent
|
||||||
></winskin>
|
{...data}
|
||||||
) : (
|
id=""
|
||||||
<g-rect
|
hidden={false}
|
||||||
loc={[0, contentY.value, data.width!, backHeight.value]}
|
x={data.padding!}
|
||||||
fill
|
y={contentY.value + data.padding!}
|
||||||
fillStyle={data.backColor}
|
width={contentWidth.value}
|
||||||
></g-rect>
|
height={contentHeight.value}
|
||||||
)}
|
onTypeEnd={onTypeEnd}
|
||||||
<TextContent
|
onTypeStart={onTypeStart}
|
||||||
{...contentData}
|
zIndex={0}
|
||||||
ref={content}
|
showAll={data.showAll}
|
||||||
x={data.padding!}
|
></TextContent>
|
||||||
y={contentY.value + data.padding!}
|
</container>
|
||||||
width={contentWidth.value}
|
);
|
||||||
height={contentHeight.value}
|
};
|
||||||
onTypeEnd={onTypeEnd}
|
|
||||||
onTypeStart={onTypeStart}
|
|
||||||
></TextContent>
|
|
||||||
</container>
|
|
||||||
);
|
|
||||||
}, textboxOptions);
|
}, textboxOptions);
|
||||||
|
|
||||||
interface TextboxStoreEmits {
|
interface TextboxStoreEmits {
|
||||||
endType: () => void;
|
endType: () => void;
|
||||||
hide: () => void;
|
|
||||||
show: () => void;
|
|
||||||
update: (value: TextboxProps) => void;
|
|
||||||
setText: (text: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TextboxStoreEvent {
|
interface TextboxStoreEvent {
|
||||||
@ -509,30 +436,21 @@ export class TextboxStore extends EventEmitter<TextboxStoreEvent> {
|
|||||||
// @ts-expect-error 无法推导
|
// @ts-expect-error 无法推导
|
||||||
if (!isNil(value)) this.data[key] = value;
|
if (!isNil(value)) this.data[key] = value;
|
||||||
}
|
}
|
||||||
this.emits.update(this.data);
|
|
||||||
this.emit('update', this.data);
|
this.emit('update', this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置显示的文本
|
|
||||||
* @param text 要显示的文本
|
|
||||||
*/
|
|
||||||
setText(text: string) {
|
|
||||||
this.emits.setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 显示文本框
|
* 显示文本框
|
||||||
*/
|
*/
|
||||||
show() {
|
show() {
|
||||||
this.emits.show();
|
this.emit('show');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 隐藏文本框
|
* 隐藏文本框
|
||||||
*/
|
*/
|
||||||
hide() {
|
hide() {
|
||||||
this.emits.hide();
|
this.emit('hide');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -135,16 +135,7 @@ export interface TyperIconRenderable {
|
|||||||
renderable: RenderableData | AutotileRenderable;
|
renderable: RenderableData | AutotileRenderable;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TyperWaitRenderable {
|
export type TyperRenderable = TyperTextRenderable | TyperIconRenderable;
|
||||||
type: TextContentType.Wait;
|
|
||||||
wait: number;
|
|
||||||
waited: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TyperRenderable =
|
|
||||||
| TyperTextRenderable
|
|
||||||
| TyperIconRenderable
|
|
||||||
| TyperWaitRenderable;
|
|
||||||
|
|
||||||
interface TextContentTyperEvent {
|
interface TextContentTyperEvent {
|
||||||
typeStart: [];
|
typeStart: [];
|
||||||
@ -280,64 +271,49 @@ export class TextContentTyper extends EventEmitter<TextContentTyperEvent> {
|
|||||||
private createTyperData(index: number, line: number) {
|
private createTyperData(index: number, line: number) {
|
||||||
const renderable = this.renderObject.data[index];
|
const renderable = this.renderObject.data[index];
|
||||||
if (!renderable) return false;
|
if (!renderable) return false;
|
||||||
switch (renderable.type) {
|
if (renderable.type === TextContentType.Text) {
|
||||||
case TextContentType.Text: {
|
if (line < 0 || line > renderable.splitLines.length) {
|
||||||
if (line < 0 || line > renderable.splitLines.length) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const start = renderable.splitLines[line - 1] ?? -1;
|
|
||||||
const end =
|
|
||||||
renderable.splitLines[line] ?? renderable.text.length - 1;
|
|
||||||
|
|
||||||
const data: TyperTextRenderable = {
|
|
||||||
type: TextContentType.Text,
|
|
||||||
x: this.x,
|
|
||||||
y: this.y,
|
|
||||||
text: renderable.text.slice(start + 1, end + 1),
|
|
||||||
font: renderable.font,
|
|
||||||
fillStyle: renderable.fillStyle,
|
|
||||||
strokeStyle: this.config.strokeStyle,
|
|
||||||
pointer: 0
|
|
||||||
};
|
|
||||||
this.processingData = data;
|
|
||||||
this.renderData.push(data);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
case TextContentType.Icon: {
|
const start = renderable.splitLines[line - 1] ?? 0;
|
||||||
const tex = texture.getRenderable(renderable.icon!);
|
const end = renderable.splitLines[line] ?? renderable.text.length;
|
||||||
if (!tex) return false;
|
const data: TyperTextRenderable = {
|
||||||
const { render } = tex;
|
type: TextContentType.Text,
|
||||||
const [, , width, height] = render[0];
|
x: this.x,
|
||||||
const aspect = width / height;
|
y: this.y,
|
||||||
let iconWidth = 0;
|
text: renderable.text.slice(start, end),
|
||||||
if (aspect < 1) {
|
font: renderable.font,
|
||||||
// 这时候应该把高度限定在当前字体大小
|
fillStyle: renderable.fillStyle,
|
||||||
iconWidth = width * (renderable.fontSize / height);
|
strokeStyle: this.config.strokeStyle,
|
||||||
} else {
|
pointer: 0
|
||||||
iconWidth = renderable.fontSize;
|
};
|
||||||
}
|
this.processingData = data;
|
||||||
const data: TyperIconRenderable = {
|
this.renderData.push(data);
|
||||||
type: TextContentType.Icon,
|
return true;
|
||||||
x: this.x,
|
} else {
|
||||||
y: this.y,
|
const tex = texture.getRenderable(renderable.icon!);
|
||||||
width: iconWidth,
|
if (!tex) return false;
|
||||||
height: iconWidth / aspect,
|
const { render } = tex;
|
||||||
renderable: tex
|
const [, , width, height] = render[0];
|
||||||
};
|
const aspect = width / height;
|
||||||
this.processingData = data;
|
let iconWidth = 0;
|
||||||
this.renderData.push(data);
|
if (aspect < 1) {
|
||||||
return true;
|
// 这时候应该把高度限定在当前字体大小
|
||||||
}
|
iconWidth = width * (renderable.fontSize / height);
|
||||||
case TextContentType.Wait: {
|
} else {
|
||||||
const data: TyperWaitRenderable = {
|
iconWidth = renderable.fontSize;
|
||||||
type: TextContentType.Wait,
|
|
||||||
wait: renderable.wait!,
|
|
||||||
waited: 0
|
|
||||||
};
|
|
||||||
this.processingData = data;
|
|
||||||
this.renderData.push(data);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
const data: TyperIconRenderable = {
|
||||||
|
type: TextContentType.Icon,
|
||||||
|
x: this.x,
|
||||||
|
y: this.y,
|
||||||
|
width: iconWidth,
|
||||||
|
height: iconWidth / aspect,
|
||||||
|
renderable: tex
|
||||||
|
};
|
||||||
|
this.processingData = data;
|
||||||
|
this.renderData.push(data);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,64 +339,62 @@ export class TextContentTyper extends EventEmitter<TextContentTyperEvent> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const lineHeight = this.renderObject.lineHeights[this.nowLine];
|
const lineHeight = this.renderObject.lineHeights[this.nowLine];
|
||||||
switch (now.type) {
|
if (now.type === TextContentType.Text) {
|
||||||
case TextContentType.Text: {
|
const restChars = now.text.length - now.pointer;
|
||||||
const restChars = now.text.length - now.pointer;
|
if (restChars <= rest) {
|
||||||
if (restChars <= rest) {
|
// 当前这段 renderable 打字完成后,刚好结束或还有内容
|
||||||
// 当前这段 renderable 打字完成后,刚好结束或还有内容
|
rest -= restChars;
|
||||||
rest -= restChars;
|
now.pointer = now.text.length;
|
||||||
now.pointer = now.text.length;
|
if (this.dataLine === renderable.splitLines.length) {
|
||||||
if (this.dataLine === renderable.splitLines.length) {
|
// 如果是最后一行
|
||||||
// 如果是最后一行
|
if (isNil(renderable.lastLineWidth)) {
|
||||||
if (isNil(renderable.lastLineWidth)) {
|
const ctx = this.parser.testCanvas.ctx;
|
||||||
const ctx = this.parser.testCanvas.ctx;
|
ctx.font = now.font;
|
||||||
ctx.font = now.font;
|
const metrics = ctx.measureText(now.text);
|
||||||
const metrics = ctx.measureText(now.text);
|
renderable.lastLineWidth = metrics.width;
|
||||||
renderable.lastLineWidth = metrics.width;
|
|
||||||
}
|
|
||||||
this.x += renderable.lastLineWidth;
|
|
||||||
this.dataLine = 0;
|
|
||||||
this.pointer++;
|
|
||||||
} else {
|
|
||||||
// 不是最后一行,那么换行
|
|
||||||
this.x = 0;
|
|
||||||
this.y += lineHeight + this.config.lineHeight;
|
|
||||||
this.dataLine++;
|
|
||||||
this.nowLine++;
|
|
||||||
}
|
}
|
||||||
|
this.x += renderable.lastLineWidth;
|
||||||
|
this.dataLine = 0;
|
||||||
|
this.pointer++;
|
||||||
|
const success = this.createTyperData(
|
||||||
|
this.pointer,
|
||||||
|
this.dataLine
|
||||||
|
);
|
||||||
|
if (!success) return true;
|
||||||
} else {
|
} else {
|
||||||
now.pointer += rest;
|
// 不是最后一行,那么换行
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TextContentType.Icon: {
|
|
||||||
rest--;
|
|
||||||
this.pointer++;
|
|
||||||
if (renderable.splitLines[0] === 0) {
|
|
||||||
// 如果图标换行
|
|
||||||
this.x = 0;
|
this.x = 0;
|
||||||
this.y += lineHeight + this.config.lineHeight;
|
this.y += lineHeight + this.config.lineHeight;
|
||||||
|
this.dataLine++;
|
||||||
this.nowLine++;
|
this.nowLine++;
|
||||||
this.dataLine = 0;
|
const success = this.createTyperData(
|
||||||
now.x = 0;
|
this.pointer,
|
||||||
now.y = this.y;
|
this.dataLine
|
||||||
} else {
|
);
|
||||||
this.x += now.width;
|
if (!success) return true;
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
|
now.pointer += rest;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
case TextContentType.Wait: {
|
} else {
|
||||||
now.waited += num;
|
rest--;
|
||||||
if (now.waited > now.wait) {
|
this.pointer++;
|
||||||
// 等待结束
|
if (renderable.splitLines[0] === 0) {
|
||||||
this.pointer++;
|
// 如果图标换行
|
||||||
}
|
this.x = 0;
|
||||||
break;
|
this.y += lineHeight + this.config.lineHeight;
|
||||||
|
this.nowLine++;
|
||||||
|
this.dataLine = 0;
|
||||||
|
now.x = 0;
|
||||||
|
now.y = this.y;
|
||||||
}
|
}
|
||||||
|
const success = this.createTyperData(
|
||||||
|
this.pointer,
|
||||||
|
this.dataLine
|
||||||
|
);
|
||||||
|
if (!success) return true;
|
||||||
}
|
}
|
||||||
const success = this.createTyperData(this.pointer, this.dataLine);
|
|
||||||
if (!success) return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -448,7 +422,6 @@ export class TextContentTyper extends EventEmitter<TextContentTyperEvent> {
|
|||||||
* 开始打字
|
* 开始打字
|
||||||
*/
|
*/
|
||||||
type() {
|
type() {
|
||||||
if (this.typing) return;
|
|
||||||
if (this.config.interval === 0) {
|
if (this.config.interval === 0) {
|
||||||
this.emit('typeStart');
|
this.emit('typeStart');
|
||||||
this.typeChars(Infinity);
|
this.typeChars(Infinity);
|
||||||
@ -465,7 +438,6 @@ export class TextContentTyper extends EventEmitter<TextContentTyperEvent> {
|
|||||||
* 立即显示所有文字
|
* 立即显示所有文字
|
||||||
*/
|
*/
|
||||||
typeAll() {
|
typeAll() {
|
||||||
if (!this.typing) return;
|
|
||||||
this.typeChars(Infinity);
|
this.typeChars(Infinity);
|
||||||
this.render?.(this.renderData, false);
|
this.render?.(this.renderData, false);
|
||||||
}
|
}
|
||||||
@ -589,7 +561,7 @@ export class TextContentParser {
|
|||||||
const end = this.indexParam(start);
|
const end = this.indexParam(start);
|
||||||
if (end === -1) {
|
if (end === -1) {
|
||||||
// 标签结束
|
// 标签结束
|
||||||
return ['', start - 1];
|
return ['', start];
|
||||||
} else {
|
} else {
|
||||||
// 标签开始
|
// 标签开始
|
||||||
return [this.text.slice(start + 1, end), end];
|
return [this.text.slice(start + 1, end), end];
|
||||||
@ -660,43 +632,43 @@ export class TextContentParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private parseFillStyle(pointer: number) {
|
private parseFillStyle(pointer: number) {
|
||||||
const [param, end] = this.getChildableTagParam(pointer + 2);
|
const [param, end] = this.getChildableTagParam(pointer + 1);
|
||||||
if (!param) {
|
if (!param) {
|
||||||
// 参数为空或没有参数,视为标签结束
|
// 参数为空或没有参数,视为标签结束
|
||||||
const color = this.fillStyleStack.pop();
|
const color = this.fillStyleStack.pop();
|
||||||
if (!color) {
|
if (!color) {
|
||||||
logger.warn(54, '\\r', pointer.toString());
|
logger.warn(54, '\\r', pointer.toString());
|
||||||
return end;
|
return pointer;
|
||||||
}
|
}
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
this.status.fillStyle = color;
|
this.status.fillStyle = color;
|
||||||
return end;
|
return pointer;
|
||||||
} else {
|
} else {
|
||||||
// 标签开始
|
// 标签开始
|
||||||
this.fillStyleStack.push(this.status.fillStyle);
|
this.fillStyleStack.push(this.status.fillStyle);
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
this.status.fillStyle = param;
|
this.status.fillStyle = param;
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseFontSize(pointer: number) {
|
private parseFontSize(pointer: number) {
|
||||||
const [param, end] = this.getChildableTagParam(pointer + 2);
|
const [param, end] = this.getChildableTagParam(pointer + 1);
|
||||||
if (!param) {
|
if (!param) {
|
||||||
// 参数为空或没有参数,视为标签结束
|
// 参数为空或没有参数,视为标签结束
|
||||||
const size = this.fontSizeStack.pop();
|
const size = this.fontSizeStack.pop();
|
||||||
if (!size) {
|
if (!size) {
|
||||||
logger.warn(54, '\\c', pointer.toString());
|
logger.warn(54, '\\c', pointer.toString());
|
||||||
return end;
|
return pointer;
|
||||||
}
|
}
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
this.status.fontSize = size;
|
this.status.fontSize = size;
|
||||||
this.font = this.buildFont();
|
this.font = this.buildFont();
|
||||||
return end;
|
return pointer;
|
||||||
} else {
|
} else {
|
||||||
// 标签开始
|
// 标签开始
|
||||||
this.fontSizeStack.push(this.status.fontSize);
|
this.fontSizeStack.push(this.status.fontSize);
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
this.status.fontSize = parseFloat(param);
|
this.status.fontSize = parseFloat(param);
|
||||||
this.font = this.buildFont();
|
this.font = this.buildFont();
|
||||||
return end;
|
return end;
|
||||||
@ -704,22 +676,22 @@ export class TextContentParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private parseFontFamily(pointer: number) {
|
private parseFontFamily(pointer: number) {
|
||||||
const [param, end] = this.getChildableTagParam(pointer + 2);
|
const [param, end] = this.getChildableTagParam(pointer + 1);
|
||||||
if (!param) {
|
if (!param) {
|
||||||
// 参数为空或没有参数,视为标签结束
|
// 参数为空或没有参数,视为标签结束
|
||||||
const font = this.fontFamilyStack.pop();
|
const font = this.fontFamilyStack.pop();
|
||||||
if (!font) {
|
if (!font) {
|
||||||
logger.warn(54, '\\g', pointer.toString());
|
logger.warn(54, '\\g', pointer.toString());
|
||||||
return end;
|
return pointer;
|
||||||
}
|
}
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
this.status.fontFamily = font;
|
this.status.fontFamily = font;
|
||||||
this.font = this.buildFont();
|
this.font = this.buildFont();
|
||||||
return end;
|
return pointer;
|
||||||
} else {
|
} else {
|
||||||
// 标签开始
|
// 标签开始
|
||||||
this.fontFamilyStack.push(this.status.fontFamily);
|
this.fontFamilyStack.push(this.status.fontFamily);
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
this.status.fontFamily = param;
|
this.status.fontFamily = param;
|
||||||
this.font = this.buildFont();
|
this.font = this.buildFont();
|
||||||
return end;
|
return end;
|
||||||
@ -727,20 +699,20 @@ export class TextContentParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private parseFontWeight() {
|
private parseFontWeight() {
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
this.status.fontWeight = this.status.fontWeight > 500 ? 500 : 700;
|
this.status.fontWeight = this.status.fontWeight > 500 ? 500 : 700;
|
||||||
this.font = this.buildFont();
|
this.font = this.buildFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseFontItalic() {
|
private parseFontItalic() {
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
this.status.fontItalic = !this.status.fontItalic;
|
this.status.fontItalic = !this.status.fontItalic;
|
||||||
this.font = this.buildFont();
|
this.font = this.buildFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseWait(pointer: number) {
|
private parseWait(pointer: number) {
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
const [param, end] = this.getTagParam(pointer + 2);
|
const [param, end] = this.getTagParam(pointer);
|
||||||
if (!param) {
|
if (!param) {
|
||||||
logger.warn(55, '\\z');
|
logger.warn(55, '\\z');
|
||||||
return pointer;
|
return pointer;
|
||||||
@ -751,10 +723,10 @@ export class TextContentParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private parseIcon(pointer: number) {
|
private parseIcon(pointer: number) {
|
||||||
if (this.resolved.length > 0) this.addTextRenderable();
|
this.addTextRenderable();
|
||||||
const [param, end] = this.getTagParam(pointer + 2);
|
const [param, end] = this.getTagParam(pointer);
|
||||||
if (!param) {
|
if (!param) {
|
||||||
logger.warn(55, '\\i');
|
logger.warn(55, '\\z');
|
||||||
return pointer;
|
return pointer;
|
||||||
}
|
}
|
||||||
if (/^\d+$/.test(param)) {
|
if (/^\d+$/.test(param)) {
|
||||||
@ -767,10 +739,6 @@ export class TextContentParser {
|
|||||||
this.addIconRenderable(num as AllNumbers);
|
this.addIconRenderable(num as AllNumbers);
|
||||||
} else {
|
} else {
|
||||||
const num = texture.idNumberMap[param as AllIds];
|
const num = texture.idNumberMap[param as AllIds];
|
||||||
if (num === void 0) {
|
|
||||||
logger.warn(59, param);
|
|
||||||
return end;
|
|
||||||
}
|
|
||||||
this.addIconRenderable(num);
|
this.addIconRenderable(num);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -874,11 +842,9 @@ export class TextContentParser {
|
|||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
this.parseFontWeight();
|
this.parseFontWeight();
|
||||||
pointer++;
|
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
this.parseFontItalic();
|
this.parseFontItalic();
|
||||||
pointer++;
|
|
||||||
break;
|
break;
|
||||||
case 'z':
|
case 'z':
|
||||||
pointer = this.parseWait(pointer);
|
pointer = this.parseWait(pointer);
|
||||||
@ -895,7 +861,7 @@ export class TextContentParser {
|
|||||||
// 表达式
|
// 表达式
|
||||||
pointer++;
|
pointer++;
|
||||||
inExpression = true;
|
inExpression = true;
|
||||||
expStart = pointer + 1;
|
expStart = pointer;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -909,8 +875,7 @@ export class TextContentParser {
|
|||||||
|
|
||||||
private getHeight(metrics: TextMetrics) {
|
private getHeight(metrics: TextMetrics) {
|
||||||
return (
|
return (
|
||||||
Math.abs(metrics.actualBoundingBoxAscent) +
|
metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent
|
||||||
Math.abs(metrics.actualBoundingBoxDescent)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -934,14 +899,10 @@ export class TextContentParser {
|
|||||||
// 如果大于猜测,那么算长度
|
// 如果大于猜测,那么算长度
|
||||||
const data = this.renderable[this.nowRenderable];
|
const data = this.renderable[this.nowRenderable];
|
||||||
const ctx = this.testCanvas.ctx;
|
const ctx = this.testCanvas.ctx;
|
||||||
ctx.font = data.font;
|
ctx.font = this.font;
|
||||||
const metrics = ctx.measureText(
|
const metrics = ctx.measureText(
|
||||||
data.text.slice(this.lineStart, pointer + 1)
|
data.text.slice(this.lineStart, pointer + 1)
|
||||||
);
|
);
|
||||||
const height = this.getHeight(metrics);
|
|
||||||
if (height > this.lineHeight) {
|
|
||||||
this.lineHeight = height;
|
|
||||||
}
|
|
||||||
if (metrics.width < rest) {
|
if (metrics.width < rest) {
|
||||||
// 实际宽度小于剩余宽度时,将猜测增益乘以剩余总宽度与当前宽度的比值的若干倍
|
// 实际宽度小于剩余宽度时,将猜测增益乘以剩余总宽度与当前宽度的比值的若干倍
|
||||||
this.guessGain *= (rest / metrics.width) * (1.1 + 1 / length);
|
this.guessGain *= (rest / metrics.width) * (1.1 + 1 / length);
|
||||||
@ -986,7 +947,7 @@ export class TextContentParser {
|
|||||||
const data = this.renderable[index];
|
const data = this.renderable[index];
|
||||||
const { wordBreak } = data;
|
const { wordBreak } = data;
|
||||||
const ctx = this.testCanvas.ctx;
|
const ctx = this.testCanvas.ctx;
|
||||||
ctx.font = data.font;
|
ctx.font = this.font;
|
||||||
while (true) {
|
while (true) {
|
||||||
const mid = Math.floor((start + end) / 2);
|
const mid = Math.floor((start + end) / 2);
|
||||||
if (mid === start) {
|
if (mid === start) {
|
||||||
@ -997,33 +958,31 @@ export class TextContentParser {
|
|||||||
}
|
}
|
||||||
const text = data.text.slice(
|
const text = data.text.slice(
|
||||||
wordBreak[this.bsStart],
|
wordBreak[this.bsStart],
|
||||||
wordBreak[mid] + 1
|
wordBreak[mid]
|
||||||
);
|
);
|
||||||
const metrics = ctx.measureText(text);
|
const metrics = ctx.measureText(text);
|
||||||
height = this.getHeight(metrics);
|
|
||||||
if (metrics.width > width) {
|
if (metrics.width > width) {
|
||||||
end = mid;
|
end = mid;
|
||||||
} else if (metrics.width === width) {
|
} else if (metrics.width === width) {
|
||||||
if (height > this.lineHeight) {
|
|
||||||
this.lineHeight = height;
|
|
||||||
}
|
|
||||||
return mid;
|
return mid;
|
||||||
} else {
|
} else {
|
||||||
start = mid;
|
start = mid;
|
||||||
}
|
}
|
||||||
|
height = this.getHeight(metrics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查剩余字符能否分行
|
* 检查剩余字符能否分行
|
||||||
*/
|
*/
|
||||||
private checkRestLine(width: number, guess: number, pointer: number) {
|
private checkRestLine(width: number, guess: number) {
|
||||||
if (this.wordBreak.length === 0) return true;
|
if (this.wordBreak.length === 0) return true;
|
||||||
if (pointer === -1) {
|
const last = this.nowRenderable - 1;
|
||||||
return this.checkLineWidth(width, guess, 0);
|
if (last === -1) {
|
||||||
|
const now = this.renderable[this.nowRenderable];
|
||||||
|
return this.checkLineWidth(width, guess, now.text.length);
|
||||||
}
|
}
|
||||||
const isLast = this.renderable.length - 1 === pointer;
|
const data = this.renderable[last];
|
||||||
const data = this.renderable[pointer];
|
|
||||||
const rest = width - this.lineWidth;
|
const rest = width - this.lineWidth;
|
||||||
if (data.type === TextContentType.Text) {
|
if (data.type === TextContentType.Text) {
|
||||||
const wordBreak = data.wordBreak;
|
const wordBreak = data.wordBreak;
|
||||||
@ -1031,7 +990,7 @@ export class TextContentParser {
|
|||||||
const lastIndex = isNil(lastLine) ? 0 : lastLine;
|
const lastIndex = isNil(lastLine) ? 0 : lastLine;
|
||||||
const restText = data.text.slice(lastIndex);
|
const restText = data.text.slice(lastIndex);
|
||||||
const ctx = this.testCanvas.ctx;
|
const ctx = this.testCanvas.ctx;
|
||||||
ctx.font = data.font;
|
ctx.font = this.font;
|
||||||
const metrics = ctx.measureText(restText);
|
const metrics = ctx.measureText(restText);
|
||||||
// 如果剩余内容不能构成完整的行
|
// 如果剩余内容不能构成完整的行
|
||||||
if (metrics.width < rest) {
|
if (metrics.width < rest) {
|
||||||
@ -1049,12 +1008,12 @@ export class TextContentParser {
|
|||||||
this.bsEnd = lastBreak;
|
this.bsEnd = lastBreak;
|
||||||
let maxWidth = rest;
|
let maxWidth = rest;
|
||||||
while (true) {
|
while (true) {
|
||||||
const index = this.bsLineWidth(maxWidth, pointer);
|
const index = this.bsLineWidth(maxWidth, last);
|
||||||
data.splitLines.push(this.wordBreak[index]);
|
data.splitLines.push(this.wordBreak[index]);
|
||||||
this.lineHeights.push(this.lineHeight);
|
this.lineHeights.push(this.lineHeight);
|
||||||
this.bsStart = index;
|
this.bsStart = index;
|
||||||
const text = data.text.slice(this.wordBreak[index]);
|
const text = data.text.slice(this.wordBreak[index]);
|
||||||
if (!isLast && text.length < guess / 4) {
|
if (text.length < guess / 4) {
|
||||||
// 如果剩余文字很少,几乎不可能会单独成一行时,直接结束循环
|
// 如果剩余文字很少,几乎不可能会单独成一行时,直接结束循环
|
||||||
this.lastBreakIndex = index;
|
this.lastBreakIndex = index;
|
||||||
break;
|
break;
|
||||||
@ -1081,15 +1040,9 @@ export class TextContentParser {
|
|||||||
} else {
|
} else {
|
||||||
iconWidth = this.status.fontSize;
|
iconWidth = this.status.fontSize;
|
||||||
}
|
}
|
||||||
this.lineWidth += iconWidth;
|
|
||||||
const iconHeight = iconWidth / aspect;
|
|
||||||
if (iconHeight > this.lineHeight) {
|
|
||||||
this.lineHeight = iconHeight;
|
|
||||||
}
|
|
||||||
if (iconWidth > rest) {
|
if (iconWidth > rest) {
|
||||||
data.splitLines.push(0);
|
data.splitLines.push(0);
|
||||||
this.lineHeights.push(this.lineHeight);
|
this.lineHeights.push(this.lineHeight);
|
||||||
this.lineWidth = 0;
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -1117,6 +1070,8 @@ export class TextContentParser {
|
|||||||
|
|
||||||
const allBreak = this.wordBreakRule === WordBreak.All;
|
const allBreak = this.wordBreakRule === WordBreak.All;
|
||||||
|
|
||||||
|
// debugger;
|
||||||
|
|
||||||
for (let i = 0; i < this.renderable.length; i++) {
|
for (let i = 0; i < this.renderable.length; i++) {
|
||||||
const data = this.renderable[i];
|
const data = this.renderable[i];
|
||||||
const { wordBreak, fontSize } = data;
|
const { wordBreak, fontSize } = data;
|
||||||
@ -1125,7 +1080,7 @@ export class TextContentParser {
|
|||||||
this.wordBreak = wordBreak;
|
this.wordBreak = wordBreak;
|
||||||
|
|
||||||
if (data.type === TextContentType.Icon) {
|
if (data.type === TextContentType.Icon) {
|
||||||
this.checkRestLine(width, guess, i - 1);
|
this.checkRestLine(width, guess);
|
||||||
continue;
|
continue;
|
||||||
} else if (data.type === TextContentType.Wait) {
|
} else if (data.type === TextContentType.Wait) {
|
||||||
continue;
|
continue;
|
||||||
@ -1167,7 +1122,7 @@ export class TextContentParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkRestLine(width, guess, i);
|
this.checkRestLine(width, guess);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -4,7 +4,6 @@ import { defineComponent } from 'vue';
|
|||||||
import { UIController } from '@/core/system';
|
import { UIController } from '@/core/system';
|
||||||
import { mainSceneUI } from './ui/main';
|
import { mainSceneUI } from './ui/main';
|
||||||
import { MAIN_HEIGHT, MAIN_WIDTH } from './shared';
|
import { MAIN_HEIGHT, MAIN_WIDTH } from './shared';
|
||||||
import { TextboxStore } from './components';
|
|
||||||
|
|
||||||
export function create() {
|
export function create() {
|
||||||
const main = new MotaRenderer();
|
const main = new MotaRenderer();
|
||||||
@ -35,10 +34,6 @@ export function create() {
|
|||||||
console.log(main);
|
console.log(main);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mota.register('module', 'MainUI', {
|
|
||||||
TextboxStore
|
|
||||||
});
|
|
||||||
|
|
||||||
export * from './components';
|
export * from './components';
|
||||||
export * from './ui';
|
export * from './ui';
|
||||||
export * from './use';
|
export * from './use';
|
||||||
|
@ -57,7 +57,9 @@ const MainScene = defineComponent(() => {
|
|||||||
const mainTextboxProps: Props<typeof Textbox> = {
|
const mainTextboxProps: Props<typeof Textbox> = {
|
||||||
text: '',
|
text: '',
|
||||||
hidden: true,
|
hidden: true,
|
||||||
loc: [0, 330, 480, 150],
|
width: 480,
|
||||||
|
height: 150,
|
||||||
|
y: 330,
|
||||||
zIndex: 30,
|
zIndex: 30,
|
||||||
fillStyle: '#fff',
|
fillStyle: '#fff',
|
||||||
titleFill: 'gold',
|
titleFill: 'gold',
|
||||||
@ -65,7 +67,7 @@ const MainScene = defineComponent(() => {
|
|||||||
titleFont: '700 20px normal',
|
titleFont: '700 20px normal',
|
||||||
winskin: 'winskin2.png',
|
winskin: 'winskin2.png',
|
||||||
interval: 100,
|
interval: 100,
|
||||||
lineHeight: 4
|
lineHeight: 6
|
||||||
};
|
};
|
||||||
|
|
||||||
const map = ref<LayerGroup>();
|
const map = ref<LayerGroup>();
|
||||||
|
Loading…
Reference in New Issue
Block a user