mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-19 17:16:08 +08:00
refactor: graphics 定位与渲染
This commit is contained in:
parent
dfa7428ba8
commit
d2bd98b735
@ -116,7 +116,9 @@ export class Container<E extends EContainerEvent = EContainerEvent>
|
|||||||
if (progress === EventProgress.Capture) {
|
if (progress === EventProgress.Capture) {
|
||||||
let success = false;
|
let success = false;
|
||||||
for (let i = len - 1; i >= 0; i--) {
|
for (let i = len - 1; i >= 0; i--) {
|
||||||
if (this.sortedChildren[i].captureEvent(type, event)) {
|
const child = this.sortedChildren[i];
|
||||||
|
if (child.hidden) continue;
|
||||||
|
if (child.captureEvent(type, event)) {
|
||||||
success = true;
|
success = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -228,6 +228,9 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
|
|
||||||
//#region 元素属性
|
//#region 元素属性
|
||||||
|
|
||||||
|
/** 是否是注释元素 */
|
||||||
|
readonly isComment: boolean = false;
|
||||||
|
|
||||||
private _id: string = '';
|
private _id: string = '';
|
||||||
/**
|
/**
|
||||||
* 元素的 id,原则上不可重复
|
* 元素的 id,原则上不可重复
|
||||||
@ -561,10 +564,22 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
? this.fallTransform
|
? this.fallTransform
|
||||||
: this._transform;
|
: this._transform;
|
||||||
if (!tran) return new DOMRectReadOnly(0, 0, this.width, this.height);
|
if (!tran) return new DOMRectReadOnly(0, 0, this.width, this.height);
|
||||||
const [x1, y1] = tran.transformed(0, 0);
|
const [x1, y1] = tran.transformed(
|
||||||
const [x2, y2] = tran.transformed(this.width, 0);
|
-this.anchorX * this.width,
|
||||||
const [x3, y3] = tran.transformed(0, this.height);
|
-this.anchorY * this.height
|
||||||
const [x4, y4] = tran.transformed(this.width, this.height);
|
);
|
||||||
|
const [x2, y2] = tran.transformed(
|
||||||
|
this.width * (1 - this.anchorX),
|
||||||
|
-this.anchorY * this.height
|
||||||
|
);
|
||||||
|
const [x3, y3] = tran.transformed(
|
||||||
|
-this.anchorX * this.width,
|
||||||
|
this.height * (1 - this.anchorY)
|
||||||
|
);
|
||||||
|
const [x4, y4] = tran.transformed(
|
||||||
|
this.width * (1 - this.anchorX),
|
||||||
|
this.height * (1 - this.anchorY)
|
||||||
|
);
|
||||||
const left = Math.min(x1, x2, x3, x4);
|
const left = Math.min(x1, x2, x3, x4);
|
||||||
const right = Math.max(x1, x2, x3, x4);
|
const right = Math.max(x1, x2, x3, x4);
|
||||||
const top = Math.min(y1, y2, y3, y4);
|
const top = Math.min(y1, y2, y3, y4);
|
||||||
@ -1142,6 +1157,19 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
this.cursor = nextValue;
|
this.cursor = nextValue;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case 'scale': {
|
||||||
|
if (!this.assertType(nextValue, Array, key)) return;
|
||||||
|
this._transform.setScale(
|
||||||
|
nextValue[0] as number,
|
||||||
|
nextValue[1] as number
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'rotate': {
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
|
this._transform.setRotate(nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const ev = this.parseEvent(key);
|
const ev = this.parseEvent(key);
|
||||||
if (ev) {
|
if (ev) {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
import { ERenderItemEvent, RenderItem, RenderItemPosition } from '../item';
|
import { ERenderItemEvent, RenderItem } from '../item';
|
||||||
import { Transform } from '../transform';
|
import { Transform } from '../transform';
|
||||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||||
import { isNil } from 'lodash-es';
|
import { clamp, isNil } from 'lodash-es';
|
||||||
|
import { logger } from '@/core/common/logger';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expected usage (this comment needs to be deleted after implementing correctly):
|
* Expected usage (this comment needs to be deleted after implementing correctly):
|
||||||
@ -63,7 +64,7 @@ export abstract class GraphicItemBase
|
|||||||
extends RenderItem<EGraphicItemEvent>
|
extends RenderItem<EGraphicItemEvent>
|
||||||
implements Required<ILineProperty>
|
implements Required<ILineProperty>
|
||||||
{
|
{
|
||||||
mode: number = GraphicMode.Fill;
|
mode: GraphicMode = GraphicMode.Fill;
|
||||||
fill: CanvasStyle = '#fff';
|
fill: CanvasStyle = '#fff';
|
||||||
stroke: CanvasStyle = '#fff';
|
stroke: CanvasStyle = '#fff';
|
||||||
lineWidth: number = 2;
|
lineWidth: number = 2;
|
||||||
@ -79,6 +80,9 @@ export abstract class GraphicItemBase
|
|||||||
private strokeAndFill: boolean = false;
|
private strokeAndFill: boolean = false;
|
||||||
private propFillSet: boolean = false;
|
private propFillSet: boolean = false;
|
||||||
|
|
||||||
|
private cachePath?: Path2D;
|
||||||
|
protected pathDirty: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取这个元素的绘制路径
|
* 获取这个元素的绘制路径
|
||||||
*/
|
*/
|
||||||
@ -90,7 +94,12 @@ export abstract class GraphicItemBase
|
|||||||
): void {
|
): void {
|
||||||
const ctx = canvas.ctx;
|
const ctx = canvas.ctx;
|
||||||
this.setCanvasState(canvas);
|
this.setCanvasState(canvas);
|
||||||
const path = this.getPath();
|
if (this.pathDirty) {
|
||||||
|
this.cachePath = this.getPath();
|
||||||
|
this.pathDirty = false;
|
||||||
|
}
|
||||||
|
const path = this.cachePath;
|
||||||
|
if (!path) return;
|
||||||
switch (this.mode) {
|
switch (this.mode) {
|
||||||
case GraphicMode.Fill:
|
case GraphicMode.Fill:
|
||||||
ctx.fill(path, this.fillRule);
|
ctx.fill(path, this.fillRule);
|
||||||
@ -111,12 +120,16 @@ export abstract class GraphicItemBase
|
|||||||
|
|
||||||
protected isActionInElement(x: number, y: number): boolean {
|
protected isActionInElement(x: number, y: number): boolean {
|
||||||
const ctx = this.cache.ctx;
|
const ctx = this.cache.ctx;
|
||||||
const path = this.getPath();
|
if (this.pathDirty) {
|
||||||
|
this.cachePath = this.getPath();
|
||||||
|
this.pathDirty = false;
|
||||||
|
}
|
||||||
|
const path = this.cachePath;
|
||||||
|
if (!path) return false;
|
||||||
switch (this.mode) {
|
switch (this.mode) {
|
||||||
case GraphicMode.Fill:
|
case GraphicMode.Fill:
|
||||||
return ctx.isPointInPath(path, x, y, this.fillRule);
|
return ctx.isPointInPath(path, x, y, this.fillRule);
|
||||||
case GraphicMode.Stroke:
|
case GraphicMode.Stroke:
|
||||||
return ctx.isPointInStroke(path, x, y);
|
|
||||||
case GraphicMode.FillAndStroke:
|
case GraphicMode.FillAndStroke:
|
||||||
case GraphicMode.StrokeAndFill:
|
case GraphicMode.StrokeAndFill:
|
||||||
return (
|
return (
|
||||||
@ -124,7 +137,6 @@ export abstract class GraphicItemBase
|
|||||||
ctx.isPointInStroke(path, x, y)
|
ctx.isPointInStroke(path, x, y)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,7 +284,7 @@ export abstract class GraphicItemBase
|
|||||||
break;
|
break;
|
||||||
case 'lineDash':
|
case 'lineDash':
|
||||||
if (!this.assertType(nextValue, Array, key)) return;
|
if (!this.assertType(nextValue, Array, key)) return;
|
||||||
this.lineDash = nextValue;
|
this.lineDash = nextValue as number[];
|
||||||
this.update();
|
this.update();
|
||||||
break;
|
break;
|
||||||
case 'lineDashOffset':
|
case 'lineDashOffset':
|
||||||
@ -301,9 +313,19 @@ export abstract class GraphicItemBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Rect extends GraphicItemBase {
|
export class Rect extends GraphicItemBase {
|
||||||
|
pos(x: number, y: number): void {
|
||||||
|
super.pos(x, y);
|
||||||
|
this.pathDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size(width: number, height: number): void {
|
||||||
|
super.size(width, height);
|
||||||
|
this.pathDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
getPath(): Path2D {
|
getPath(): Path2D {
|
||||||
const path = new Path2D();
|
const path = new Path2D();
|
||||||
path.rect(this.x, this.y, this.width, this.height);
|
path.rect(0, 0, this.width, this.height);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,32 +334,13 @@ export class Circle extends GraphicItemBase {
|
|||||||
radius: number = 10;
|
radius: number = 10;
|
||||||
start: number = 0;
|
start: number = 0;
|
||||||
end: number = Math.PI * 2;
|
end: number = Math.PI * 2;
|
||||||
|
anchorX: number = 0.5;
|
||||||
|
anchorY: number = 0.5;
|
||||||
|
|
||||||
protected render(
|
getPath(): Path2D {
|
||||||
canvas: MotaOffscreenCanvas2D,
|
const path = new Path2D();
|
||||||
_transform: Transform
|
path.arc(this.radius, this.radius, this.radius, this.start, this.end);
|
||||||
): void {
|
return path;
|
||||||
const ctx = canvas.ctx;
|
|
||||||
this.setCanvasState(canvas);
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(this.x, this.y, this.radius, this.start, this.end);
|
|
||||||
|
|
||||||
switch (this.mode) {
|
|
||||||
case GraphicMode.Fill:
|
|
||||||
ctx.fill(this.fillRule);
|
|
||||||
break;
|
|
||||||
case GraphicMode.Stroke:
|
|
||||||
ctx.stroke();
|
|
||||||
break;
|
|
||||||
case GraphicMode.FillAndStroke:
|
|
||||||
ctx.fill(this.fillRule);
|
|
||||||
ctx.stroke();
|
|
||||||
break;
|
|
||||||
case GraphicMode.StrokeAndFill:
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.fill(this.fillRule);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -347,6 +350,7 @@ export class Circle extends GraphicItemBase {
|
|||||||
setRadius(radius: number) {
|
setRadius(radius: number) {
|
||||||
this.radius = radius;
|
this.radius = radius;
|
||||||
this.size(radius * 2, radius * 2);
|
this.size(radius * 2, radius * 2);
|
||||||
|
this.pathDirty = true;
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,6 +362,7 @@ export class Circle extends GraphicItemBase {
|
|||||||
setAngle(start: number, end: number) {
|
setAngle(start: number, end: number) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
|
this.pathDirty = true;
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,6 +386,18 @@ export class Circle extends GraphicItemBase {
|
|||||||
if (!this.assertType(nextValue, 'number', key)) return;
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
this.setAngle(this.start, nextValue);
|
this.setAngle(this.start, nextValue);
|
||||||
return;
|
return;
|
||||||
|
case 'circle':
|
||||||
|
if (!this.assertType(nextValue, Array, key)) return;
|
||||||
|
if (!isNil(nextValue[0])) {
|
||||||
|
this.setRadius(nextValue[0] as number);
|
||||||
|
}
|
||||||
|
if (!isNil(nextValue[1]) && !isNil(nextValue[2])) {
|
||||||
|
this.setAngle(
|
||||||
|
nextValue[1] as number,
|
||||||
|
nextValue[2] as number
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
}
|
}
|
||||||
@ -391,40 +408,21 @@ export class Ellipse extends GraphicItemBase {
|
|||||||
radiusY: number = 10;
|
radiusY: number = 10;
|
||||||
start: number = 0;
|
start: number = 0;
|
||||||
end: number = Math.PI * 2;
|
end: number = Math.PI * 2;
|
||||||
|
anchorX: number = 0.5;
|
||||||
|
anchorY: number = 0.5;
|
||||||
|
|
||||||
protected render(
|
getPath(): Path2D {
|
||||||
canvas: MotaOffscreenCanvas2D,
|
const path = new Path2D();
|
||||||
_transform: Transform
|
path.ellipse(
|
||||||
): void {
|
this.radiusX,
|
||||||
const ctx = canvas.ctx;
|
this.radiusY,
|
||||||
this.setCanvasState(canvas);
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.ellipse(
|
|
||||||
this.x,
|
|
||||||
this.y,
|
|
||||||
this.radiusX,
|
this.radiusX,
|
||||||
this.radiusY,
|
this.radiusY,
|
||||||
0,
|
0,
|
||||||
this.start,
|
this.start,
|
||||||
this.end
|
this.end
|
||||||
);
|
);
|
||||||
|
return path;
|
||||||
switch (this.mode) {
|
|
||||||
case GraphicMode.Fill:
|
|
||||||
ctx.fill(this.fillRule);
|
|
||||||
break;
|
|
||||||
case GraphicMode.Stroke:
|
|
||||||
ctx.stroke();
|
|
||||||
break;
|
|
||||||
case GraphicMode.FillAndStroke:
|
|
||||||
ctx.fill(this.fillRule);
|
|
||||||
ctx.stroke();
|
|
||||||
break;
|
|
||||||
case GraphicMode.StrokeAndFill:
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.fill(this.fillRule);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -435,6 +433,8 @@ export class Ellipse extends GraphicItemBase {
|
|||||||
setRadius(x: number, y: number) {
|
setRadius(x: number, y: number) {
|
||||||
this.radiusX = x;
|
this.radiusX = x;
|
||||||
this.radiusY = y;
|
this.radiusY = y;
|
||||||
|
this.size(x, y);
|
||||||
|
this.pathDirty = true;
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,6 +446,7 @@ export class Ellipse extends GraphicItemBase {
|
|||||||
setAngle(start: number, end: number) {
|
setAngle(start: number, end: number) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
|
this.pathDirty = true;
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,6 +474,21 @@ export class Ellipse extends GraphicItemBase {
|
|||||||
if (!this.assertType(nextValue, 'number', key)) return;
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
this.setAngle(this.start, nextValue);
|
this.setAngle(this.start, nextValue);
|
||||||
return;
|
return;
|
||||||
|
case 'ellipse':
|
||||||
|
if (!this.assertType(nextValue, Array, key)) return;
|
||||||
|
if (!isNil(nextValue[0]) && !isNil(nextValue[1])) {
|
||||||
|
this.setRadius(
|
||||||
|
nextValue[0] as number,
|
||||||
|
nextValue[1] as number
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!isNil(nextValue[2]) && !isNil(nextValue[3])) {
|
||||||
|
this.setAngle(
|
||||||
|
nextValue[2] as number,
|
||||||
|
nextValue[3] as number
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
}
|
}
|
||||||
@ -483,17 +499,15 @@ export class Line extends GraphicItemBase {
|
|||||||
y1: number = 0;
|
y1: number = 0;
|
||||||
x2: number = 0;
|
x2: number = 0;
|
||||||
y2: number = 0;
|
y2: number = 0;
|
||||||
|
mode: GraphicMode = GraphicMode.Stroke;
|
||||||
|
|
||||||
protected render(
|
getPath(): Path2D {
|
||||||
canvas: MotaOffscreenCanvas2D,
|
const path = new Path2D();
|
||||||
_transform: Transform
|
const x = this.x;
|
||||||
): void {
|
const y = this.y;
|
||||||
const ctx = canvas.ctx;
|
path.moveTo(this.x1 - x, this.y1 - y);
|
||||||
this.setCanvasState(canvas);
|
path.lineTo(this.x2 - x, this.y2 - y);
|
||||||
ctx.beginPath();
|
return path;
|
||||||
ctx.moveTo(this.x1, this.y1);
|
|
||||||
ctx.lineTo(this.x2, this.y2);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -502,6 +516,7 @@ export class Line extends GraphicItemBase {
|
|||||||
setPoint1(x: number, y: number) {
|
setPoint1(x: number, y: number) {
|
||||||
this.x1 = x;
|
this.x1 = x;
|
||||||
this.y1 = y;
|
this.y1 = y;
|
||||||
|
this.fitRect();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,9 +526,20 @@ export class Line extends GraphicItemBase {
|
|||||||
setPoint2(x: number, y: number) {
|
setPoint2(x: number, y: number) {
|
||||||
this.x2 = x;
|
this.x2 = x;
|
||||||
this.y2 = y;
|
this.y2 = y;
|
||||||
|
this.fitRect();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fitRect() {
|
||||||
|
const left = Math.min(this.x1, this.x2);
|
||||||
|
const top = Math.min(this.y1, this.y2);
|
||||||
|
const right = Math.max(this.x1, this.x2);
|
||||||
|
const bottom = Math.max(this.y1, this.y2);
|
||||||
|
this.pos(left, top);
|
||||||
|
this.size(right - left, bottom - top);
|
||||||
|
this.pathDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
patchProp(
|
patchProp(
|
||||||
key: string,
|
key: string,
|
||||||
prevValue: any,
|
prevValue: any,
|
||||||
@ -538,6 +564,11 @@ export class Line extends GraphicItemBase {
|
|||||||
if (!this.assertType(nextValue, 'number', key)) return;
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
this.setPoint2(this.x2, nextValue);
|
this.setPoint2(this.x2, nextValue);
|
||||||
return;
|
return;
|
||||||
|
case 'line':
|
||||||
|
if (!this.assertType(nextValue as number[], Array, key)) return;
|
||||||
|
this.setPoint1(nextValue[0], nextValue[1]);
|
||||||
|
this.setPoint2(nextValue[2], nextValue[3]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
}
|
}
|
||||||
@ -552,24 +583,22 @@ export class BezierCurve extends GraphicItemBase {
|
|||||||
cp2y: number = 0;
|
cp2y: number = 0;
|
||||||
ex: number = 0;
|
ex: number = 0;
|
||||||
ey: number = 0;
|
ey: number = 0;
|
||||||
|
mode: GraphicMode = GraphicMode.Stroke;
|
||||||
|
|
||||||
protected render(
|
getPath(): Path2D {
|
||||||
canvas: MotaOffscreenCanvas2D,
|
const path = new Path2D();
|
||||||
_transform: Transform
|
const x = this.x;
|
||||||
): void {
|
const y = this.y;
|
||||||
const ctx = canvas.ctx;
|
path.moveTo(this.sx - x, this.sy - y);
|
||||||
this.setCanvasState(canvas);
|
path.bezierCurveTo(
|
||||||
ctx.beginPath();
|
this.cp1x - x,
|
||||||
ctx.moveTo(this.sx, this.sy);
|
this.cp1y - y,
|
||||||
ctx.bezierCurveTo(
|
this.cp2x - x,
|
||||||
this.cp1x,
|
this.cp2y - y,
|
||||||
this.cp1y,
|
this.ex - x,
|
||||||
this.cp2x,
|
this.ey - y
|
||||||
this.cp2y,
|
|
||||||
this.ex,
|
|
||||||
this.ey
|
|
||||||
);
|
);
|
||||||
ctx.stroke();
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -578,6 +607,7 @@ export class BezierCurve extends GraphicItemBase {
|
|||||||
setStart(x: number, y: number) {
|
setStart(x: number, y: number) {
|
||||||
this.sx = x;
|
this.sx = x;
|
||||||
this.sy = y;
|
this.sy = y;
|
||||||
|
this.fitRect();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,6 +617,7 @@ export class BezierCurve extends GraphicItemBase {
|
|||||||
setControl1(x: number, y: number) {
|
setControl1(x: number, y: number) {
|
||||||
this.cp1x = x;
|
this.cp1x = x;
|
||||||
this.cp1y = y;
|
this.cp1y = y;
|
||||||
|
this.fitRect();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,6 +627,7 @@ export class BezierCurve extends GraphicItemBase {
|
|||||||
setControl2(x: number, y: number) {
|
setControl2(x: number, y: number) {
|
||||||
this.cp2x = x;
|
this.cp2x = x;
|
||||||
this.cp2y = y;
|
this.cp2y = y;
|
||||||
|
this.fitRect();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,9 +637,20 @@ export class BezierCurve extends GraphicItemBase {
|
|||||||
setEnd(x: number, y: number) {
|
setEnd(x: number, y: number) {
|
||||||
this.ex = x;
|
this.ex = x;
|
||||||
this.ey = y;
|
this.ey = y;
|
||||||
|
this.fitRect();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fitRect() {
|
||||||
|
const left = Math.min(this.sx, this.cp1x, this.cp2x, this.ex);
|
||||||
|
const top = Math.min(this.sy, this.cp1y, this.cp2y, this.ey);
|
||||||
|
const right = Math.max(this.sx, this.cp1x, this.cp2x, this.ex);
|
||||||
|
const bottom = Math.max(this.sy, this.cp1y, this.cp2y, this.ey);
|
||||||
|
this.pos(left, top);
|
||||||
|
this.size(right - left, bottom - top);
|
||||||
|
this.pathDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
patchProp(
|
patchProp(
|
||||||
key: string,
|
key: string,
|
||||||
prevValue: any,
|
prevValue: any,
|
||||||
@ -648,6 +691,13 @@ export class BezierCurve extends GraphicItemBase {
|
|||||||
if (!this.assertType(nextValue, 'number', key)) return;
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
this.setEnd(this.ex, nextValue);
|
this.setEnd(this.ex, nextValue);
|
||||||
return;
|
return;
|
||||||
|
case 'curve':
|
||||||
|
if (!this.assertType(nextValue as number[], Array, key)) return;
|
||||||
|
this.setStart(nextValue[0], nextValue[1]);
|
||||||
|
this.setControl1(nextValue[2], nextValue[3]);
|
||||||
|
this.setControl2(nextValue[4], nextValue[5]);
|
||||||
|
this.setEnd(nextValue[6], nextValue[7]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
}
|
}
|
||||||
@ -660,6 +710,21 @@ export class QuadraticCurve extends GraphicItemBase {
|
|||||||
cpy: number = 0;
|
cpy: number = 0;
|
||||||
ex: number = 0;
|
ex: number = 0;
|
||||||
ey: number = 0;
|
ey: number = 0;
|
||||||
|
mode: GraphicMode = GraphicMode.Stroke;
|
||||||
|
|
||||||
|
getPath(): Path2D {
|
||||||
|
const path = new Path2D();
|
||||||
|
const x = this.x;
|
||||||
|
const y = this.y;
|
||||||
|
path.moveTo(this.sx - x, this.sy - y);
|
||||||
|
path.quadraticCurveTo(
|
||||||
|
this.cpx - x,
|
||||||
|
this.cpy - y,
|
||||||
|
this.ex - x,
|
||||||
|
this.ey - y
|
||||||
|
);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
protected render(
|
protected render(
|
||||||
canvas: MotaOffscreenCanvas2D,
|
canvas: MotaOffscreenCanvas2D,
|
||||||
@ -679,6 +744,7 @@ export class QuadraticCurve extends GraphicItemBase {
|
|||||||
setStart(x: number, y: number) {
|
setStart(x: number, y: number) {
|
||||||
this.sx = x;
|
this.sx = x;
|
||||||
this.sy = y;
|
this.sy = y;
|
||||||
|
this.fitRect();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -688,6 +754,7 @@ export class QuadraticCurve extends GraphicItemBase {
|
|||||||
setControl(x: number, y: number) {
|
setControl(x: number, y: number) {
|
||||||
this.cpx = x;
|
this.cpx = x;
|
||||||
this.cpy = y;
|
this.cpy = y;
|
||||||
|
this.fitRect();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,9 +764,20 @@ export class QuadraticCurve extends GraphicItemBase {
|
|||||||
setEnd(x: number, y: number) {
|
setEnd(x: number, y: number) {
|
||||||
this.ex = x;
|
this.ex = x;
|
||||||
this.ey = y;
|
this.ey = y;
|
||||||
|
this.fitRect();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fitRect() {
|
||||||
|
const left = Math.min(this.sx, this.cpx, this.ex);
|
||||||
|
const top = Math.min(this.sy, this.cpy, this.ey);
|
||||||
|
const right = Math.max(this.sx, this.cpx, this.ex);
|
||||||
|
const bottom = Math.max(this.sy, this.cpy, this.ey);
|
||||||
|
this.pos(left, top);
|
||||||
|
this.size(right - left, bottom - top);
|
||||||
|
this.pathDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
patchProp(
|
patchProp(
|
||||||
key: string,
|
key: string,
|
||||||
prevValue: any,
|
prevValue: any,
|
||||||
@ -732,6 +810,12 @@ export class QuadraticCurve extends GraphicItemBase {
|
|||||||
if (!this.assertType(nextValue, 'number', key)) return;
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
this.setEnd(this.ex, nextValue);
|
this.setEnd(this.ex, nextValue);
|
||||||
return;
|
return;
|
||||||
|
case 'curve':
|
||||||
|
if (!this.assertType(nextValue as number[], Array, key)) return;
|
||||||
|
this.setStart(nextValue[0], nextValue[1]);
|
||||||
|
this.setControl(nextValue[2], nextValue[3]);
|
||||||
|
this.setEnd(nextValue[4], nextValue[5]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
}
|
}
|
||||||
@ -741,30 +825,6 @@ export class Path extends GraphicItemBase {
|
|||||||
/** 路径 */
|
/** 路径 */
|
||||||
path: Path2D = new Path2D();
|
path: Path2D = new Path2D();
|
||||||
|
|
||||||
protected render(
|
|
||||||
canvas: MotaOffscreenCanvas2D,
|
|
||||||
_transform: Transform
|
|
||||||
): void {
|
|
||||||
const ctx = canvas.ctx;
|
|
||||||
this.setCanvasState(canvas);
|
|
||||||
switch (this.mode) {
|
|
||||||
case GraphicMode.Fill:
|
|
||||||
ctx.fill(this.path, this.fillRule);
|
|
||||||
break;
|
|
||||||
case GraphicMode.Stroke:
|
|
||||||
ctx.stroke(this.path);
|
|
||||||
break;
|
|
||||||
case GraphicMode.FillAndStroke:
|
|
||||||
ctx.fill(this.path, this.fillRule);
|
|
||||||
ctx.stroke(this.path);
|
|
||||||
break;
|
|
||||||
case GraphicMode.StrokeAndFill:
|
|
||||||
ctx.stroke(this.path);
|
|
||||||
ctx.fill(this.path, this.fillRule);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前路径
|
* 获取当前路径
|
||||||
*/
|
*/
|
||||||
@ -778,6 +838,7 @@ export class Path extends GraphicItemBase {
|
|||||||
*/
|
*/
|
||||||
addPath(path: Path2D) {
|
addPath(path: Path2D) {
|
||||||
this.path.addPath(path);
|
this.path.addPath(path);
|
||||||
|
this.pathDirty = true;
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -792,6 +853,7 @@ export class Path extends GraphicItemBase {
|
|||||||
case 'path':
|
case 'path':
|
||||||
if (!this.assertType(nextValue, Path2D, key)) return;
|
if (!this.assertType(nextValue, Path2D, key)) return;
|
||||||
this.path = nextValue;
|
this.path = nextValue;
|
||||||
|
this.pathDirty = true;
|
||||||
this.update();
|
this.update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -799,91 +861,157 @@ export class Path extends GraphicItemBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const enum RectRType {
|
export const enum RectRCorner {
|
||||||
/** 圆角为椭圆 */
|
TopLeft,
|
||||||
Ellipse,
|
TopRight,
|
||||||
/** 圆角为二次贝塞尔曲线 */
|
BottomRight,
|
||||||
Quad,
|
BottomLeft
|
||||||
/** 圆角为三次贝塞尔曲线。该模式下,包含两个控制点,一个控制点位于上下矩形边延长线,另一个控制点位于左右矩形边延长线 */
|
|
||||||
Cubic,
|
|
||||||
/** 圆角为直线连接 */
|
|
||||||
Line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RectRCircleParams = [
|
||||||
|
r1: number,
|
||||||
|
r2?: number,
|
||||||
|
r3?: number,
|
||||||
|
r4?: number
|
||||||
|
];
|
||||||
|
export type RectREllipseParams = [
|
||||||
|
rx1: number,
|
||||||
|
ry1: number,
|
||||||
|
rx2?: number,
|
||||||
|
ry2?: number,
|
||||||
|
rx3?: number,
|
||||||
|
ry3?: number,
|
||||||
|
rx4?: number,
|
||||||
|
ry4?: number
|
||||||
|
];
|
||||||
|
|
||||||
export class RectR extends GraphicItemBase {
|
export class RectR extends GraphicItemBase {
|
||||||
/** 矩形路径 */
|
/** 圆角属性,四元素数组,每个元素是一个二元素数组,表示这个角的半径,顺序为 左上,右上,右下,左下 */
|
||||||
private path: Path2D;
|
readonly corner: [radiusX: number, radiusY: number][] = [
|
||||||
|
[0, 0],
|
||||||
/** 圆角类型 */
|
[0, 0],
|
||||||
roundType: RectRType = RectRType.Ellipse;
|
[0, 0],
|
||||||
/** 横向圆角半径 */
|
[0, 0]
|
||||||
radiusX: number = 0;
|
];
|
||||||
/** 纵向圆角半径 */
|
|
||||||
radiusY: number = 0;
|
|
||||||
/**
|
|
||||||
* 二次贝塞尔曲线下,表示控制点的横向比例,控制点在上下矩形边与圆角的交界处为0,在左右矩形边与圆角的交界处延长线为1
|
|
||||||
* 三次贝塞尔曲线下,表示在上下矩形边延长线上的控制点的比例
|
|
||||||
*/
|
|
||||||
cpx: number = 0;
|
|
||||||
/**
|
|
||||||
* 二次贝塞尔曲线下,表示控制点的纵向比例,控制点在左右矩形边与圆角的交界处为0,在上下矩形边与圆角的交界处延长线为1
|
|
||||||
* 三次贝塞尔曲线下,表示在左右矩形边延长线上的控制点的比例
|
|
||||||
*/
|
|
||||||
cpy: number = 0;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
type: RenderItemPosition,
|
|
||||||
cache: boolean = false,
|
|
||||||
fall: boolean = false
|
|
||||||
) {
|
|
||||||
super(type, cache, fall);
|
|
||||||
|
|
||||||
|
getPath(): Path2D {
|
||||||
const path = new Path2D();
|
const path = new Path2D();
|
||||||
path.rect(this.x, this.y, this.width, this.height);
|
const { width: w, height: h } = this;
|
||||||
this.path = path;
|
const [[xtl, ytl], [xtr, ytr], [xbr, ybr], [xbl, ybl]] = this.corner;
|
||||||
|
// 左上圆角终点
|
||||||
|
path.moveTo(xtl, 0);
|
||||||
|
// 右上圆角起点
|
||||||
|
path.lineTo(w - xtr, 0);
|
||||||
|
// 右上圆角终点
|
||||||
|
path.ellipse(w - xtr, ytr, xtr, ytr, 0, -Math.PI / 2, 0);
|
||||||
|
// 右下圆角起点
|
||||||
|
path.lineTo(w, h - ybr);
|
||||||
|
// 右下圆角终点
|
||||||
|
path.ellipse(w - xbr, h - ybr, xbr, ybr, 0, 0, Math.PI / 2);
|
||||||
|
// 左下圆角起点
|
||||||
|
path.lineTo(xbl, h);
|
||||||
|
// 左下圆角终点
|
||||||
|
path.ellipse(xbl, h - ybl, xbl, ybl, 0, Math.PI / 2, Math.PI);
|
||||||
|
// 左上圆角起点
|
||||||
|
path.lineTo(0, ytl);
|
||||||
|
// 左上圆角终点
|
||||||
|
path.ellipse(xtl, ytl, xtl, ytl, 0, Math.PI, -Math.PI / 2);
|
||||||
|
path.closePath();
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新路径
|
|
||||||
*/
|
|
||||||
private updatePath() {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置圆角半径
|
* 设置圆角半径
|
||||||
* @param x 横向半径
|
* @param x 横向半径
|
||||||
* @param y 纵向半径
|
* @param y 纵向半径
|
||||||
*/
|
*/
|
||||||
setRadius(x: number, y: number) {}
|
setRadius(x: number, y: number, corner: RectRCorner) {
|
||||||
|
const hw = this.width / 2;
|
||||||
|
const hh = this.height / 2;
|
||||||
|
this.corner[corner] = [clamp(x, 0, hw), clamp(y, 0, hh)];
|
||||||
|
this.pathDirty = true;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置贝塞尔曲线模式下的控制点
|
* 设置圆形圆角参数
|
||||||
* @param x cpx
|
* @param circle 圆形圆角参数
|
||||||
* @param y cpy
|
|
||||||
*/
|
*/
|
||||||
setControl(x: number, y: number) {}
|
setCircle(circle: RectRCircleParams) {
|
||||||
|
const [r1, r2 = 0, r3 = 0, r4 = 0] = circle;
|
||||||
|
switch (circle.length) {
|
||||||
|
case 1: {
|
||||||
|
this.setRadius(r1, r1, RectRCorner.BottomLeft);
|
||||||
|
this.setRadius(r1, r1, RectRCorner.BottomRight);
|
||||||
|
this.setRadius(r1, r1, RectRCorner.TopLeft);
|
||||||
|
this.setRadius(r1, r1, RectRCorner.TopRight);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
this.setRadius(r1, r1, RectRCorner.TopLeft);
|
||||||
|
this.setRadius(r1, r1, RectRCorner.BottomRight);
|
||||||
|
this.setRadius(r2, r2, RectRCorner.BottomLeft);
|
||||||
|
this.setRadius(r2, r2, RectRCorner.TopRight);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this.setRadius(r1, r1, RectRCorner.TopLeft);
|
||||||
|
this.setRadius(r2, r2, RectRCorner.TopRight);
|
||||||
|
this.setRadius(r2, r2, RectRCorner.BottomLeft);
|
||||||
|
this.setRadius(r3, r3, RectRCorner.BottomRight);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this.setRadius(r1, r1, RectRCorner.TopLeft);
|
||||||
|
this.setRadius(r2, r2, RectRCorner.TopRight);
|
||||||
|
this.setRadius(r3, r3, RectRCorner.BottomRight);
|
||||||
|
this.setRadius(r4, r4, RectRCorner.BottomLeft);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected render(
|
/**
|
||||||
canvas: MotaOffscreenCanvas2D,
|
* 设置椭圆圆角参数
|
||||||
_transform: Transform
|
* @param ellipse 椭圆圆角参数
|
||||||
): void {
|
*/
|
||||||
const ctx = canvas.ctx;
|
setEllipse(ellipse: RectREllipseParams) {
|
||||||
this.setCanvasState(canvas);
|
const [rx1, ry1, rx2 = 0, ry2 = 0, rx3 = 0, ry3 = 0, rx4 = 0, ry4 = 0] =
|
||||||
switch (this.mode) {
|
ellipse;
|
||||||
case GraphicMode.Fill:
|
|
||||||
ctx.fill(this.path, this.fillRule);
|
switch (ellipse.length) {
|
||||||
|
case 2: {
|
||||||
|
this.setRadius(rx1, ry1, RectRCorner.BottomLeft);
|
||||||
|
this.setRadius(rx1, ry1, RectRCorner.BottomRight);
|
||||||
|
this.setRadius(rx1, ry1, RectRCorner.TopLeft);
|
||||||
|
this.setRadius(rx1, ry1, RectRCorner.TopRight);
|
||||||
break;
|
break;
|
||||||
case GraphicMode.Stroke:
|
}
|
||||||
ctx.stroke(this.path);
|
case 4: {
|
||||||
|
this.setRadius(rx1, ry1, RectRCorner.TopLeft);
|
||||||
|
this.setRadius(rx1, ry1, RectRCorner.BottomRight);
|
||||||
|
this.setRadius(rx2, ry2, RectRCorner.BottomLeft);
|
||||||
|
this.setRadius(rx2, ry2, RectRCorner.TopRight);
|
||||||
break;
|
break;
|
||||||
case GraphicMode.FillAndStroke:
|
}
|
||||||
ctx.fill(this.path, this.fillRule);
|
case 6: {
|
||||||
ctx.stroke(this.path);
|
this.setRadius(rx1, ry1, RectRCorner.TopLeft);
|
||||||
|
this.setRadius(rx2, ry2, RectRCorner.TopRight);
|
||||||
|
this.setRadius(rx2, ry2, RectRCorner.BottomLeft);
|
||||||
|
this.setRadius(rx3, ry3, RectRCorner.BottomRight);
|
||||||
break;
|
break;
|
||||||
case GraphicMode.StrokeAndFill:
|
}
|
||||||
ctx.stroke(this.path);
|
case 8: {
|
||||||
ctx.fill(this.path, this.fillRule);
|
this.setRadius(rx1, ry1, RectRCorner.TopLeft);
|
||||||
|
this.setRadius(rx2, ry2, RectRCorner.TopRight);
|
||||||
|
this.setRadius(rx3, ry3, RectRCorner.BottomRight);
|
||||||
|
this.setRadius(rx4, ry4, RectRCorner.BottomLeft);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default: {
|
||||||
|
logger.warn(58, ellipse.length.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
patchProp(
|
patchProp(
|
||||||
@ -894,6 +1022,18 @@ export class RectR extends GraphicItemBase {
|
|||||||
parentComponent?: ComponentInternalInstance | null
|
parentComponent?: ComponentInternalInstance | null
|
||||||
): void {
|
): void {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
|
case 'circle': {
|
||||||
|
const value = nextValue as RectRCircleParams;
|
||||||
|
if (!this.assertType(value, Array, key)) return;
|
||||||
|
this.setCircle(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'ellipse': {
|
||||||
|
const value = nextValue as RectREllipseParams;
|
||||||
|
if (!this.assertType(value, Array, key)) return;
|
||||||
|
this.setEllipse(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
}
|
}
|
||||||
|
@ -197,11 +197,17 @@ export class Image extends RenderItem<EImageEvent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Comment extends RenderItem {
|
export class Comment extends RenderItem {
|
||||||
|
readonly isComment: boolean = true;
|
||||||
|
|
||||||
constructor(public text: string = '') {
|
constructor(public text: string = '') {
|
||||||
super('static');
|
super('static', false, false);
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBoundingRect(): DOMRectReadOnly {
|
||||||
|
return new DOMRectReadOnly(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
protected render(
|
protected render(
|
||||||
_canvas: MotaOffscreenCanvas2D,
|
_canvas: MotaOffscreenCanvas2D,
|
||||||
_transform: Transform
|
_transform: Transform
|
||||||
|
@ -116,7 +116,7 @@ const enum ElementState {
|
|||||||
/**
|
/**
|
||||||
* standardElementFor
|
* standardElementFor
|
||||||
*/
|
*/
|
||||||
const se = (
|
const _se = (
|
||||||
Item: new (
|
Item: new (
|
||||||
type: RenderItemPosition,
|
type: RenderItemPosition,
|
||||||
cache?: boolean,
|
cache?: boolean,
|
||||||
@ -235,14 +235,14 @@ tagMap.register<EDamageEvent, Damage>('damage', (_0, _1, _props) => {
|
|||||||
tagMap.register('animation', (_0, _1, _props) => {
|
tagMap.register('animation', (_0, _1, _props) => {
|
||||||
return new Animate();
|
return new Animate();
|
||||||
});
|
});
|
||||||
tagMap.register('g-rect', se(Rect, 'absolute', ElementState.None));
|
tagMap.register('g-rect', standardElementNoCache(Rect));
|
||||||
tagMap.register('g-circle', se(Circle, 'absolute', ElementState.None));
|
tagMap.register('g-circle', standardElementNoCache(Circle));
|
||||||
tagMap.register('g-ellipse', se(Ellipse, 'absolute', ElementState.None));
|
tagMap.register('g-ellipse', standardElementNoCache(Ellipse));
|
||||||
tagMap.register('g-line', se(Line, 'absolute', ElementState.None));
|
tagMap.register('g-line', standardElementNoCache(Line));
|
||||||
tagMap.register('g-bezier', se(BezierCurve, 'absolute', ElementState.None));
|
tagMap.register('g-bezier', standardElementNoCache(BezierCurve));
|
||||||
tagMap.register('g-quad', se(QuadraticCurve, 'absolute', ElementState.None));
|
tagMap.register('g-quad', standardElementNoCache(QuadraticCurve));
|
||||||
tagMap.register('g-path', se(Path, 'absolute', ElementState.None));
|
tagMap.register('g-path', standardElementNoCache(Path));
|
||||||
tagMap.register('g-rectr', se(RectR, 'absolute', ElementState.None));
|
tagMap.register('g-rectr', standardElementNoCache(RectR));
|
||||||
tagMap.register('icon', standardElementNoCache(Icon));
|
tagMap.register('icon', standardElementNoCache(Icon));
|
||||||
tagMap.register('winskin', (_0, _1, props) => {
|
tagMap.register('winskin', (_0, _1, props) => {
|
||||||
if (!props) return new Winskin(core.material.images.images['winskin.png']);
|
if (!props) return new Winskin(core.material.images.images['winskin.png']);
|
||||||
|
@ -6,8 +6,12 @@ import {
|
|||||||
ILayerRenderExtends
|
ILayerRenderExtends
|
||||||
} from '../preset/layer';
|
} from '../preset/layer';
|
||||||
import type { EnemyCollection } from '@/game/enemy/damage';
|
import type { EnemyCollection } from '@/game/enemy/damage';
|
||||||
import { ILineProperty } from '../preset/graphics';
|
import {
|
||||||
import { ElementAnchor, ElementLocator } from '../utils';
|
ILineProperty,
|
||||||
|
RectRCircleParams,
|
||||||
|
RectREllipseParams
|
||||||
|
} from '../preset/graphics';
|
||||||
|
import { ElementAnchor, ElementLocator, ElementScale } from '../utils';
|
||||||
import { CustomContainerRenderFn } from '../container';
|
import { CustomContainerRenderFn } from '../container';
|
||||||
|
|
||||||
export interface CustomProps {
|
export interface CustomProps {
|
||||||
@ -45,6 +49,10 @@ export interface BaseProps {
|
|||||||
loc?: ElementLocator;
|
loc?: ElementLocator;
|
||||||
/** 锚点属性,可以填 `[x锚点,y锚点]`,是 anchorX, anchorY 的简写属性 */
|
/** 锚点属性,可以填 `[x锚点,y锚点]`,是 anchorX, anchorY 的简写属性 */
|
||||||
anc?: ElementAnchor;
|
anc?: ElementAnchor;
|
||||||
|
/** 放缩属性,可以填 `[x比例,y比例]`,是 transform 的简写属性之一 */
|
||||||
|
scale?: ElementScale;
|
||||||
|
/** 旋转属性,单位弧度,是 transform 的简写属性之一 */
|
||||||
|
rotate?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SpriteProps extends BaseProps {
|
export interface SpriteProps extends BaseProps {
|
||||||
@ -124,12 +132,44 @@ export interface GraphicPropsBase extends BaseProps, Partial<ILineProperty> {
|
|||||||
strokeStyle?: CanvasStyle;
|
strokeStyle?: CanvasStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CircleParams = [radius?: number, start?: number, end?: number];
|
||||||
|
export type EllipseParams = [
|
||||||
|
radiusX?: number,
|
||||||
|
radiusY?: number,
|
||||||
|
start?: number,
|
||||||
|
end?: number
|
||||||
|
];
|
||||||
|
export type LineParams = [x1: number, y1: number, x2: number, y2: number];
|
||||||
|
export type BezierParams = [
|
||||||
|
sx: number,
|
||||||
|
sy: number,
|
||||||
|
cp1x: number,
|
||||||
|
cp1y: number,
|
||||||
|
cp2x: number,
|
||||||
|
cp2y: number,
|
||||||
|
ex: number,
|
||||||
|
ey: number
|
||||||
|
];
|
||||||
|
export type QuadParams = [
|
||||||
|
sx: number,
|
||||||
|
sy: number,
|
||||||
|
cpx: number,
|
||||||
|
cpy: number,
|
||||||
|
ex: number,
|
||||||
|
ey: number
|
||||||
|
];
|
||||||
|
|
||||||
export interface RectProps extends GraphicPropsBase {}
|
export interface RectProps extends GraphicPropsBase {}
|
||||||
|
|
||||||
export interface CirclesProps extends GraphicPropsBase {
|
export interface CirclesProps extends GraphicPropsBase {
|
||||||
radius?: number;
|
radius?: number;
|
||||||
start?: number;
|
start?: number;
|
||||||
end?: number;
|
end?: number;
|
||||||
|
/**
|
||||||
|
* 圆属性参数,可以填 `[半径,起始角度,终止角度]`,是 radius, start, end 的简写,
|
||||||
|
* 其中半径可选,后两项要么都填,要么都不填
|
||||||
|
*/
|
||||||
|
circle?: CircleParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EllipseProps extends GraphicPropsBase {
|
export interface EllipseProps extends GraphicPropsBase {
|
||||||
@ -137,6 +177,11 @@ export interface EllipseProps extends GraphicPropsBase {
|
|||||||
radiusY?: number;
|
radiusY?: number;
|
||||||
start?: number;
|
start?: number;
|
||||||
end?: number;
|
end?: number;
|
||||||
|
/**
|
||||||
|
* 椭圆属性参数,可以填 `[x半径,y半径,起始角度,终止角度]`,是 radiusX, radiusY, start, end 的简写,
|
||||||
|
* 其中前两项和后两项要么都填,要么都不填
|
||||||
|
*/
|
||||||
|
ellipse?: EllipseParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LineProps extends GraphicPropsBase {
|
export interface LineProps extends GraphicPropsBase {
|
||||||
@ -144,6 +189,8 @@ export interface LineProps extends GraphicPropsBase {
|
|||||||
y1?: number;
|
y1?: number;
|
||||||
x2?: number;
|
x2?: number;
|
||||||
y2?: number;
|
y2?: number;
|
||||||
|
/** 直线属性参数,可以填 `[x1, y1, x2, y2]`,都是必填 */
|
||||||
|
line?: LineParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BezierProps extends GraphicPropsBase {
|
export interface BezierProps extends GraphicPropsBase {
|
||||||
@ -155,6 +202,8 @@ export interface BezierProps extends GraphicPropsBase {
|
|||||||
cp2y?: number;
|
cp2y?: number;
|
||||||
ex?: number;
|
ex?: number;
|
||||||
ey?: number;
|
ey?: number;
|
||||||
|
/** 三次贝塞尔曲线参数,可以填 `[sx, sy, cp1x, cp1y, cp2x, cp2y, ex, ey]`,都是必填 */
|
||||||
|
curve?: BezierParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QuadraticProps extends GraphicPropsBase {
|
export interface QuadraticProps extends GraphicPropsBase {
|
||||||
@ -164,6 +213,8 @@ export interface QuadraticProps extends GraphicPropsBase {
|
|||||||
cpy?: number;
|
cpy?: number;
|
||||||
ex?: number;
|
ex?: number;
|
||||||
ey?: number;
|
ey?: number;
|
||||||
|
/** 二次贝塞尔曲线参数,可以填 `[sx, sy, cpx, cpy, ex, ey]`,都是必填 */
|
||||||
|
curve?: QuadParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PathProps extends GraphicPropsBase {
|
export interface PathProps extends GraphicPropsBase {
|
||||||
@ -171,26 +222,23 @@ export interface PathProps extends GraphicPropsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RectRProps extends GraphicPropsBase {
|
export interface RectRProps extends GraphicPropsBase {
|
||||||
/** 圆角半径,此参数传入时,radiusX 和 radiusY 应保持一致 */
|
/**
|
||||||
radius: number;
|
* 圆形圆角参数,可以填 `[r1, r2, r3, r4]`,后三项可选。填写不同数量下的表现:
|
||||||
/** 圆角横向半径 */
|
* - 1个:每个角都是 `r1` 半径的圆
|
||||||
radiusX?: number;
|
* - 2个:左上和右下是 `r1` 半径的圆,右上和左下是 `r2` 半径的圆
|
||||||
/** 圆角纵向半径 */
|
* - 3个:左上是 `r1` 半径的圆,右上和左下是 `r2` 半径的圆,右下是 `r3` 半径的圆
|
||||||
radiusY?: number;
|
* - 4个:左上、右上、左下、右下 分别是 `r1, r2, r3, r4` 半径的圆
|
||||||
/** 圆角为线模式 */
|
*/
|
||||||
line?: boolean;
|
circle?: RectRCircleParams;
|
||||||
/** 圆角为椭圆模式,默认值 */
|
/**
|
||||||
ellipse?: boolean;
|
* 圆形圆角参数,可以填 `[rx1, ry1, rx2, ry2, rx3, ry3, rx4, ry4]`,
|
||||||
/** 圆角为二次贝塞尔曲线模式 */
|
* 两两一组,后三组可选,填写不同数量下的表现:
|
||||||
quad?: boolean;
|
* - 1组:每个角都是 `[rx1, ry1]` 半径的椭圆
|
||||||
/** 圆角为三次贝塞尔曲线模式 */
|
* - 2组:左上和右下是 `[rx1, ry1]` 半径的椭圆,右上和左下是 `[rx2, ry2]` 半径的椭圆
|
||||||
cubic?: boolean;
|
* - 3组:左上是 `[rx1, ry1]` 半径的椭圆,右上和左下是 `[rx2, ey2]` 半径的椭圆,右下是 `[rx3, ry3]` 半径的椭圆
|
||||||
/** 控制点,此参数传入时,cpx 和 cpy 应保持一致 */
|
* - 4组:左上、右上、左下、右下 分别是 `[rx1, ry1], [rx2, ry2], [rx3, ry3], [rx4, ry4]` 半径的椭圆
|
||||||
cp?: number;
|
*/
|
||||||
/** 横向控制点 */
|
ellipse?: RectREllipseParams;
|
||||||
cpx?: number;
|
|
||||||
/** 纵向控制点 */
|
|
||||||
cpy?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IconProps extends BaseProps {
|
export interface IconProps extends BaseProps {
|
||||||
|
@ -29,6 +29,7 @@ export type ElementLocator = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export type ElementAnchor = [x: number, y: number];
|
export type ElementAnchor = [x: number, y: number];
|
||||||
|
export type ElementScale = [x: number, y: number];
|
||||||
|
|
||||||
export function disableViewport() {
|
export function disableViewport() {
|
||||||
const adapter = RenderAdapter.get<FloorViewport>('viewport');
|
const adapter = RenderAdapter.get<FloorViewport>('viewport');
|
||||||
|
@ -96,6 +96,11 @@ export class UIController<C extends UIComponent = UIComponent>
|
|||||||
() => this.userShowBack.value && this.sysShowBack.value
|
() => this.userShowBack.value && this.sysShowBack.value
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** 当前是否显示 UI */
|
||||||
|
get active() {
|
||||||
|
return this.showBack.value;
|
||||||
|
}
|
||||||
|
|
||||||
/** 自定义显示模式下的配置信息 */
|
/** 自定义显示模式下的配置信息 */
|
||||||
private config?: IUICustomConfig<C>;
|
private config?: IUICustomConfig<C>;
|
||||||
/** 是否维持背景 UI */
|
/** 是否维持背景 UI */
|
||||||
|
@ -89,6 +89,7 @@
|
|||||||
"55": "Unchildable tag '$1' should follow with param.",
|
"55": "Unchildable tag '$1' should follow with param.",
|
||||||
"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",
|
||||||
"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."
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,7 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
|
|||||||
scrollTarget =
|
scrollTarget =
|
||||||
(height.value - scrollLength) * (contentTarget / max);
|
(height.value - scrollLength) * (contentTarget / max);
|
||||||
}
|
}
|
||||||
|
if (isNaN(scrollTarget)) scrollTarget = 0;
|
||||||
transition.time(time).transition('scroll', scrollTarget);
|
transition.time(time).transition('scroll', scrollTarget);
|
||||||
transition.time(time).transition('showScroll', target);
|
transition.time(time).transition('showScroll', target);
|
||||||
};
|
};
|
||||||
@ -200,6 +201,7 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
|
|||||||
const onTransform = (item: RenderItem) => {
|
const onTransform = (item: RenderItem) => {
|
||||||
const rect = item.getBoundingRect();
|
const rect = item.getBoundingRect();
|
||||||
const pad = props.pad ?? 0;
|
const pad = props.pad ?? 0;
|
||||||
|
if (item.parent === content.value) {
|
||||||
if (direction.value === ScrollDirection.Horizontal) {
|
if (direction.value === ScrollDirection.Horizontal) {
|
||||||
if (rect.right > maxLength - pad) {
|
if (rect.right > maxLength - pad) {
|
||||||
maxLength = rect.right + pad;
|
maxLength = rect.right + pad;
|
||||||
@ -211,6 +213,10 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
|
|||||||
updatePosition();
|
updatePosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
item.off('transform', onTransform);
|
||||||
|
listenedChild.delete(item);
|
||||||
|
}
|
||||||
getArea(item, rect);
|
getArea(item, rect);
|
||||||
checkItem(item);
|
checkItem(item);
|
||||||
scroll.value?.update();
|
scroll.value?.update();
|
||||||
@ -222,9 +228,10 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
|
|||||||
*/
|
*/
|
||||||
const updatePosition = () => {
|
const updatePosition = () => {
|
||||||
if (direction.value === ScrollDirection.Horizontal) {
|
if (direction.value === ScrollDirection.Horizontal) {
|
||||||
scrollLength = Math.max(
|
scrollLength = clamp(
|
||||||
|
(height.value / maxLength) * width.value,
|
||||||
SCROLL_MIN_LENGTH,
|
SCROLL_MIN_LENGTH,
|
||||||
(width.value / maxLength) * width.value
|
width.value
|
||||||
);
|
);
|
||||||
const h = props.noscroll
|
const h = props.noscroll
|
||||||
? height.value
|
? height.value
|
||||||
@ -234,7 +241,7 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
|
|||||||
scrollLength = clamp(
|
scrollLength = clamp(
|
||||||
(height.value / maxLength) * height.value,
|
(height.value / maxLength) * height.value,
|
||||||
SCROLL_MIN_LENGTH,
|
SCROLL_MIN_LENGTH,
|
||||||
height.value - 10
|
height.value
|
||||||
);
|
);
|
||||||
const w = props.noscroll
|
const w = props.noscroll
|
||||||
? width.value
|
? width.value
|
||||||
@ -255,6 +262,7 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
|
|||||||
listenedChild.clear();
|
listenedChild.clear();
|
||||||
areaMap.clear();
|
areaMap.clear();
|
||||||
content.value.children.forEach(v => {
|
content.value.children.forEach(v => {
|
||||||
|
if (v.isComment) return;
|
||||||
const rect = v.getBoundingRect();
|
const rect = v.getBoundingRect();
|
||||||
if (direction.value === ScrollDirection.Horizontal) {
|
if (direction.value === ScrollDirection.Horizontal) {
|
||||||
if (rect.right > max) {
|
if (rect.right > max) {
|
||||||
@ -266,11 +274,13 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
getArea(v, rect);
|
getArea(v, rect);
|
||||||
|
if (!listenedChild.has(v)) {
|
||||||
v.on('transform', onTransform);
|
v.on('transform', onTransform);
|
||||||
listenedChild.add(v);
|
listenedChild.add(v);
|
||||||
|
}
|
||||||
checkItem(v);
|
checkItem(v);
|
||||||
});
|
});
|
||||||
maxLength = max + (props.pad ?? 0);
|
maxLength = Math.max(max + (props.pad ?? 0), 10);
|
||||||
updatePosition();
|
updatePosition();
|
||||||
scroll.value?.update();
|
scroll.value?.update();
|
||||||
};
|
};
|
||||||
@ -491,6 +501,7 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
|
|||||||
</container-custom>
|
</container-custom>
|
||||||
<sprite
|
<sprite
|
||||||
nocache
|
nocache
|
||||||
|
hidden={props.noscroll}
|
||||||
loc={sp.value}
|
loc={sp.value}
|
||||||
ref={scroll}
|
ref={scroll}
|
||||||
render={drawScroll}
|
render={drawScroll}
|
||||||
|
@ -147,8 +147,13 @@ const MainScene = defineComponent(() => {
|
|||||||
status={rightStatus}
|
status={rightStatus}
|
||||||
></RightStatusBar>
|
></RightStatusBar>
|
||||||
)}
|
)}
|
||||||
|
<container
|
||||||
|
loc={[0, 0, MAIN_WIDTH, MAIN_HEIGHT]}
|
||||||
|
hidden={mainUIController.showBack.value}
|
||||||
|
>
|
||||||
{mainUIController.render()}
|
{mainUIController.render()}
|
||||||
</container>
|
</container>
|
||||||
|
</container>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -169,12 +169,7 @@ export const RightStatusBar = defineComponent<StatusBarProps<IRightHeroStatus>>(
|
|||||||
return (
|
return (
|
||||||
<container loc={p.loc}>
|
<container loc={p.loc}>
|
||||||
<g-rect loc={[0, 0, p.loc[2], p.loc[3]]} stroke></g-rect>
|
<g-rect loc={[0, 0, p.loc[2], p.loc[3]]} stroke></g-rect>
|
||||||
<Scroll loc={[0, 100, 180, 100]} hor>
|
<Scroll loc={[0, 0, 180, 100]}></Scroll>
|
||||||
<text text="测试1" loc={[0, 0]}></text>
|
|
||||||
<text text="测试2" loc={[50, 0]}></text>
|
|
||||||
<text text="测试3" loc={[100, 0]}></text>
|
|
||||||
<text text="测试4" loc={[200, 0]}></text>
|
|
||||||
</Scroll>
|
|
||||||
</container>
|
</container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user