@vvfx/artis 0.0.1-alpha.1 → 0.0.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artis/operations/operations.d.ts +15 -10
- package/dist/artis/types.d.ts +2 -2
- package/dist/core/command/commands/{picture-cut-commit-command.d.ts → image-cut-commit-command.d.ts} +4 -4
- package/dist/core/command/commands/{picture-expand-commit-command.d.ts → image-expand-commit-command.d.ts} +5 -5
- package/dist/core/command/commands/index.d.ts +4 -4
- package/dist/core/command/commands/item-replace-source-command.d.ts +5 -0
- package/dist/core/command/commands/item-set-font-family-command.d.ts +1 -0
- package/dist/core/command/commands/item-transform-command.d.ts +1 -0
- package/dist/core/command/commands/item-update-property-by-schema-command.d.ts +1 -0
- package/dist/core/gizmo/gizmo-command-binder.d.ts +3 -3
- package/dist/core/gizmo/gizmo-registry.d.ts +2 -2
- package/dist/index.cjs +1599 -1495
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1596 -1492
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Description: 智能画布
|
|
4
4
|
* Author: Ant Group CO., Ltd.
|
|
5
5
|
* Contributors: 赤芍,何即,不择,意绮
|
|
6
|
-
* Version: v0.0.1-alpha.
|
|
6
|
+
* Version: v0.0.1-alpha.2
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
@@ -71094,6 +71094,29 @@ const textareaDefaultCssClass = `
|
|
|
71094
71094
|
return this._editingItem !== undefined;
|
|
71095
71095
|
}
|
|
71096
71096
|
/**
|
|
71097
|
+
* 选中当前编辑文本的全部内容,并将焦点交给输入框。
|
|
71098
|
+
*
|
|
71099
|
+
* 输入框尚未完成首次画布投影时会先保存全文选区,待投影打开后恢复。
|
|
71100
|
+
* @returns 当前存在可编辑文本时返回 true
|
|
71101
|
+
*/ selectAllText() {
|
|
71102
|
+
if (this._disposed) {
|
|
71103
|
+
return false;
|
|
71104
|
+
}
|
|
71105
|
+
const textComponent = this._editingItem?.getComponent(TextComponent);
|
|
71106
|
+
if (!textComponent) {
|
|
71107
|
+
return false;
|
|
71108
|
+
}
|
|
71109
|
+
const textLength = this.textAreaElement.style.display === 'block' ? this.textAreaElement.value.length : (textComponent.text ?? '').length;
|
|
71110
|
+
this._selectionStart = 0;
|
|
71111
|
+
this._selectionEnd = textLength;
|
|
71112
|
+
if (this.textAreaElement.style.display === 'block') {
|
|
71113
|
+
this.textAreaElement.focus();
|
|
71114
|
+
this.textAreaElement.select();
|
|
71115
|
+
this.textAreaElement.scrollTop = 0;
|
|
71116
|
+
}
|
|
71117
|
+
return true;
|
|
71118
|
+
}
|
|
71119
|
+
/**
|
|
71097
71120
|
* 使用当前单选文本元素开始编辑。
|
|
71098
71121
|
* @param textareaType 文本框激活方式
|
|
71099
71122
|
* @returns 是否成功开始编辑
|
|
@@ -71532,11 +71555,11 @@ const textareaDefaultCssClass = `
|
|
|
71532
71555
|
/** 角点旋转工具。 */ GizmoType["CORNER_ROTATION"] = "corner-rotation";
|
|
71533
71556
|
/** 选区缩放工具。 */ GizmoType["RESIZE_SELECTION"] = "resize-selection";
|
|
71534
71557
|
/** 视口覆盖层。 */ GizmoType["VIEWPORT_OVERLAY"] = "viewport-overlay";
|
|
71535
|
-
/** 图片裁切工具。 */ GizmoType["
|
|
71558
|
+
/** 图片裁切工具。 */ GizmoType["IMAGE_CUT"] = "image-cut";
|
|
71536
71559
|
/** 文本编辑工具。 */ GizmoType["TEXT"] = "text";
|
|
71537
71560
|
/** 蒙版编辑工具。 */ GizmoType["MASK"] = "mask";
|
|
71538
71561
|
/** 加载状态工具。 */ GizmoType["LOADING"] = "loading";
|
|
71539
|
-
/** 图片扩边工具。 */ GizmoType["
|
|
71562
|
+
/** 图片扩边工具。 */ GizmoType["IMAGE_EXPAND"] = "image-expand";
|
|
71540
71563
|
/** 精准文字编辑工具。 */ GizmoType["SPRITE_TEXT_EDIT"] = "sprite-text-edit";
|
|
71541
71564
|
/** 媒体图标工具。 */ GizmoType["ICON"] = "icon";
|
|
71542
71565
|
/** 元素创建工具。 */ GizmoType["ITEM_CREATE"] = "item-create";
|
|
@@ -71680,95 +71703,311 @@ class GizmoViewportUtils {
|
|
|
71680
71703
|
defaults: freezeConfigValue(cloneConfigValue(definition.defaults))
|
|
71681
71704
|
});
|
|
71682
71705
|
}
|
|
71683
|
-
|
|
71684
|
-
|
|
71685
|
-
|
|
71686
|
-
|
|
71687
|
-
|
|
71688
|
-
|
|
71689
|
-
|
|
71690
|
-
|
|
71691
|
-
|
|
71692
|
-
|
|
71693
|
-
|
|
71694
|
-
|
|
71695
|
-
|
|
71696
|
-
|
|
71697
|
-
|
|
71698
|
-
|
|
71699
|
-
|
|
71700
|
-
|
|
71701
|
-
|
|
71702
|
-
|
|
71703
|
-
|
|
71706
|
+
const DEFAULT_LOADING_FRAGMENT = `
|
|
71707
|
+
// Shadertoy Fragment Shader
|
|
71708
|
+
// Diagonal background gradient + wider colorful transparent shimmer
|
|
71709
|
+
|
|
71710
|
+
precision highp float;
|
|
71711
|
+
|
|
71712
|
+
varying vec2 vUV;
|
|
71713
|
+
|
|
71714
|
+
uniform vec4 _Time;
|
|
71715
|
+
|
|
71716
|
+
// ------------------------------------------------------------
|
|
71717
|
+
// Background
|
|
71718
|
+
// ------------------------------------------------------------
|
|
71719
|
+
vec3 background(vec2 uv) {
|
|
71720
|
+
vec3 topLeft = vec3(0.88, 0.85, 1.00);
|
|
71721
|
+
vec3 bottomRight = vec3(0.98, 0.98, 0.98);
|
|
71722
|
+
|
|
71723
|
+
float d = (uv.x + (1.0 - uv.y)) * 0.5;
|
|
71724
|
+
d = smoothstep(0.0, 1.0, d);
|
|
71725
|
+
|
|
71726
|
+
vec3 col = mix(topLeft, bottomRight, d);
|
|
71727
|
+
|
|
71728
|
+
vec2 p1 = uv - vec2(1.13, 1.40);
|
|
71729
|
+
float r1 = length(p1 / vec2(0.85, 0.85));
|
|
71730
|
+
float g1 = 1.0 - smoothstep(0.0, 0.96, r1);
|
|
71731
|
+
col += vec3(172.0/255.0, 183.0/255.0, 1.0) * g1 * 0.28;
|
|
71732
|
+
|
|
71733
|
+
vec2 p2 = uv - vec2(0.25, 0.23);
|
|
71734
|
+
float r2 = length(p2 / vec2(0.74, 0.64));
|
|
71735
|
+
float g2 = 1.0 - smoothstep(0.0, 1.0, r2);
|
|
71736
|
+
col += vec3(147.0/255.0, 108.0/255.0, 1.0) * g2 * 0.10;
|
|
71737
|
+
|
|
71738
|
+
return col;
|
|
71739
|
+
}
|
|
71740
|
+
|
|
71741
|
+
// ------------------------------------------------------------
|
|
71742
|
+
// Easing: slow at start, fast at end
|
|
71743
|
+
// ------------------------------------------------------------
|
|
71744
|
+
float easeInQuad(float x) {
|
|
71745
|
+
return x * x;
|
|
71746
|
+
}
|
|
71747
|
+
|
|
71748
|
+
// ------------------------------------------------------------
|
|
71749
|
+
// Color palette helper
|
|
71750
|
+
// ------------------------------------------------------------
|
|
71751
|
+
vec3 palette(float t) {
|
|
71752
|
+
// 柔和的彩色条:蓝 -> 青 -> 紫 -> 粉 -> 蓝
|
|
71753
|
+
vec3 a = vec3(0.62, 0.82, 1.00);
|
|
71754
|
+
vec3 b = vec3(0.72, 0.95, 1.00);
|
|
71755
|
+
vec3 c = vec3(0.83, 0.76, 1.00);
|
|
71756
|
+
vec3 d = vec3(1.00, 0.82, 0.95);
|
|
71757
|
+
|
|
71758
|
+
t = fract(t);
|
|
71759
|
+
|
|
71760
|
+
if (t < 0.33) {
|
|
71761
|
+
float k = smoothstep(0.0, 0.33, t);
|
|
71762
|
+
return mix(a, b, k);
|
|
71763
|
+
} else if (t < 0.66) {
|
|
71764
|
+
float k = smoothstep(0.33, 0.66, t);
|
|
71765
|
+
return mix(b, c, k);
|
|
71766
|
+
} else {
|
|
71767
|
+
float k = smoothstep(0.66, 1.0, t);
|
|
71768
|
+
return mix(c, d, k);
|
|
71704
71769
|
}
|
|
71705
71770
|
}
|
|
71706
|
-
|
|
71707
|
-
|
|
71708
|
-
|
|
71709
|
-
|
|
71710
|
-
|
|
71711
|
-
|
|
71712
|
-
|
|
71713
|
-
|
|
71714
|
-
|
|
71715
|
-
|
|
71716
|
-
|
|
71771
|
+
|
|
71772
|
+
// ------------------------------------------------------------
|
|
71773
|
+
// Wider colorful transparent left-to-right shimmer
|
|
71774
|
+
// ------------------------------------------------------------
|
|
71775
|
+
vec4 shimmerBand(vec2 uv, float t) {
|
|
71776
|
+
float cycle = fract(t * 0.70);
|
|
71777
|
+
cycle = easeInQuad(cycle);
|
|
71778
|
+
|
|
71779
|
+
float x = cycle * 1.55 - 0.25; // sweep from left to right
|
|
71780
|
+
|
|
71781
|
+
float d = abs(uv.x - x);
|
|
71782
|
+
float band = 1.0 - smoothstep(0.0, 0.30, d);
|
|
71783
|
+
float core = 1.0 - smoothstep(0.0, 0.10, d);
|
|
71784
|
+
|
|
71785
|
+
float x2 = x + (uv.y - 0.5) * 0.18;
|
|
71786
|
+
float d2 = abs(uv.x - x2);
|
|
71787
|
+
float band2 = 1.0 - smoothstep(0.0, 0.20, d2);
|
|
71788
|
+
|
|
71789
|
+
float s = max(band * 0.85, band2 * 0.60);
|
|
71790
|
+
s = max(s, core * 0.95);
|
|
71791
|
+
s = clamp(s, 0.0, 1.0);
|
|
71792
|
+
|
|
71793
|
+
// 颜色沿条带变化,模拟彩色渐变
|
|
71794
|
+
float hueT = uv.y * 0.25 + uv.x * 0.60 + t * 0.10;
|
|
71795
|
+
vec3 color1 = palette(hueT);
|
|
71796
|
+
|
|
71797
|
+
// 中间更亮一点,边缘更淡
|
|
71798
|
+
vec3 color2 = mix(color1, vec3(1.0), core * 0.35);
|
|
71799
|
+
|
|
71800
|
+
return vec4(color2, s);
|
|
71801
|
+
}
|
|
71802
|
+
|
|
71803
|
+
// ------------------------------------------------------------
|
|
71804
|
+
// Main
|
|
71805
|
+
// ------------------------------------------------------------
|
|
71806
|
+
void main() {
|
|
71807
|
+
vec2 uv = vUV;
|
|
71808
|
+
|
|
71809
|
+
vec3 col = background(uv);
|
|
71810
|
+
|
|
71811
|
+
vec4 sh = shimmerBand(uv, _Time.y);
|
|
71812
|
+
|
|
71813
|
+
// 可选:增加一点柔光感
|
|
71814
|
+
col += sh.rgb * sh.a * 0.06;
|
|
71815
|
+
|
|
71816
|
+
gl_FragColor = vec4(col, 1.0);
|
|
71817
|
+
}`;
|
|
71818
|
+
const loadingConfig = defineConfig({
|
|
71819
|
+
id: 'feedback.loading',
|
|
71820
|
+
defaults: {
|
|
71821
|
+
loadingFragment: DEFAULT_LOADING_FRAGMENT
|
|
71717
71822
|
}
|
|
71718
|
-
|
|
71719
|
-
|
|
71720
|
-
|
|
71721
|
-
|
|
71722
|
-
|
|
71723
|
-
|
|
71724
|
-
|
|
71725
|
-
const vfxItem = VFXItemFactory.createVFXItem(composition, parent, name, SpriteComponent);
|
|
71726
|
-
vfxItem.type = index$1.ItemType.sprite;
|
|
71727
|
-
return vfxItem;
|
|
71823
|
+
});
|
|
71824
|
+
const viewportNavigationConfig = defineConfig({
|
|
71825
|
+
id: 'viewport.navigation',
|
|
71826
|
+
defaults: {
|
|
71827
|
+
scrollWheelZoom: false,
|
|
71828
|
+
invertZoom: false,
|
|
71829
|
+
zoomStep: 0.1
|
|
71728
71830
|
}
|
|
71729
|
-
|
|
71730
|
-
|
|
71731
|
-
|
|
71732
|
-
|
|
71733
|
-
|
|
71734
|
-
|
|
71735
|
-
|
|
71736
|
-
|
|
71737
|
-
|
|
71738
|
-
|
|
71831
|
+
});
|
|
71832
|
+
const viewportOverlayConfig = defineConfig({
|
|
71833
|
+
id: 'viewport.overlay',
|
|
71834
|
+
defaults: {
|
|
71835
|
+
boxColor: 0xFF0000,
|
|
71836
|
+
boxWidth: 1,
|
|
71837
|
+
outerMaskEnabled: true,
|
|
71838
|
+
markColor: 0x000000,
|
|
71839
|
+
markAlpha: 0.17,
|
|
71840
|
+
safeAreaEnabled: true,
|
|
71841
|
+
safeAreaBoxColor: 0x00FF00,
|
|
71842
|
+
safeAreaBoxAlpha: 0.3
|
|
71739
71843
|
}
|
|
71740
|
-
|
|
71741
|
-
|
|
71742
|
-
|
|
71743
|
-
|
|
71744
|
-
|
|
71745
|
-
|
|
71746
|
-
|
|
71747
|
-
|
|
71748
|
-
|
|
71749
|
-
|
|
71844
|
+
});
|
|
71845
|
+
const selectionPreviewConfig = defineConfig({
|
|
71846
|
+
id: 'selection.preview',
|
|
71847
|
+
defaults: {
|
|
71848
|
+
videoPreSelectedPlay: true,
|
|
71849
|
+
preSelectedColor: 0x3b82f6,
|
|
71850
|
+
preSelectedWidth: 2,
|
|
71851
|
+
regionBoxColor: 0x3b82f6,
|
|
71852
|
+
regionBoxAlpha: 0.17,
|
|
71853
|
+
regionWireframeColor: 0x3b82f6,
|
|
71854
|
+
regionWireframeAlpha: 0.78,
|
|
71855
|
+
regionWireframeWidth: 1
|
|
71750
71856
|
}
|
|
71751
|
-
|
|
71752
|
-
|
|
71753
|
-
|
|
71754
|
-
|
|
71755
|
-
|
|
71756
|
-
|
|
71757
|
-
|
|
71758
|
-
|
|
71759
|
-
const preComposition = await AssetManager$1.loadPrecomposition(effects, {
|
|
71760
|
-
autoplay: false
|
|
71761
|
-
});
|
|
71762
|
-
const vfxItem = PrecompositionManager.instantiate(preComposition, composition);
|
|
71763
|
-
vfxItem.name = urlname;
|
|
71764
|
-
vfxItem.type = index$1.ItemType.composition;
|
|
71765
|
-
vfxItem.getComponent(CompositionComponent).endBehavior = vfxItem.endBehavior;
|
|
71766
|
-
if (parent) {
|
|
71767
|
-
vfxItem.setParent(parent);
|
|
71768
|
-
}
|
|
71769
|
-
return vfxItem;
|
|
71857
|
+
});
|
|
71858
|
+
const selectionSnapConfig = defineConfig({
|
|
71859
|
+
id: 'selection.snap',
|
|
71860
|
+
defaults: {
|
|
71861
|
+
enabled: true,
|
|
71862
|
+
lineWidth: 0.8,
|
|
71863
|
+
lineColor: 0x0BD6FF,
|
|
71864
|
+
distance: 6
|
|
71770
71865
|
}
|
|
71771
|
-
}
|
|
71866
|
+
});
|
|
71867
|
+
const resizeSelectionConfig = defineConfig({
|
|
71868
|
+
id: 'resize-selection',
|
|
71869
|
+
defaults: {
|
|
71870
|
+
pixelRatio: 1,
|
|
71871
|
+
contentRatio: 1,
|
|
71872
|
+
wireframeColor: 0x3b82f6,
|
|
71873
|
+
wireframeAlpha: 1,
|
|
71874
|
+
wireframeWidth: 1.5,
|
|
71875
|
+
cornerFillColor: 0xFFFFFF,
|
|
71876
|
+
cornerLineColor: 0x3b82f6,
|
|
71877
|
+
cornerLineWidth: 1.5,
|
|
71878
|
+
cornerLineAlpha: 1,
|
|
71879
|
+
scaleCircleSize: 4,
|
|
71880
|
+
rotationCircleSize: 7,
|
|
71881
|
+
infoShowEnabled: true,
|
|
71882
|
+
sizeTextColor: 0x666666,
|
|
71883
|
+
nameTextColor: 0x666666,
|
|
71884
|
+
frameMoveLineColor: 0x3b82f6,
|
|
71885
|
+
frameMoveLineWidth: 2,
|
|
71886
|
+
imageLogoUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*F2wVS7x0MfIAAAAAQBAAAAgAev-aAQ/original',
|
|
71887
|
+
groupLogoUrl: 'https://mdn.alipayobjects.com/huamei_ppzin5/afts/img/Yo69Sr7boqYAAAAAH3AAAAgADjdkAQFr/original',
|
|
71888
|
+
textLogoUrl: 'https://mdn.alipayobjects.com/huamei_ppzin5/afts/img/Yo69Sr7boqYAAAAAH3AAAAgADjdkAQFr/original',
|
|
71889
|
+
videoLogoUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*w1fnS4mq0VgAAAAAQCAAAAgAev-aAQ/original',
|
|
71890
|
+
frameLogoUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*DRF_RpndkjUAAAAAQDAAAAgAev-aAQ/original',
|
|
71891
|
+
effectsLogoUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*RMewR4ruUnYAAAAAQGAAAAgAev-aAQ/original'
|
|
71892
|
+
}
|
|
71893
|
+
});
|
|
71894
|
+
const imageCutConfig = defineConfig({
|
|
71895
|
+
id: 'tool.image-cut',
|
|
71896
|
+
defaults: {
|
|
71897
|
+
maskColor: 0xFFFFFF,
|
|
71898
|
+
maskAlpha: 0.5,
|
|
71899
|
+
cutBoxLineWidth: 2,
|
|
71900
|
+
cutBoxLineColor: 0x6A34FF,
|
|
71901
|
+
cutBoxLineAlpha: 1,
|
|
71902
|
+
itemBoxLineWidth: 1,
|
|
71903
|
+
itemBoxLineColor: 0x6A34FF,
|
|
71904
|
+
itemBoxLineAlpha: 1,
|
|
71905
|
+
cutBoxCornerRadius: 5,
|
|
71906
|
+
cutBoxCornerFillColor: 0xFFFFFF,
|
|
71907
|
+
cutBoxCornerLineWidth: 2,
|
|
71908
|
+
cutBoxCornerLineColor: 0x6A34FF,
|
|
71909
|
+
cutBoxCornerLineAlpha: 1,
|
|
71910
|
+
scaleInteractionDistance: 8,
|
|
71911
|
+
directionScaleInteractionDistance: 5,
|
|
71912
|
+
gridLineWidth: 1,
|
|
71913
|
+
gridLineColor: 0xFFFFFF,
|
|
71914
|
+
gridLineAlpha: 1,
|
|
71915
|
+
gridCount: 2
|
|
71916
|
+
}
|
|
71917
|
+
});
|
|
71918
|
+
const imageExpandConfig = defineConfig({
|
|
71919
|
+
id: 'tool.image-expand',
|
|
71920
|
+
defaults: {
|
|
71921
|
+
maskColor: 0x6A34FF,
|
|
71922
|
+
maskAlpha: 0.2,
|
|
71923
|
+
expandBoxLineWidth: 2,
|
|
71924
|
+
expandBoxLineColor: 0x6A34FF,
|
|
71925
|
+
expandBoxLineAlpha: 1,
|
|
71926
|
+
expandBoxCornerRadius: 5,
|
|
71927
|
+
expandBoxCornerLineWidth: 2,
|
|
71928
|
+
expandBoxCornerLineColor: 0x6A34FF,
|
|
71929
|
+
expandBoxCornerLineAlpha: 1,
|
|
71930
|
+
expandBoxCornerFillColor: 0xFFFFFF,
|
|
71931
|
+
scaleInteractionDistance: 8,
|
|
71932
|
+
directionScaleInteractionDistance: 5,
|
|
71933
|
+
gridLineWidth: 1,
|
|
71934
|
+
gridLineColor: 0xFFFFFF,
|
|
71935
|
+
gridLineAlpha: 1,
|
|
71936
|
+
gridCount: 2
|
|
71937
|
+
}
|
|
71938
|
+
});
|
|
71939
|
+
const maskConfig = defineConfig({
|
|
71940
|
+
id: 'tool.mask',
|
|
71941
|
+
defaults: {
|
|
71942
|
+
maskImage: '',
|
|
71943
|
+
brushSize: 20,
|
|
71944
|
+
brushColor: 0x6A34FF,
|
|
71945
|
+
brushAlpha: 0.5,
|
|
71946
|
+
maskColor: 0x00FF00,
|
|
71947
|
+
maskBackgroundColor: 0xFFFFFF,
|
|
71948
|
+
maskAlpha: 1,
|
|
71949
|
+
boxLineWidth: 1,
|
|
71950
|
+
boxLineColor: 0x6A34FF,
|
|
71951
|
+
boxLineAlpha: 1
|
|
71952
|
+
}
|
|
71953
|
+
});
|
|
71954
|
+
const spriteTextEditConfig = defineConfig({
|
|
71955
|
+
id: 'tool.sprite-text-edit',
|
|
71956
|
+
defaults: {
|
|
71957
|
+
textColor: 0xFFFFFF,
|
|
71958
|
+
preSelectedTextColor: 0xFFFFFF,
|
|
71959
|
+
boxLineWidth: 3,
|
|
71960
|
+
dashLineDash: 8,
|
|
71961
|
+
dashLineGap: 8,
|
|
71962
|
+
editBoxAlpha: 0.15,
|
|
71963
|
+
editBoxColor: 0x3B82F6,
|
|
71964
|
+
editBoxLineAlpha: 1,
|
|
71965
|
+
editBoxLineColor: 0x3B82F6,
|
|
71966
|
+
editBoxPreSelectedAlpha: 0.25,
|
|
71967
|
+
editBoxPreSelectedColor: 0x3B82F6,
|
|
71968
|
+
editBoxLinePreSelectedAlpha: 1,
|
|
71969
|
+
editBoxLinePreSelectedColor: 0x3B82F6,
|
|
71970
|
+
hasChangedEditBoxAlpha: 0.2,
|
|
71971
|
+
hasChangedEditBoxColor: 0x22C55E,
|
|
71972
|
+
hasChangedEditBoxLineAlpha: 1,
|
|
71973
|
+
hasChangedEditBoxLineColor: 0x22C55E,
|
|
71974
|
+
hasChangedEditBoxPreSelectedAlpha: 0.3,
|
|
71975
|
+
hasChangedEditBoxPreSelectedColor: 0x22C55E,
|
|
71976
|
+
hasChangedEditBoxLinePreSelectedAlpha: 1,
|
|
71977
|
+
hasChangedEditBoxLinePreSelectedColor: 0x22C55E,
|
|
71978
|
+
editBoxSelectedAlpha: 0.6,
|
|
71979
|
+
editBoxSelectedColor: 0xFFFF00
|
|
71980
|
+
}
|
|
71981
|
+
});
|
|
71982
|
+
const iconConfig = defineConfig({
|
|
71983
|
+
id: 'feedback.icon',
|
|
71984
|
+
defaults: {
|
|
71985
|
+
autoShow: true,
|
|
71986
|
+
videoPlayUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*ORMmSYYHIHUAAAAAJbAAAAgAev-aAQ/original',
|
|
71987
|
+
videoPlayShift: [
|
|
71988
|
+
20,
|
|
71989
|
+
20
|
|
71990
|
+
],
|
|
71991
|
+
videoPlayWidth: 20,
|
|
71992
|
+
videoPlayHeight: 20,
|
|
71993
|
+
imageGeneratorUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*bYB-TIEWLBkAAAAAQGAAAAgAev-aAQ/original',
|
|
71994
|
+
videoGeneratorUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*6cTFT44CuKEAAAAAQCAAAAgAev-aAQ/original',
|
|
71995
|
+
generatorWidth: 200,
|
|
71996
|
+
generatorHeight: 200
|
|
71997
|
+
}
|
|
71998
|
+
});
|
|
71999
|
+
const itemCreateConfig = defineConfig({
|
|
72000
|
+
id: 'tool.item-create',
|
|
72001
|
+
defaults: {
|
|
72002
|
+
frameBorderColor: 0x2178FF,
|
|
72003
|
+
frameBorderWidth: 1,
|
|
72004
|
+
frameBorderAlpha: 0.8,
|
|
72005
|
+
frameFillColor: 0x2178FF,
|
|
72006
|
+
frameFillAlpha: 0.15,
|
|
72007
|
+
frameChildBoxAlpha: 0.35,
|
|
72008
|
+
frameChildBoxColor: 0x2178FF
|
|
72009
|
+
}
|
|
72010
|
+
});
|
|
71772
72011
|
/**
|
|
71773
72012
|
* 获取元素所在合成的相机视图投影矩阵。
|
|
71774
72013
|
* @param item 播放器元素
|
|
@@ -71888,23 +72127,244 @@ class GizmoViewportUtils {
|
|
|
71888
72127
|
return plane;
|
|
71889
72128
|
}
|
|
71890
72129
|
/**
|
|
71891
|
-
*
|
|
71892
|
-
* @param
|
|
71893
|
-
* @
|
|
72130
|
+
* 缩放视图。
|
|
72131
|
+
* @param engine 引擎
|
|
72132
|
+
* @param zoom 缩放值
|
|
72133
|
+
* @param center 缩放中心
|
|
72134
|
+
*/ function zoomView(engine, zoom, center = new Vector2()) {
|
|
72135
|
+
const composition = engine.compositions[0];
|
|
72136
|
+
if (!composition) {
|
|
72137
|
+
return;
|
|
72138
|
+
}
|
|
72139
|
+
const { camera } = composition;
|
|
72140
|
+
const scale = camera.getViewportMatrix().elements[0];
|
|
72141
|
+
const translation = new Vector2(camera.getViewportMatrix().elements[12], camera.getViewportMatrix().elements[13]);
|
|
72142
|
+
const result = scale + zoom;
|
|
72143
|
+
// 1. 反算缩放中心对应的世界坐标。
|
|
72144
|
+
const worldX = (center.x - translation.x) / scale;
|
|
72145
|
+
const worldY = (center.y - translation.y) / scale;
|
|
72146
|
+
// 2. 调整平移,使缩放中心在屏幕上的位置保持不变。
|
|
72147
|
+
const newNDCTranslation = new Vector2(center.x - worldX * result, center.y - worldY * result);
|
|
72148
|
+
const viewportMatrix = new Matrix4().compose(new Vector3(newNDCTranslation.x, newNDCTranslation.y, 0), new Quaternion(), new Vector3(result, result, 1));
|
|
72149
|
+
composition.camera.setViewportMatrix(viewportMatrix);
|
|
72150
|
+
}
|
|
72151
|
+
/**
|
|
72152
|
+
* 平移视图。
|
|
72153
|
+
* @param engine 引擎
|
|
72154
|
+
* @param translation 位移值
|
|
72155
|
+
*/ function panView(engine, translation) {
|
|
72156
|
+
const composition = engine.compositions[0];
|
|
72157
|
+
if (!composition) {
|
|
72158
|
+
return;
|
|
72159
|
+
}
|
|
72160
|
+
const { camera } = composition;
|
|
72161
|
+
const scale = camera.getViewportMatrix().elements[0];
|
|
72162
|
+
const resultTranslation = new Vector2(camera.getViewportMatrix().elements[12], camera.getViewportMatrix().elements[13]).add(translation);
|
|
72163
|
+
const viewportMatrix = new Matrix4().compose(new Vector3(resultTranslation.x, resultTranslation.y, 0), new Quaternion(), new Vector3(scale, scale, 1));
|
|
72164
|
+
camera.setViewportMatrix(viewportMatrix);
|
|
72165
|
+
}
|
|
72166
|
+
/** 统一处理手形工具和滚轮触发的视口平移与缩放。 */ class ViewportNavigationController extends EventEmitter {
|
|
72167
|
+
/** 当前视口导航配置。 */ get config() {
|
|
72168
|
+
return this._owner.getConfigManager().get(viewportNavigationConfig);
|
|
72169
|
+
}
|
|
72170
|
+
/** 当前 Effects 引擎。 */ get _engine() {
|
|
72171
|
+
return this._owner.getEngine();
|
|
72172
|
+
}
|
|
72173
|
+
/**
|
|
72174
|
+
* 设置允许的视口缩放范围。
|
|
72175
|
+
* @param minScale 最小缩放
|
|
72176
|
+
* @param maxScale 最大缩放
|
|
72177
|
+
*/ setScaleRange(minScale, maxScale) {
|
|
72178
|
+
if (!Number.isFinite(minScale) || !Number.isFinite(maxScale) || minScale <= 0 || maxScale <= 0 || minScale > maxScale) {
|
|
72179
|
+
throw new RangeError('Viewport scale range must be finite, positive, and ordered.');
|
|
72180
|
+
}
|
|
72181
|
+
this._minScale = minScale;
|
|
72182
|
+
this._maxScale = maxScale;
|
|
72183
|
+
}
|
|
72184
|
+
/**
|
|
72185
|
+
* 根据滚轮来源和修饰键执行平移或缩放。
|
|
72186
|
+
* @param event 滚轮事件
|
|
72187
|
+
* @param _pointerCaptured 指针是否已被捕获
|
|
72188
|
+
* @returns 视口是否发生变化
|
|
72189
|
+
*/ handleWheel(event, _pointerCaptured) {
|
|
72190
|
+
if (!this._hasViewport()) {
|
|
72191
|
+
return false;
|
|
72192
|
+
}
|
|
72193
|
+
event.accept();
|
|
72194
|
+
if (event.buttonMask !== MouseButtonMask.None) {
|
|
72195
|
+
return false;
|
|
72196
|
+
}
|
|
72197
|
+
const { delta, source } = this._normalizeWheel(event);
|
|
72198
|
+
const mode = this._classifyWheel(event, source);
|
|
72199
|
+
const center = new Vector2(event.position.x, event.position.y);
|
|
72200
|
+
if (mode === 'zoom') {
|
|
72201
|
+
if (delta.y === 0) {
|
|
72202
|
+
return false;
|
|
72203
|
+
}
|
|
72204
|
+
const direction = this.config.invertZoom ? -delta.y : delta.y;
|
|
72205
|
+
const zoomStep = this.config.zoomStep;
|
|
72206
|
+
const zoomShift = clamp(direction * 0.01, -zoomStep, zoomStep);
|
|
72207
|
+
return this._zoomByShift(zoomShift, center, event.ctrlPressed && source === 'trackpad' ? 'pinch-zoom' : 'wheel-zoom');
|
|
72208
|
+
}
|
|
72209
|
+
if (delta.x === 0 && delta.y === 0) {
|
|
72210
|
+
return false;
|
|
72211
|
+
}
|
|
72212
|
+
return this.panByViewDelta(delta, center, 'wheel-pan');
|
|
72213
|
+
}
|
|
72214
|
+
/**
|
|
72215
|
+
* 按视图像素增量平移视口。
|
|
72216
|
+
* @param delta 视图像素位移
|
|
72217
|
+
* @param center 操作中心
|
|
72218
|
+
* @param source 操作来源
|
|
72219
|
+
* @returns 视口是否发生变化
|
|
72220
|
+
*/ panByViewDelta(delta, center, source) {
|
|
72221
|
+
if (!this._hasViewport() || delta.x === 0 && delta.y === 0) {
|
|
72222
|
+
return false;
|
|
72223
|
+
}
|
|
72224
|
+
const containerSize = this._containerSize();
|
|
72225
|
+
if (containerSize.x <= 0 || containerSize.y <= 0) {
|
|
72226
|
+
return false;
|
|
72227
|
+
}
|
|
72228
|
+
panView(this._engine, viewSizeToNDC(delta, containerSize));
|
|
72229
|
+
this._emitChange(center, source);
|
|
72230
|
+
return true;
|
|
72231
|
+
}
|
|
72232
|
+
/**
|
|
72233
|
+
* 按倍率缩放视口。
|
|
72234
|
+
* @param factor 缩放倍率
|
|
72235
|
+
* @param center 缩放中心
|
|
72236
|
+
* @param source 操作来源
|
|
72237
|
+
* @returns 视口是否发生变化
|
|
72238
|
+
*/ zoomByFactor(factor, center, source) {
|
|
72239
|
+
if (!this._hasViewport() || !Number.isFinite(factor) || factor <= 0) {
|
|
72240
|
+
return false;
|
|
72241
|
+
}
|
|
72242
|
+
const currentScale = GizmoViewportUtils.getViewScale(this._engine);
|
|
72243
|
+
const nextScale = clamp(currentScale * factor, this._minScale, this._maxScale);
|
|
72244
|
+
if (nextScale === currentScale) {
|
|
72245
|
+
return false;
|
|
72246
|
+
}
|
|
72247
|
+
const containerSize = this._containerSize();
|
|
72248
|
+
if (containerSize.x <= 0 || containerSize.y <= 0) {
|
|
72249
|
+
return false;
|
|
72250
|
+
}
|
|
72251
|
+
zoomView(this._engine, nextScale - currentScale, viewPositionToNDC(center, containerSize));
|
|
72252
|
+
this._emitChange(center, source);
|
|
72253
|
+
return true;
|
|
72254
|
+
}
|
|
72255
|
+
/** 释放配置订阅和事件监听器。 */ dispose() {
|
|
72256
|
+
this._configOff?.();
|
|
72257
|
+
this._configOff = undefined;
|
|
72258
|
+
for (const listener of this.getListeners('change').slice()){
|
|
72259
|
+
this.off('change', listener);
|
|
72260
|
+
}
|
|
72261
|
+
}
|
|
72262
|
+
/**
|
|
72263
|
+
* 根据滚轮来源和修饰键确定导航模式。
|
|
72264
|
+
* @param event 滚轮事件
|
|
72265
|
+
* @param source 输入设备类型
|
|
72266
|
+
* @returns 滚轮导航模式
|
|
72267
|
+
*/ _classifyWheel(event, source) {
|
|
72268
|
+
const signature = `${source}:${Number(event.ctrlPressed)}:${Number(event.metaPressed)}`;
|
|
72269
|
+
if (signature !== this._wheelSignature) {
|
|
72270
|
+
this._wheelSignature = signature;
|
|
72271
|
+
this._wheelMode = event.ctrlPressed || event.metaPressed || source === 'mouse' && this.config.scrollWheelZoom ? 'zoom' : 'pan';
|
|
72272
|
+
}
|
|
72273
|
+
return this._wheelMode;
|
|
72274
|
+
}
|
|
72275
|
+
/**
|
|
72276
|
+
* 将方向型滚轮事件转换为视图位移。
|
|
72277
|
+
* @param event 滚轮事件
|
|
72278
|
+
* @returns 视图位移和输入设备类型
|
|
72279
|
+
*/ _normalizeWheel(event) {
|
|
72280
|
+
const delta = new Vector2();
|
|
72281
|
+
switch(event.buttonIndex){
|
|
72282
|
+
case MouseButton.WheelUp:
|
|
72283
|
+
delta.y = event.factor;
|
|
72284
|
+
break;
|
|
72285
|
+
case MouseButton.WheelDown:
|
|
72286
|
+
delta.y = -event.factor;
|
|
72287
|
+
break;
|
|
72288
|
+
case MouseButton.WheelLeft:
|
|
72289
|
+
delta.x = event.factor;
|
|
72290
|
+
break;
|
|
72291
|
+
case MouseButton.WheelRight:
|
|
72292
|
+
delta.x = -event.factor;
|
|
72293
|
+
break;
|
|
72294
|
+
}
|
|
72295
|
+
// Windows 下 Shift + 滚轮映射为水平平移。
|
|
72296
|
+
const isApplePlatform = typeof navigator !== 'undefined' && /Mac|iPhone|iPad|iPod/.test(navigator.platform);
|
|
72297
|
+
if (!isApplePlatform && event.shiftPressed && delta.x === 0) {
|
|
72298
|
+
delta.x = delta.y;
|
|
72299
|
+
delta.y = 0;
|
|
72300
|
+
}
|
|
72301
|
+
const source = event.ctrlPressed || event.metaPressed || !Number.isInteger(event.factor) || delta.x !== 0 ? 'trackpad' : 'mouse';
|
|
72302
|
+
return {
|
|
72303
|
+
delta,
|
|
72304
|
+
source
|
|
72305
|
+
};
|
|
72306
|
+
}
|
|
72307
|
+
/**
|
|
72308
|
+
* 按绝对缩放增量更新视口。
|
|
72309
|
+
* @param shift 缩放增量
|
|
72310
|
+
* @param center 缩放中心
|
|
72311
|
+
* @param source 操作来源
|
|
72312
|
+
* @returns 视口是否发生变化
|
|
72313
|
+
*/ _zoomByShift(shift, center, source) {
|
|
72314
|
+
const currentScale = GizmoViewportUtils.getViewScale(this._engine);
|
|
72315
|
+
const nextScale = roundNumber(clamp(currentScale + shift, this._minScale, this._maxScale), 2);
|
|
72316
|
+
if (nextScale === currentScale) {
|
|
72317
|
+
return false;
|
|
72318
|
+
}
|
|
72319
|
+
return this.zoomByFactor(nextScale / currentScale, center, source);
|
|
72320
|
+
}
|
|
72321
|
+
/**
|
|
72322
|
+
* 发出当前视口快照。
|
|
72323
|
+
* @param center 操作中心
|
|
72324
|
+
* @param source 操作来源
|
|
72325
|
+
*/ _emitChange(center, source) {
|
|
72326
|
+
const containerSize = this._containerSize();
|
|
72327
|
+
this.emit('change', {
|
|
72328
|
+
source,
|
|
72329
|
+
scale: GizmoViewportUtils.getViewScale(this._engine),
|
|
72330
|
+
translation: ndcSizeToViewSize(GizmoViewportUtils.getViewportTranslation(this._engine), containerSize),
|
|
72331
|
+
center: center.clone()
|
|
72332
|
+
});
|
|
72333
|
+
}
|
|
72334
|
+
/** @returns 当前画布容器尺寸。 */ _containerSize() {
|
|
72335
|
+
return GizmoViewportUtils.getContainerSize(this._engine.canvas.parentElement);
|
|
72336
|
+
}
|
|
72337
|
+
/** @returns 是否存在可操作的画布容器和相机。 */ _hasViewport() {
|
|
72338
|
+
return Boolean(this._engine.canvas.parentElement && this._engine.compositions[0]?.camera);
|
|
72339
|
+
}
|
|
72340
|
+
/**
|
|
72341
|
+
* @param owner Gizmo 宿主
|
|
72342
|
+
*/ constructor(owner){
|
|
72343
|
+
super(), _define_property(this, "_owner", void 0), _define_property(this, "_minScale", 0.01), _define_property(this, "_maxScale", 20), _define_property(this, "_configOff", void 0), _define_property(this, "_wheelSignature", ''), _define_property(this, "_wheelMode", 'pan');
|
|
72344
|
+
this._owner = owner;
|
|
72345
|
+
this._configOff = owner.getConfigManager().onChange(viewportNavigationConfig, ()=>{
|
|
72346
|
+
this._wheelSignature = '';
|
|
72347
|
+
});
|
|
72348
|
+
}
|
|
72349
|
+
}
|
|
72350
|
+
/**
|
|
72351
|
+
* 判断播放器元素是否为画板元素。
|
|
72352
|
+
* @param item 播放器元素
|
|
72353
|
+
* @returns 是否为画板元素
|
|
71894
72354
|
*/ function isFramePlayerItem(item) {
|
|
71895
72355
|
return item.type === index$1.ItemType.null && item.getComponent(FrameComponent) !== undefined && item.name === '画板';
|
|
71896
72356
|
}
|
|
71897
|
-
/**
|
|
71898
|
-
* 判断播放器元素是否为特效元素。
|
|
71899
|
-
* @param item 播放器元素
|
|
71900
|
-
* @returns 是否为特效元素
|
|
72357
|
+
/**
|
|
72358
|
+
* 判断播放器元素是否为特效元素。
|
|
72359
|
+
* @param item 播放器元素
|
|
72360
|
+
* @returns 是否为特效元素
|
|
71901
72361
|
*/ function isEffectsPlayerItem(item) {
|
|
71902
72362
|
return item.type === index$1.ItemType.null && item.getComponent(FrameComponent) !== undefined && item.name === '特效';
|
|
71903
72363
|
}
|
|
71904
|
-
/**
|
|
71905
|
-
* 判断播放器元素是否为生成器元素。
|
|
71906
|
-
* @param item 播放器元素
|
|
71907
|
-
* @returns 是否为生成器元素
|
|
72364
|
+
/**
|
|
72365
|
+
* 判断播放器元素是否为生成器元素。
|
|
72366
|
+
* @param item 播放器元素
|
|
72367
|
+
* @returns 是否为生成器元素
|
|
71908
72368
|
*/ function isGeneratorPlayerItem(item) {
|
|
71909
72369
|
if (item.type !== index$1.ItemType.sprite) {
|
|
71910
72370
|
return false;
|
|
@@ -71923,18 +72383,95 @@ class GizmoViewportUtils {
|
|
|
71923
72383
|
const content = item.definition?.content;
|
|
71924
72384
|
return content?.generatorType === 'video' || content?.isVideoGenerator === true || item.name.includes('视频');
|
|
71925
72385
|
}
|
|
71926
|
-
/**
|
|
71927
|
-
* 判断播放器元素是否为组元素。
|
|
71928
|
-
* @param item 播放器元素
|
|
71929
|
-
* @returns 是否为组元素
|
|
72386
|
+
/**
|
|
72387
|
+
* 判断播放器元素是否为组元素。
|
|
72388
|
+
* @param item 播放器元素
|
|
72389
|
+
* @returns 是否为组元素
|
|
71930
72390
|
*/ function isGroupPlayerItem(item) {
|
|
71931
72391
|
return item.type === index$1.ItemType.null && !item.getComponent(FrameComponent);
|
|
71932
72392
|
}
|
|
71933
|
-
/**
|
|
71934
|
-
*
|
|
71935
|
-
* @param item 播放器元素
|
|
71936
|
-
* @
|
|
71937
|
-
|
|
72393
|
+
/**
|
|
72394
|
+
* 获取播放器元素的所有子元素。
|
|
72395
|
+
* @param item 播放器元素
|
|
72396
|
+
* @returns 子元素数组
|
|
72397
|
+
*/ function getItemChildren(item) {
|
|
72398
|
+
const children = [];
|
|
72399
|
+
const isFrameItem = isFramePlayerItem(item);
|
|
72400
|
+
const isEffectItem = isEffectsPlayerItem(item);
|
|
72401
|
+
if (isEffectItem) {
|
|
72402
|
+
return children;
|
|
72403
|
+
}
|
|
72404
|
+
/**
|
|
72405
|
+
* 递归展开子元素。
|
|
72406
|
+
* @param items 当前层子元素
|
|
72407
|
+
* @returns 当前层及其全部后代
|
|
72408
|
+
*/ function getAllChildren(items) {
|
|
72409
|
+
const result = [];
|
|
72410
|
+
for (const child of items){
|
|
72411
|
+
result.push(child);
|
|
72412
|
+
if (child.children && child.children.length > 0) {
|
|
72413
|
+
result.push(...getAllChildren(child.children));
|
|
72414
|
+
}
|
|
72415
|
+
}
|
|
72416
|
+
return result;
|
|
72417
|
+
}
|
|
72418
|
+
if (isFrameItem) {
|
|
72419
|
+
return getAllChildren(item.children);
|
|
72420
|
+
} else if (item.children && item.children.length > 0) {
|
|
72421
|
+
return getAllChildren(item.children);
|
|
72422
|
+
}
|
|
72423
|
+
return children;
|
|
72424
|
+
}
|
|
72425
|
+
/**
|
|
72426
|
+
* 在合成元素树内按实例 ID 查找元素。
|
|
72427
|
+
* @param composition 合成(无合成返回 undefined)
|
|
72428
|
+
* @param id 元素 instanceId
|
|
72429
|
+
* @returns 命中元素;未命中或无合成返回 undefined
|
|
72430
|
+
*/ function getPlayerItemById(composition, id) {
|
|
72431
|
+
if (!composition) {
|
|
72432
|
+
return undefined;
|
|
72433
|
+
}
|
|
72434
|
+
/**
|
|
72435
|
+
* 在当前元素子树中递归查找实例。
|
|
72436
|
+
* @param items 当前层元素。
|
|
72437
|
+
* @param targetId 目标实例 ID。
|
|
72438
|
+
* @returns 命中的元素。
|
|
72439
|
+
*/ const dfs = (items, targetId)=>{
|
|
72440
|
+
for (const item of items){
|
|
72441
|
+
if (item.getInstanceId() === targetId) {
|
|
72442
|
+
return item;
|
|
72443
|
+
}
|
|
72444
|
+
const found = dfs(item.children, targetId);
|
|
72445
|
+
if (found) {
|
|
72446
|
+
return found;
|
|
72447
|
+
}
|
|
72448
|
+
}
|
|
72449
|
+
return undefined;
|
|
72450
|
+
};
|
|
72451
|
+
return dfs(composition.items, id);
|
|
72452
|
+
}
|
|
72453
|
+
/**
|
|
72454
|
+
* 过滤掉祖先也在列表中的元素,仅保留顶层元素。
|
|
72455
|
+
* @param items 待过滤元素
|
|
72456
|
+
* @returns 保持原顺序的顶层元素
|
|
72457
|
+
*/ function filterTopLevelPlayerItems(items) {
|
|
72458
|
+
const selectedItems = new Set(items);
|
|
72459
|
+
return items.filter((item)=>{
|
|
72460
|
+
let parent = item.parent;
|
|
72461
|
+
while(parent){
|
|
72462
|
+
if (selectedItems.has(parent)) {
|
|
72463
|
+
return false;
|
|
72464
|
+
}
|
|
72465
|
+
parent = parent.parent;
|
|
72466
|
+
}
|
|
72467
|
+
return true;
|
|
72468
|
+
});
|
|
72469
|
+
}
|
|
72470
|
+
/**
|
|
72471
|
+
* 获取播放器元素的视图包围盒。
|
|
72472
|
+
* @param item 播放器元素
|
|
72473
|
+
* @param containerSize 视图大小
|
|
72474
|
+
* @returns 视图包围盒
|
|
71938
72475
|
*/ function getItemViewBox(item, containerSize) {
|
|
71939
72476
|
// 1. 跳过不可见元素并收集子元素。
|
|
71940
72477
|
const box = new Box2();
|
|
@@ -71987,21 +72524,21 @@ class GizmoViewportUtils {
|
|
|
71987
72524
|
}
|
|
71988
72525
|
return box;
|
|
71989
72526
|
}
|
|
71990
|
-
/**
|
|
71991
|
-
* 获取播放器元素在视图中的锚点坐标。
|
|
71992
|
-
* @param item 播放器元素
|
|
71993
|
-
* @param containerSize 视图容器大小
|
|
71994
|
-
* @returns 视图锚点坐标
|
|
72527
|
+
/**
|
|
72528
|
+
* 获取播放器元素在视图中的锚点坐标。
|
|
72529
|
+
* @param item 播放器元素
|
|
72530
|
+
* @param containerSize 视图容器大小
|
|
72531
|
+
* @returns 视图锚点坐标
|
|
71995
72532
|
*/ function getItemViewAnchor(item, containerSize) {
|
|
71996
72533
|
const anchor = item.transform.anchor ?? item.transform.position;
|
|
71997
72534
|
const worldAnchor = new Vector3().copyFrom(anchor).applyMatrix(new Matrix4().copyFrom(item.transform.getWorldMatrix()));
|
|
71998
72535
|
const viewAnchor = projectPoint(worldAnchor, item, containerSize).multiply(100).round().divide(100);
|
|
71999
72536
|
return viewAnchor;
|
|
72000
72537
|
}
|
|
72001
|
-
/**
|
|
72002
|
-
* 获取元素的变换矩阵和父级变换矩阵。
|
|
72003
|
-
* @param item 播放器元素
|
|
72004
|
-
* @returns 元素的变换矩阵和父级变换矩阵
|
|
72538
|
+
/**
|
|
72539
|
+
* 获取元素的变换矩阵和父级变换矩阵。
|
|
72540
|
+
* @param item 播放器元素
|
|
72541
|
+
* @returns 元素的变换矩阵和父级变换矩阵
|
|
72005
72542
|
*/ function getItemTransform(item) {
|
|
72006
72543
|
const transform = {
|
|
72007
72544
|
matrix: new Matrix4(),
|
|
@@ -72015,43 +72552,11 @@ class GizmoViewportUtils {
|
|
|
72015
72552
|
}
|
|
72016
72553
|
return transform;
|
|
72017
72554
|
}
|
|
72018
|
-
/**
|
|
72019
|
-
*
|
|
72020
|
-
*
|
|
72021
|
-
*
|
|
72022
|
-
|
|
72023
|
-
const children = [];
|
|
72024
|
-
const isFrameItem = isFramePlayerItem(item);
|
|
72025
|
-
const isEffectItem = isEffectsPlayerItem(item);
|
|
72026
|
-
if (isEffectItem) {
|
|
72027
|
-
return children;
|
|
72028
|
-
}
|
|
72029
|
-
/**
|
|
72030
|
-
* 递归展开子元素。
|
|
72031
|
-
* @param items 当前层子元素
|
|
72032
|
-
* @returns 当前层及其全部后代
|
|
72033
|
-
*/ function getAllChildren(items) {
|
|
72034
|
-
const result = [];
|
|
72035
|
-
for (const child of items){
|
|
72036
|
-
result.push(child);
|
|
72037
|
-
if (child.children && child.children.length > 0) {
|
|
72038
|
-
result.push(...getAllChildren(child.children));
|
|
72039
|
-
}
|
|
72040
|
-
}
|
|
72041
|
-
return result;
|
|
72042
|
-
}
|
|
72043
|
-
if (isFrameItem) {
|
|
72044
|
-
return getAllChildren(item.children);
|
|
72045
|
-
} else if (item.children && item.children.length > 0) {
|
|
72046
|
-
return getAllChildren(item.children);
|
|
72047
|
-
}
|
|
72048
|
-
return children;
|
|
72049
|
-
}
|
|
72050
|
-
/**
|
|
72051
|
-
* 获取元素在所属合成坐标系中的实际世界尺寸。
|
|
72052
|
-
*
|
|
72053
|
-
* `transform.size` 是元素本地尺寸;这里叠加元素到合成根之间的缩放,
|
|
72054
|
-
* 但排除合成本身的世界变换,与 Frame 尺寸写回使用的坐标系保持一致。
|
|
72555
|
+
/**
|
|
72556
|
+
* 获取元素在所属合成坐标系中的实际世界尺寸。
|
|
72557
|
+
*
|
|
72558
|
+
* `transform.size` 是元素本地尺寸;这里叠加元素到合成根之间的缩放,
|
|
72559
|
+
* 但排除合成本身的世界变换,与 Frame 尺寸写回使用的坐标系保持一致。
|
|
72055
72560
|
* @param item 播放器元素
|
|
72056
72561
|
* @returns 元素在所属合成坐标系中的尺寸
|
|
72057
72562
|
*/ function getItemWorldSize(item) {
|
|
@@ -72063,1256 +72568,432 @@ class GizmoViewportUtils {
|
|
|
72063
72568
|
itemToComposition.decompose(new Vector3(), new Quaternion(), worldScale);
|
|
72064
72569
|
return new Vector2(Math.abs(transform.size.x * worldScale.x), Math.abs(transform.size.y * worldScale.y));
|
|
72065
72570
|
}
|
|
72066
|
-
/**
|
|
72067
|
-
* 在合成元素树内按实例 ID 查找元素。
|
|
72068
|
-
* @param composition 合成(无合成返回 undefined)
|
|
72069
|
-
* @param id 元素 instanceId
|
|
72070
|
-
* @returns 命中元素;未命中或无合成返回 undefined
|
|
72071
|
-
*/ function getPlayerItemById(composition, id) {
|
|
72072
|
-
if (!composition) {
|
|
72073
|
-
return undefined;
|
|
72074
|
-
}
|
|
72075
|
-
/**
|
|
72076
|
-
* 在当前元素子树中递归查找实例。
|
|
72077
|
-
* @param items 当前层元素。
|
|
72078
|
-
* @param targetId 目标实例 ID。
|
|
72079
|
-
* @returns 命中的元素。
|
|
72080
|
-
*/ const dfs = (items, targetId)=>{
|
|
72081
|
-
for (const item of items){
|
|
72082
|
-
if (item.getInstanceId() === targetId) {
|
|
72083
|
-
return item;
|
|
72084
|
-
}
|
|
72085
|
-
const found = dfs(item.children, targetId);
|
|
72086
|
-
if (found) {
|
|
72087
|
-
return found;
|
|
72088
|
-
}
|
|
72089
|
-
}
|
|
72090
|
-
return undefined;
|
|
72091
|
-
};
|
|
72092
|
-
return dfs(composition.items, id);
|
|
72093
|
-
}
|
|
72094
72571
|
/**
|
|
72095
|
-
*
|
|
72096
|
-
* @param
|
|
72097
|
-
* @
|
|
72098
|
-
|
|
72099
|
-
|
|
72100
|
-
|
|
72101
|
-
|
|
72102
|
-
|
|
72103
|
-
if (selectedItems.has(parent)) {
|
|
72104
|
-
return false;
|
|
72105
|
-
}
|
|
72106
|
-
parent = parent.parent;
|
|
72107
|
-
}
|
|
72108
|
-
return true;
|
|
72109
|
-
});
|
|
72110
|
-
}
|
|
72111
|
-
/**
|
|
72112
|
-
* 播放视频元素。
|
|
72113
|
-
* @param engine 引擎
|
|
72114
|
-
* @param id 视频元素 id
|
|
72115
|
-
*/ function playVideoItem(engine, id) {
|
|
72116
|
-
const playerItem = getPlayerItemById(engine.compositions[0], id);
|
|
72117
|
-
if (playerItem?.type !== index$1.ItemType.video) {
|
|
72572
|
+
* 调整画板元素(Frame)的尺寸与位置,并同步其子合成 Item 的位移。
|
|
72573
|
+
* @param frameItem 画板元素(Frame 空节点控制器)
|
|
72574
|
+
* @param worldSize 目标世界尺寸(所属合成坐标系,不受 viewport zoom 影响)
|
|
72575
|
+
* @param translation 位移
|
|
72576
|
+
* @param initialSize pointer down 时冻结的本地 / 世界尺寸;传入后允许尺寸连续经过 0 并变为负值
|
|
72577
|
+
*/ function resizeFrameItem(frameItem, worldSize, translation, initialSize) {
|
|
72578
|
+
if (!isFramePlayerItem(frameItem)) {
|
|
72579
|
+
console.warn(`Item ${frameItem.getInstanceId()} is not a frame item.`);
|
|
72118
72580
|
return;
|
|
72119
72581
|
}
|
|
72120
|
-
|
|
72121
|
-
|
|
72122
|
-
|
|
72123
|
-
|
|
72124
|
-
*
|
|
72125
|
-
|
|
72126
|
-
|
|
72127
|
-
|
|
72128
|
-
|
|
72129
|
-
|
|
72130
|
-
return;
|
|
72582
|
+
// 1. 根据本地尺寸与世界尺寸的比例写回画板尺寸。
|
|
72583
|
+
const currentWorldSize = initialSize?.worldSize ?? getItemWorldSize(frameItem);
|
|
72584
|
+
const currentLocalSize = initialSize?.localSize ?? frameItem.transform.size;
|
|
72585
|
+
const localWidth = currentWorldSize.x === 0 ? worldSize.x : currentLocalSize.x * worldSize.x / currentWorldSize.x;
|
|
72586
|
+
const localHeight = currentWorldSize.y === 0 ? worldSize.y : currentLocalSize.y * worldSize.y / currentWorldSize.y;
|
|
72587
|
+
frameItem.transform.setSize(localWidth, localHeight);
|
|
72588
|
+
// 2. 将交互位移应用到画板。
|
|
72589
|
+
if (translation && (translation.x !== 0 || translation.y !== 0)) {
|
|
72590
|
+
const currentPosition = frameItem.transform.position;
|
|
72591
|
+
frameItem.setPosition(currentPosition.x + translation.x, currentPosition.y + translation.y, currentPosition.z);
|
|
72131
72592
|
}
|
|
72132
|
-
|
|
72133
|
-
|
|
72134
|
-
|
|
72135
|
-
|
|
72136
|
-
|
|
72137
|
-
|
|
72138
|
-
|
|
72139
|
-
|
|
72140
|
-
|
|
72141
|
-
|
|
72142
|
-
if (compositionItem?.type !== index$1.ItemType.composition) {
|
|
72143
|
-
return;
|
|
72593
|
+
// 3. 反向补偿子合成元素,保持其世界位置不变。
|
|
72594
|
+
const subCompositionItem = frameItem?.children?.[0];
|
|
72595
|
+
if (subCompositionItem && translation) {
|
|
72596
|
+
subCompositionItem.children.forEach((item)=>{
|
|
72597
|
+
const parentMatrix = new Matrix4().copyFrom(item.transform.getParentMatrix() ?? new Matrix4());
|
|
72598
|
+
parentMatrix.setPosition(new Vector3());
|
|
72599
|
+
const result = translation.clone().applyMatrix(parentMatrix.invert()).negate();
|
|
72600
|
+
item.translate(...result.toArray());
|
|
72601
|
+
item.transform.updateLocalMatrix();
|
|
72602
|
+
});
|
|
72144
72603
|
}
|
|
72145
|
-
compositionItem.getComponent(CompositionComponent).play();
|
|
72146
72604
|
}
|
|
72147
|
-
/**
|
|
72148
|
-
|
|
72149
|
-
|
|
72150
|
-
|
|
72151
|
-
|
|
72152
|
-
|
|
72153
|
-
|
|
72154
|
-
|
|
72155
|
-
|
|
72605
|
+
/** 创建 VFXItem、挂载组件并加入合成的底层工厂。 */ class VFXItemFactory {
|
|
72606
|
+
/**
|
|
72607
|
+
* 创建并挂载 VFXItem。
|
|
72608
|
+
* @param composition 目标合成
|
|
72609
|
+
* @param parent 父元素
|
|
72610
|
+
* @param name 元素名称
|
|
72611
|
+
* @param types 待挂载的组件类型
|
|
72612
|
+
* @returns 新建的 VFXItem
|
|
72613
|
+
*/ static createVFXItem(composition, parent = null, name = 'NewVFXItem', ...types) {
|
|
72614
|
+
const vfxItem = new VFXItem(composition.engine);
|
|
72615
|
+
vfxItem.name = name;
|
|
72616
|
+
for (const type of types){
|
|
72617
|
+
vfxItem.addComponent(type);
|
|
72618
|
+
}
|
|
72619
|
+
composition.addItem(vfxItem);
|
|
72620
|
+
// @ts-expect-error rootComposition.items 的类型未公开受支持的写入操作。
|
|
72621
|
+
composition.rootComposition.items.push(vfxItem);
|
|
72622
|
+
if (parent) {
|
|
72623
|
+
vfxItem.setParent(parent);
|
|
72624
|
+
}
|
|
72625
|
+
return vfxItem;
|
|
72156
72626
|
}
|
|
72157
|
-
compositionItem.getComponent(CompositionComponent).pause();
|
|
72158
72627
|
}
|
|
72159
|
-
/**
|
|
72160
|
-
/**
|
|
72161
|
-
|
|
72162
|
-
|
|
72163
|
-
|
|
72164
|
-
|
|
72165
|
-
|
|
72166
|
-
|
|
72167
|
-
|
|
72168
|
-
|
|
72169
|
-
|
|
72170
|
-
* @param alpha 透明度 0..1
|
|
72171
|
-
* @returns effects Color
|
|
72172
|
-
*/ function toColor(hex, alpha = 1) {
|
|
72173
|
-
const key = `${hex}_${alpha}`;
|
|
72174
|
-
const cached = colorCache.get(key);
|
|
72175
|
-
if (cached) {
|
|
72176
|
-
return cached;
|
|
72177
|
-
}
|
|
72178
|
-
const color = new Color((hex >> 16 & 0xff) / 255, (hex >> 8 & 0xff) / 255, (hex & 0xff) / 255, alpha);
|
|
72179
|
-
colorCache.set(key, color);
|
|
72180
|
-
return color;
|
|
72181
|
-
}
|
|
72182
|
-
/**
|
|
72183
|
-
* 离屏文本测量上下文,懒加载并复用。
|
|
72184
|
-
*/ let _measureCtx = null;
|
|
72185
|
-
/**
|
|
72186
|
-
* 测量文本的宽度与可见字形边界。
|
|
72187
|
-
* @param text 文本内容
|
|
72188
|
-
* @param fontSize 字号
|
|
72189
|
-
* @param fontFamily 字体
|
|
72190
|
-
* @param fontWeight 字重
|
|
72191
|
-
* @param fontStyle 字体样式
|
|
72192
|
-
* @returns 文本测量结果
|
|
72193
|
-
*/ function measureTextMetrics(text, fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY, fontWeight, fontStyle) {
|
|
72194
|
-
_measureCtx ?? (_measureCtx = document.createElement('canvas').getContext('2d'));
|
|
72195
|
-
if (!_measureCtx) {
|
|
72196
|
-
return {
|
|
72197
|
-
width: 0,
|
|
72198
|
-
actualBoundingBoxAscent: 0,
|
|
72199
|
-
actualBoundingBoxDescent: 0
|
|
72200
|
-
};
|
|
72201
|
-
}
|
|
72202
|
-
_measureCtx.font = `${''}${fontWeight ? `${fontWeight} ` : ''}${fontSize}px ${fontFamily}`;
|
|
72203
|
-
const metrics = _measureCtx.measureText(text);
|
|
72204
|
-
return {
|
|
72205
|
-
width: metrics.width,
|
|
72206
|
-
actualBoundingBoxAscent: metrics.actualBoundingBoxAscent ?? 0,
|
|
72207
|
-
actualBoundingBoxDescent: metrics.actualBoundingBoxDescent ?? 0
|
|
72208
|
-
};
|
|
72209
|
-
}
|
|
72210
|
-
/**
|
|
72211
|
-
* 计算 effects Control.drawText 实际使用的 atlas cell 高度。
|
|
72212
|
-
* @param fontSize 字号
|
|
72213
|
-
* @param fontFamily 字体
|
|
72214
|
-
* @param fontWeight 字重
|
|
72215
|
-
* @param fontStyle 字体样式
|
|
72216
|
-
* @param resolution 渲染分辨率
|
|
72217
|
-
* @returns 字符图集单元格高度
|
|
72218
|
-
*/ function measureControlTextCellHeight(fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY, fontWeight, fontStyle, resolution = 1) {
|
|
72219
|
-
const safeResolution = Number.isFinite(resolution) && resolution > 0 ? resolution : 1;
|
|
72220
|
-
const scaledFontSize = fontSize * safeResolution;
|
|
72221
|
-
const metrics = measureTextMetrics(METRICS_STRING + BASELINE_SYMBOL, scaledFontSize, fontFamily, fontWeight);
|
|
72222
|
-
const ascent = metrics.actualBoundingBoxAscent || scaledFontSize * 0.8;
|
|
72223
|
-
const descent = metrics.actualBoundingBoxDescent || scaledFontSize * 0.2;
|
|
72224
|
-
return Math.ceil(ascent + descent + CONTROL_TEXT_GLYPH_PADDING * 2 * safeResolution) / safeResolution;
|
|
72225
|
-
}
|
|
72226
|
-
/**
|
|
72227
|
-
* 测量文本宽度(逻辑像素),用于信息标签的溢出判断与右对齐定位。
|
|
72228
|
-
* @param text 文本内容
|
|
72229
|
-
* @param fontSize 字号
|
|
72230
|
-
* @param fontFamily 字体
|
|
72231
|
-
* @param fontWeight 字重
|
|
72232
|
-
* @returns 文本宽度
|
|
72233
|
-
*/ function measureTextWidth(text, fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY, fontWeight) {
|
|
72234
|
-
return measureTextMetrics(text, fontSize, fontFamily, fontWeight).width;
|
|
72235
|
-
}
|
|
72236
|
-
/**
|
|
72237
|
-
* 单行文本截断:逐字符累加,超出可用宽度时把最后两个字符替换为省略号。
|
|
72238
|
-
* @param text 文本内容
|
|
72239
|
-
* @param maxWidth 可用宽度
|
|
72240
|
-
* @param fontSize 字号
|
|
72241
|
-
* @param fontFamily 字体
|
|
72242
|
-
* @returns 截断后的文本
|
|
72243
|
-
*/ function truncateText(text, maxWidth, fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY) {
|
|
72244
|
-
if (measureTextWidth(text, fontSize, fontFamily) <= maxWidth) {
|
|
72245
|
-
return text;
|
|
72246
|
-
}
|
|
72247
|
-
let current = '';
|
|
72248
|
-
for (const char of text){
|
|
72249
|
-
if (measureTextWidth(current + char, fontSize, fontFamily) > maxWidth) {
|
|
72250
|
-
return current.length > 2 ? current.slice(0, -2) + '...' : '...';
|
|
72251
|
-
}
|
|
72252
|
-
current += char;
|
|
72253
|
-
}
|
|
72254
|
-
return current;
|
|
72255
|
-
}
|
|
72256
|
-
/**
|
|
72257
|
-
* 多行逐字符折行:行宽超出 maxWidth 时换行;行数达到 maxLines 时末行用省略号收尾。
|
|
72258
|
-
* @param text 文本内容
|
|
72259
|
-
* @param maxWidth 单行可用宽度
|
|
72260
|
-
* @param maxLines 最大行数
|
|
72261
|
-
* @param fontSize 字号
|
|
72262
|
-
* @param fontFamily 字体
|
|
72263
|
-
* @returns 折行后的文本行数组
|
|
72264
|
-
*/ function wrapText(text, maxWidth, maxLines, fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY) {
|
|
72265
|
-
const lines = [];
|
|
72266
|
-
let currentLine = '';
|
|
72267
|
-
const chars = text.split('');
|
|
72268
|
-
for(let i = 0; i < chars.length; i++){
|
|
72269
|
-
const char = chars[i];
|
|
72270
|
-
const testLine = currentLine + char;
|
|
72271
|
-
if (measureTextWidth(testLine, fontSize, fontFamily) > maxWidth) {
|
|
72272
|
-
lines.push(currentLine);
|
|
72273
|
-
if (lines.length >= maxLines) {
|
|
72274
|
-
const lastLine = lines[maxLines - 1];
|
|
72275
|
-
lines[maxLines - 1] = lastLine.length > 2 ? lastLine.slice(0, -2) + '...' : '...';
|
|
72276
|
-
break;
|
|
72277
|
-
}
|
|
72278
|
-
currentLine = char;
|
|
72279
|
-
} else {
|
|
72280
|
-
currentLine = testLine;
|
|
72281
|
-
}
|
|
72282
|
-
if (i === chars.length - 1 && currentLine) {
|
|
72283
|
-
lines.push(currentLine);
|
|
72284
|
-
}
|
|
72285
|
-
}
|
|
72286
|
-
return lines.slice(0, maxLines);
|
|
72287
|
-
}
|
|
72288
|
-
/**
|
|
72289
|
-
* 绘制由 4 个角点构成的(可能非正交的)包围盒边框。
|
|
72290
|
-
* @param control 绘制控制器
|
|
72291
|
-
* @param corners 4 个角点(视图坐标,Y 向下)
|
|
72292
|
-
* @param color 边框颜色
|
|
72293
|
-
* @param width 线宽
|
|
72294
|
-
*/ function drawCorners(control, corners, color, width) {
|
|
72295
|
-
if (corners.length < 4) {
|
|
72296
|
-
return;
|
|
72297
|
-
}
|
|
72298
|
-
for(let i = 0; i < 4; i++){
|
|
72299
|
-
const start = corners[i];
|
|
72300
|
-
const end = corners[(i + 1) % 4];
|
|
72301
|
-
control.drawLine(start.x, start.y, end.x, end.y, color, width);
|
|
72302
|
-
}
|
|
72303
|
-
}
|
|
72304
|
-
/**
|
|
72305
|
-
* 绘制虚线包围盒,并在相邻边之间保持连续虚线相位。
|
|
72306
|
-
* @param control 绘制控制器
|
|
72307
|
-
* @param corners 四个视图角点
|
|
72308
|
-
* @param color 虚线颜色
|
|
72309
|
-
* @param width 线宽
|
|
72310
|
-
* @param dashLength 实线段长度
|
|
72311
|
-
* @param gapLength 间隔长度
|
|
72312
|
-
*/ function drawDashedCorners(control, corners, color, width, dashLength, gapLength) {
|
|
72313
|
-
// 1. 校验角点和虚线参数。
|
|
72314
|
-
if (corners.length < 4 || dashLength <= 0 || gapLength < 0) {
|
|
72315
|
-
return;
|
|
72316
|
-
}
|
|
72317
|
-
const patternLength = dashLength + gapLength;
|
|
72318
|
-
let perimeterOffset = 0;
|
|
72319
|
-
// 2. 逐边计算虚线相位,并继承上一条边的周长偏移。
|
|
72320
|
-
for(let i = 0; i < 4; i++){
|
|
72321
|
-
const start = corners[i];
|
|
72322
|
-
const end = corners[(i + 1) % 4];
|
|
72323
|
-
const dx = end.x - start.x;
|
|
72324
|
-
const dy = end.y - start.y;
|
|
72325
|
-
const edgeLength = Math.hypot(dx, dy);
|
|
72326
|
-
if (edgeLength <= Number.EPSILON) {
|
|
72327
|
-
continue;
|
|
72328
|
-
}
|
|
72329
|
-
let edgeOffset = 0;
|
|
72330
|
-
while(edgeOffset < edgeLength){
|
|
72331
|
-
const patternOffset = perimeterOffset % patternLength;
|
|
72332
|
-
const drawingDash = gapLength === 0 || patternOffset < dashLength;
|
|
72333
|
-
const phaseRemaining = drawingDash ? dashLength - patternOffset : patternLength - patternOffset;
|
|
72334
|
-
const segmentLength = Math.min(phaseRemaining, edgeLength - edgeOffset);
|
|
72335
|
-
// 3. 仅绘制当前相位中的实线段。
|
|
72336
|
-
if (drawingDash && segmentLength > Number.EPSILON) {
|
|
72337
|
-
const startRatio = edgeOffset / edgeLength;
|
|
72338
|
-
const endRatio = (edgeOffset + segmentLength) / edgeLength;
|
|
72339
|
-
control.drawLine(start.x + dx * startRatio, start.y + dy * startRatio, start.x + dx * endRatio, start.y + dy * endRatio, color, width);
|
|
72340
|
-
}
|
|
72341
|
-
edgeOffset += segmentLength;
|
|
72342
|
-
perimeterOffset += segmentLength;
|
|
72343
|
-
}
|
|
72344
|
-
}
|
|
72345
|
-
}
|
|
72346
|
-
/**
|
|
72347
|
-
* 绘制包围盒边框(沿 4 个角点连边)。
|
|
72348
|
-
* @param control 绘制控制器
|
|
72349
|
-
* @param box 包围盒(视图坐标,Y 向下)
|
|
72350
|
-
* @param color 边框颜色
|
|
72351
|
-
* @param width 线宽
|
|
72352
|
-
*/ function drawBox(control, box, color, width) {
|
|
72353
|
-
drawCorners(control, box.corners, color, width);
|
|
72354
|
-
}
|
|
72355
|
-
/**
|
|
72356
|
-
* 填充轴对齐包围盒(实心矩形)。
|
|
72357
|
-
* @param control 绘制控制器
|
|
72358
|
-
* @param box 包围盒(视图坐标,Y 向下)
|
|
72359
|
-
* @param color 填充颜色
|
|
72360
|
-
*/ function fillBox(control, box, color) {
|
|
72361
|
-
const size = box.getSize();
|
|
72362
|
-
control.fillRect(box.min.x, box.min.y, size.x, size.y, color);
|
|
72363
|
-
}
|
|
72364
|
-
/**
|
|
72365
|
-
* 构造沿局部轴展开的旋转矩形角点。
|
|
72366
|
-
* @param center 矩形中心(视图坐标)
|
|
72367
|
-
* @param xAxis 局部 X 轴单位向量(视图坐标,宽方向)
|
|
72368
|
-
* @param yAxis 局部 Y 轴单位向量(视图坐标,高方向)
|
|
72369
|
-
* @param halfWidth 半宽(沿 xAxis,视图像素)
|
|
72370
|
-
* @param halfHeight 半高(沿 yAxis,视图像素)
|
|
72371
|
-
* @returns 4 个角点(视图坐标,环绕顺序)
|
|
72372
|
-
*/ function rotatedRectCorners(center, xAxis, yAxis, halfWidth, halfHeight) {
|
|
72373
|
-
const halfW = xAxis.clone().multiply(halfWidth);
|
|
72374
|
-
const halfH = yAxis.clone().multiply(halfHeight);
|
|
72375
|
-
return [
|
|
72376
|
-
center.clone().subtract(halfW).subtract(halfH),
|
|
72377
|
-
center.clone().add(halfW).subtract(halfH),
|
|
72378
|
-
center.clone().add(halfW).add(halfH),
|
|
72379
|
-
center.clone().subtract(halfW).add(halfH)
|
|
72380
|
-
];
|
|
72381
|
-
}
|
|
72382
|
-
/**
|
|
72383
|
-
* 填充沿局部轴展开的旋转矩形。
|
|
72384
|
-
* @param control 绘制控制器
|
|
72385
|
-
* @param center 矩形中心(视图坐标)
|
|
72386
|
-
* @param xAxis 局部 X 轴单位向量(视图坐标,宽方向)
|
|
72387
|
-
* @param yAxis 局部 Y 轴单位向量(视图坐标,高方向)
|
|
72388
|
-
* @param width 宽(沿 xAxis,视图像素)
|
|
72389
|
-
* @param height 高(沿 yAxis,视图像素)
|
|
72390
|
-
* @param color 填充颜色
|
|
72391
|
-
*/ function fillRotatedRect(control, center, xAxis, yAxis, width, height, color) {
|
|
72392
|
-
const ex = xAxis.clone();
|
|
72393
|
-
const ey = yAxis.clone();
|
|
72394
|
-
const anchor = center.clone();
|
|
72395
|
-
drawRotatedQuad(control, anchor, ex, ey, ()=>{
|
|
72396
|
-
control.fillRect(-width / 2, -height / 2, width, height, color);
|
|
72397
|
-
});
|
|
72398
|
-
}
|
|
72399
|
-
/**
|
|
72400
|
-
* 在由锚点和两个基向量定义的局部坐标系内执行绘制。
|
|
72401
|
-
* @param control 绘制控制器
|
|
72402
|
-
* @param anchor Control 绘制空间锚点(局部原点)
|
|
72403
|
-
* @param ex 局部 X 轴基向量(单位向量)
|
|
72404
|
-
* @param ey 局部 Y 轴基向量(单位向量)
|
|
72405
|
-
* @param drawFn 在局部坐标系内执行的绘制
|
|
72406
|
-
*/ function drawRotatedQuad(control, anchor, ex, ey, drawFn) {
|
|
72407
|
-
const graphics = control.engine.graphics;
|
|
72408
|
-
// 列优先:第一列 = ex,第二列 = ey,第三列 = anchor(平移)
|
|
72409
|
-
const matrix = Matrix3.fromColumnVectors(new Vector3(ex.x, ex.y, 0), new Vector3(ey.x, ey.y, 0), new Vector3(anchor.x, anchor.y, 1));
|
|
72410
|
-
graphics.pushTransform(matrix);
|
|
72411
|
-
drawFn();
|
|
72412
|
-
graphics.popTransform();
|
|
72413
|
-
}
|
|
72414
|
-
const DEFAULT_LOADING_FRAGMENT = `
|
|
72415
|
-
// Shadertoy Fragment Shader
|
|
72416
|
-
// Diagonal background gradient + wider colorful transparent shimmer
|
|
72417
|
-
|
|
72418
|
-
precision highp float;
|
|
72419
|
-
|
|
72420
|
-
varying vec2 vUV;
|
|
72421
|
-
|
|
72422
|
-
uniform vec4 _Time;
|
|
72423
|
-
|
|
72424
|
-
// ------------------------------------------------------------
|
|
72425
|
-
// Background
|
|
72426
|
-
// ------------------------------------------------------------
|
|
72427
|
-
vec3 background(vec2 uv) {
|
|
72428
|
-
vec3 topLeft = vec3(0.88, 0.85, 1.00);
|
|
72429
|
-
vec3 bottomRight = vec3(0.98, 0.98, 0.98);
|
|
72430
|
-
|
|
72431
|
-
float d = (uv.x + (1.0 - uv.y)) * 0.5;
|
|
72432
|
-
d = smoothstep(0.0, 1.0, d);
|
|
72433
|
-
|
|
72434
|
-
vec3 col = mix(topLeft, bottomRight, d);
|
|
72435
|
-
|
|
72436
|
-
vec2 p1 = uv - vec2(1.13, 1.40);
|
|
72437
|
-
float r1 = length(p1 / vec2(0.85, 0.85));
|
|
72438
|
-
float g1 = 1.0 - smoothstep(0.0, 0.96, r1);
|
|
72439
|
-
col += vec3(172.0/255.0, 183.0/255.0, 1.0) * g1 * 0.28;
|
|
72440
|
-
|
|
72441
|
-
vec2 p2 = uv - vec2(0.25, 0.23);
|
|
72442
|
-
float r2 = length(p2 / vec2(0.74, 0.64));
|
|
72443
|
-
float g2 = 1.0 - smoothstep(0.0, 1.0, r2);
|
|
72444
|
-
col += vec3(147.0/255.0, 108.0/255.0, 1.0) * g2 * 0.10;
|
|
72445
|
-
|
|
72446
|
-
return col;
|
|
72447
|
-
}
|
|
72448
|
-
|
|
72449
|
-
// ------------------------------------------------------------
|
|
72450
|
-
// Easing: slow at start, fast at end
|
|
72451
|
-
// ------------------------------------------------------------
|
|
72452
|
-
float easeInQuad(float x) {
|
|
72453
|
-
return x * x;
|
|
72454
|
-
}
|
|
72455
|
-
|
|
72456
|
-
// ------------------------------------------------------------
|
|
72457
|
-
// Color palette helper
|
|
72458
|
-
// ------------------------------------------------------------
|
|
72459
|
-
vec3 palette(float t) {
|
|
72460
|
-
// 柔和的彩色条:蓝 -> 青 -> 紫 -> 粉 -> 蓝
|
|
72461
|
-
vec3 a = vec3(0.62, 0.82, 1.00);
|
|
72462
|
-
vec3 b = vec3(0.72, 0.95, 1.00);
|
|
72463
|
-
vec3 c = vec3(0.83, 0.76, 1.00);
|
|
72464
|
-
vec3 d = vec3(1.00, 0.82, 0.95);
|
|
72465
|
-
|
|
72466
|
-
t = fract(t);
|
|
72467
|
-
|
|
72468
|
-
if (t < 0.33) {
|
|
72469
|
-
float k = smoothstep(0.0, 0.33, t);
|
|
72470
|
-
return mix(a, b, k);
|
|
72471
|
-
} else if (t < 0.66) {
|
|
72472
|
-
float k = smoothstep(0.33, 0.66, t);
|
|
72473
|
-
return mix(b, c, k);
|
|
72474
|
-
} else {
|
|
72475
|
-
float k = smoothstep(0.66, 1.0, t);
|
|
72476
|
-
return mix(c, d, k);
|
|
72477
|
-
}
|
|
72478
|
-
}
|
|
72479
|
-
|
|
72480
|
-
// ------------------------------------------------------------
|
|
72481
|
-
// Wider colorful transparent left-to-right shimmer
|
|
72482
|
-
// ------------------------------------------------------------
|
|
72483
|
-
vec4 shimmerBand(vec2 uv, float t) {
|
|
72484
|
-
float cycle = fract(t * 0.70);
|
|
72485
|
-
cycle = easeInQuad(cycle);
|
|
72486
|
-
|
|
72487
|
-
float x = cycle * 1.55 - 0.25; // sweep from left to right
|
|
72488
|
-
|
|
72489
|
-
float d = abs(uv.x - x);
|
|
72490
|
-
float band = 1.0 - smoothstep(0.0, 0.30, d);
|
|
72491
|
-
float core = 1.0 - smoothstep(0.0, 0.10, d);
|
|
72492
|
-
|
|
72493
|
-
float x2 = x + (uv.y - 0.5) * 0.18;
|
|
72494
|
-
float d2 = abs(uv.x - x2);
|
|
72495
|
-
float band2 = 1.0 - smoothstep(0.0, 0.20, d2);
|
|
72496
|
-
|
|
72497
|
-
float s = max(band * 0.85, band2 * 0.60);
|
|
72498
|
-
s = max(s, core * 0.95);
|
|
72499
|
-
s = clamp(s, 0.0, 1.0);
|
|
72500
|
-
|
|
72501
|
-
// 颜色沿条带变化,模拟彩色渐变
|
|
72502
|
-
float hueT = uv.y * 0.25 + uv.x * 0.60 + t * 0.10;
|
|
72503
|
-
vec3 color1 = palette(hueT);
|
|
72504
|
-
|
|
72505
|
-
// 中间更亮一点,边缘更淡
|
|
72506
|
-
vec3 color2 = mix(color1, vec3(1.0), core * 0.35);
|
|
72507
|
-
|
|
72508
|
-
return vec4(color2, s);
|
|
72509
|
-
}
|
|
72510
|
-
|
|
72511
|
-
// ------------------------------------------------------------
|
|
72512
|
-
// Main
|
|
72513
|
-
// ------------------------------------------------------------
|
|
72514
|
-
void main() {
|
|
72515
|
-
vec2 uv = vUV;
|
|
72516
|
-
|
|
72517
|
-
vec3 col = background(uv);
|
|
72518
|
-
|
|
72519
|
-
vec4 sh = shimmerBand(uv, _Time.y);
|
|
72520
|
-
|
|
72521
|
-
// 可选:增加一点柔光感
|
|
72522
|
-
col += sh.rgb * sh.a * 0.06;
|
|
72523
|
-
|
|
72524
|
-
gl_FragColor = vec4(col, 1.0);
|
|
72525
|
-
}`;
|
|
72526
|
-
const loadingConfig = defineConfig({
|
|
72527
|
-
id: 'feedback.loading',
|
|
72528
|
-
defaults: {
|
|
72529
|
-
loadingFragment: DEFAULT_LOADING_FRAGMENT
|
|
72530
|
-
}
|
|
72531
|
-
});
|
|
72532
|
-
const LOADING_TIP_FONT_SIZE = 16;
|
|
72533
|
-
const LOADING_TIP_FONT_FAMILY = 'sans-serif';
|
|
72534
|
-
const LOADING_TIP_COLOR = 0x000000;
|
|
72535
|
-
/** 为目标元素叠加加载动画与提示文案。 */ class LoadingGizmo extends Gizmo {
|
|
72536
|
-
/** 当前加载覆盖层配置。 */ get config() {
|
|
72537
|
-
return this._owner.getConfigManager().get(loadingConfig);
|
|
72538
|
-
}
|
|
72539
|
-
/** 当前 Gizmo 实例持有的可销毁渲染投影。 */ get idMap() {
|
|
72540
|
-
return this._idMap;
|
|
72541
|
-
}
|
|
72542
|
-
/** 当前 loading 元素 id 列表(由稳定 manager 派生)。 */ get loadingIds() {
|
|
72543
|
-
return this._manager.ids;
|
|
72544
|
-
}
|
|
72545
|
-
/**
|
|
72546
|
-
* 在片元着色器变化时重建加载动画对象。
|
|
72547
|
-
* @param change 加载配置变更。
|
|
72548
|
-
*/ _onConfigChange(change) {
|
|
72549
|
-
if (change.previous.loadingFragment === change.current.loadingFragment) {
|
|
72550
|
-
return;
|
|
72551
|
-
}
|
|
72552
|
-
for (const [id, loadingItem] of this._idMap){
|
|
72553
|
-
this._disposeLoadingVFXItem(loadingItem.loadingVFXItem);
|
|
72554
|
-
loadingItem.loadingVFXItem = this.createLoadingVFXItem();
|
|
72555
|
-
this.updateLoadingVFXItemTransform(id, loadingItem);
|
|
72556
|
-
}
|
|
72557
|
-
}
|
|
72558
|
-
/**
|
|
72559
|
-
* 订阅 loading 集合变化:add/delete 后回调当前全量 id 列表。
|
|
72560
|
-
* @param cb 收到当前全量 loading id 列表的回调
|
|
72561
|
-
* @returns 退订函数,调用后取消对应监听
|
|
72562
|
-
*/ onLoadingChange(cb) {
|
|
72563
|
-
return this._manager.on('change', (change)=>{
|
|
72564
|
-
if (change.addedIds.length > 0 || change.removedIds.length > 0) {
|
|
72565
|
-
cb(change.ids);
|
|
72566
|
-
}
|
|
72567
|
-
});
|
|
72568
|
-
}
|
|
72569
|
-
/**
|
|
72570
|
-
* 为指定元素添加 loading 覆盖层;同一元素不可重复添加。
|
|
72571
|
-
* @param id 目标元素 ID
|
|
72572
|
-
* @param options loading 配置(文案、位置、自定义区域、是否清空选中)
|
|
72573
|
-
*/ add(id, options) {
|
|
72574
|
-
this._manager.add(id, options);
|
|
72628
|
+
/** 创建内置类型 VFXItem 的便捷工厂。 */ class DefaultVFXItems {
|
|
72629
|
+
/**
|
|
72630
|
+
* 创建空元素。
|
|
72631
|
+
* @param composition 目标合成
|
|
72632
|
+
* @param parent 父元素
|
|
72633
|
+
* @param name 元素名称
|
|
72634
|
+
* @returns 新建的空元素
|
|
72635
|
+
*/ static createEmpty(composition, parent = null, name = 'NewVFXItem') {
|
|
72636
|
+
const vfxItem = VFXItemFactory.createVFXItem(composition, parent, name);
|
|
72637
|
+
vfxItem.type = index$1.ItemType.null;
|
|
72638
|
+
return vfxItem;
|
|
72575
72639
|
}
|
|
72576
72640
|
/**
|
|
72577
|
-
*
|
|
72578
|
-
* @param
|
|
72579
|
-
|
|
72580
|
-
|
|
72581
|
-
|
|
72582
|
-
|
|
72583
|
-
|
|
72584
|
-
|
|
72585
|
-
|
|
72586
|
-
*/ updateItem(id, options) {
|
|
72587
|
-
this._manager.update(id, options);
|
|
72588
|
-
}
|
|
72589
|
-
/** 根据目标元素的实时包围盒刷新加载动画。 */ onPreRender() {
|
|
72590
|
-
if (this._idMap.size === 0) {
|
|
72591
|
-
return;
|
|
72592
|
-
}
|
|
72593
|
-
for (const [id, loadingItem] of this._idMap){
|
|
72594
|
-
this.updateLoadingVFXItemTransform(id, loadingItem);
|
|
72595
|
-
}
|
|
72596
|
-
}
|
|
72597
|
-
/**
|
|
72598
|
-
* 绘制所有加载提示文案。
|
|
72599
|
-
* @param control 绘制控制器。
|
|
72600
|
-
*/ draw(control) {
|
|
72601
|
-
if (this._idMap.size === 0) {
|
|
72602
|
-
return;
|
|
72603
|
-
}
|
|
72604
|
-
for (const [id, loadingItem] of this._idMap){
|
|
72605
|
-
if (!loadingItem.tip?.text) {
|
|
72606
|
-
continue;
|
|
72607
|
-
}
|
|
72608
|
-
const item = getPlayerItemById(this._owner.getEngine().compositions[0], id);
|
|
72609
|
-
const itemBox = item ? getItemViewBox(item, GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement)) : new Box2();
|
|
72610
|
-
const loadingBox = getBoxByNormalizeBox(itemBox, loadingItem.loadingBox);
|
|
72611
|
-
this.drawLoadingTip(control, loadingBox, itemBox, loadingItem.tip);
|
|
72612
|
-
}
|
|
72613
|
-
}
|
|
72614
|
-
/**
|
|
72615
|
-
* 绘制单条加载提示,并将文本限制在加载区域内。
|
|
72616
|
-
* @param control 绘制控制器。
|
|
72617
|
-
* @param loadingBox 加载区域。
|
|
72618
|
-
* @param itemBox 目标元素包围盒。
|
|
72619
|
-
* @param tip 加载提示配置。
|
|
72620
|
-
*/ drawLoadingTip(control, loadingBox, itemBox, tip) {
|
|
72621
|
-
// 步骤 1:计算提示位置与可用宽度。
|
|
72622
|
-
const { x: width, y: height } = loadingBox.getSize(this._loadingBoxSize);
|
|
72623
|
-
if (!tip.text || !Number.isFinite(width) || width <= 0) {
|
|
72624
|
-
return;
|
|
72625
|
-
}
|
|
72626
|
-
const positionedLeft = tip.position ? itemBox.min.x + (Number.isFinite(tip.position.x) ? tip.position.x : 0) : loadingBox.min.x;
|
|
72627
|
-
const left = tip.position ? Math.min(Math.max(positionedLeft, loadingBox.min.x), loadingBox.max.x) : loadingBox.min.x;
|
|
72628
|
-
const availableWidth = tip.position ? loadingBox.max.x - left : width;
|
|
72629
|
-
if (availableWidth <= 0) {
|
|
72630
|
-
return;
|
|
72631
|
-
}
|
|
72632
|
-
/**
|
|
72633
|
-
* 测量加载提示的粗体字形。
|
|
72634
|
-
* @param text 提示文本。
|
|
72635
|
-
* @param fontSize 字号。
|
|
72636
|
-
* @returns 文本字形尺寸。
|
|
72637
|
-
*/ const measureTipText = (text, fontSize)=>{
|
|
72638
|
-
const metrics = measureTextMetrics(text, fontSize, LOADING_TIP_FONT_FAMILY, 'bold');
|
|
72639
|
-
return {
|
|
72640
|
-
width: metrics.width || Array.from(text).length * fontSize,
|
|
72641
|
-
actualBoundingBoxAscent: metrics.actualBoundingBoxAscent || fontSize * 0.8,
|
|
72642
|
-
actualBoundingBoxDescent: metrics.actualBoundingBoxDescent || fontSize * 0.2
|
|
72643
|
-
};
|
|
72644
|
-
};
|
|
72645
|
-
// 步骤 2:测量文本并缩小字号以适应可用宽度。
|
|
72646
|
-
const measuredAtBaseSize = measureTipText(tip.text, LOADING_TIP_FONT_SIZE);
|
|
72647
|
-
let fontSize = measuredAtBaseSize.width > availableWidth ? LOADING_TIP_FONT_SIZE * availableWidth / measuredAtBaseSize.width : LOADING_TIP_FONT_SIZE;
|
|
72648
|
-
let textMetrics = measureTipText(tip.text, fontSize);
|
|
72649
|
-
if (textMetrics.width > availableWidth) {
|
|
72650
|
-
fontSize *= availableWidth / textMetrics.width;
|
|
72651
|
-
textMetrics = measureTipText(tip.text, fontSize);
|
|
72652
|
-
}
|
|
72653
|
-
// 步骤 3:按字形基线计算最终绘制位置。
|
|
72654
|
-
const textLeft = tip.position ? left : loadingBox.min.x + (width - textMetrics.width) / 2;
|
|
72655
|
-
const inkHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;
|
|
72656
|
-
const top = tip.position ? itemBox.min.y + (Number.isFinite(tip.position.y) ? tip.position.y : 0) : loadingBox.min.y + (height - inkHeight) / 2;
|
|
72657
|
-
const probeMetrics = measureTipText(METRICS_STRING + BASELINE_SYMBOL, fontSize);
|
|
72658
|
-
const inkTopFromCellTop = CONTROL_TEXT_GLYPH_PADDING + probeMetrics.actualBoundingBoxAscent - textMetrics.actualBoundingBoxAscent;
|
|
72659
|
-
control.drawText(textLeft, top - inkTopFromCellTop, tip.text, fontSize, toColor(LOADING_TIP_COLOR, 1), LOADING_TIP_FONT_FAMILY, 'bold');
|
|
72660
|
-
}
|
|
72661
|
-
/** 释放当前实例的渲染投影;稳定 LoadingManager 不随 Gizmo 重建清空。 */ dispose() {
|
|
72662
|
-
this._configOff?.();
|
|
72663
|
-
this._configOff = undefined;
|
|
72664
|
-
this._managerOff?.();
|
|
72665
|
-
this._managerOff = undefined;
|
|
72666
|
-
this._idMap.forEach((loadingItem)=>{
|
|
72667
|
-
this._disposeLoadingVFXItem(loadingItem.loadingVFXItem);
|
|
72668
|
-
});
|
|
72669
|
-
this._idMap.clear();
|
|
72670
|
-
super.dispose();
|
|
72671
|
-
}
|
|
72672
|
-
/** 将可销毁的渲染投影同步到当前加载状态。 */ _syncManager() {
|
|
72673
|
-
const activeIds = new Set(this._manager.ids);
|
|
72674
|
-
for (const [id, loadingItem] of this._idMap){
|
|
72675
|
-
if (!activeIds.has(id)) {
|
|
72676
|
-
this._disposeLoadingVFXItem(loadingItem.loadingVFXItem);
|
|
72677
|
-
this._idMap.delete(id);
|
|
72678
|
-
}
|
|
72679
|
-
}
|
|
72680
|
-
for (const id of activeIds){
|
|
72681
|
-
const state = this._manager.get(id);
|
|
72682
|
-
if (!state) {
|
|
72683
|
-
continue;
|
|
72684
|
-
}
|
|
72685
|
-
const existing = this._idMap.get(id);
|
|
72686
|
-
if (existing) {
|
|
72687
|
-
existing.tip = {
|
|
72688
|
-
...state.tip,
|
|
72689
|
-
position: state.tip.position ? state.tip.position.clone() : undefined
|
|
72690
|
-
};
|
|
72691
|
-
} else {
|
|
72692
|
-
this._createProjection(id, state);
|
|
72693
|
-
}
|
|
72694
|
-
}
|
|
72695
|
-
}
|
|
72696
|
-
/**
|
|
72697
|
-
* 为加载状态创建渲染投影。
|
|
72698
|
-
* @param id 目标元素 ID。
|
|
72699
|
-
* @param state 加载状态。
|
|
72700
|
-
*/ _createProjection(id, state) {
|
|
72701
|
-
const item = getPlayerItemById(this._owner.getEngine().compositions[0], id);
|
|
72702
|
-
const itemViewBox = item ? getItemViewBox(item, GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement)) : new Box2();
|
|
72703
|
-
const currentBox = state.loadingBox ? this.getViewBoxByBox(state.loadingBox) : itemViewBox;
|
|
72704
|
-
this._idMap.set(id, {
|
|
72705
|
-
loadingBox: getNormalizeBoxByBoxes(itemViewBox, currentBox),
|
|
72706
|
-
loadingVFXItem: this.createLoadingVFXItem(),
|
|
72707
|
-
tip: {
|
|
72708
|
-
...state.tip,
|
|
72709
|
-
position: state.tip.position ? state.tip.position.clone() : undefined
|
|
72710
|
-
}
|
|
72711
|
-
});
|
|
72712
|
-
this.updateLoadingVFXItemTransform(id, this._idMap.get(id));
|
|
72713
|
-
}
|
|
72714
|
-
/** @returns 当前相机信息。 */ getCameraInfo() {
|
|
72715
|
-
return GizmoViewportUtils.getCameraInfo(this._owner.getEngine());
|
|
72716
|
-
}
|
|
72717
|
-
/** @returns 当前视口的缩放、平移与尺寸。 */ getViewportParams() {
|
|
72718
|
-
const composition = this._owner.getEngine().compositions[0];
|
|
72719
|
-
const camera = composition?.camera;
|
|
72720
|
-
if (camera) {
|
|
72721
|
-
const viewportMatrix = camera.getViewportMatrix();
|
|
72722
|
-
const scale = viewportMatrix.elements[0];
|
|
72723
|
-
const translation = new Vector2(viewportMatrix.elements[12], viewportMatrix.elements[13]);
|
|
72724
|
-
const width = GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement).x;
|
|
72725
|
-
const height = GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement).y;
|
|
72726
|
-
return {
|
|
72727
|
-
scale,
|
|
72728
|
-
translation,
|
|
72729
|
-
width,
|
|
72730
|
-
height
|
|
72731
|
-
};
|
|
72732
|
-
}
|
|
72733
|
-
return {
|
|
72734
|
-
scale: 1,
|
|
72735
|
-
translation: new Vector2(),
|
|
72736
|
-
width: GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement).x,
|
|
72737
|
-
height: GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement).y
|
|
72738
|
-
};
|
|
72739
|
-
}
|
|
72740
|
-
/**
|
|
72741
|
-
* 将视图坐标下的包围盒按当前视口缩放与平移变换为实际渲染包围盒。
|
|
72742
|
-
* @param box 视图坐标下的包围盒
|
|
72743
|
-
* @returns 经视口缩放与平移后的实际渲染包围盒
|
|
72744
|
-
*/ getViewBoxByBox(box) {
|
|
72745
|
-
const { scale, translation, width, height } = this.getViewportParams();
|
|
72746
|
-
const center = new Vector2(width / 2, height / 2);
|
|
72747
|
-
const result = scaleBox(box.clone(), scale, center).translate(translation);
|
|
72748
|
-
return result;
|
|
72641
|
+
* 创建图片元素。
|
|
72642
|
+
* @param composition 目标合成
|
|
72643
|
+
* @param parent 父元素
|
|
72644
|
+
* @param name 元素名称
|
|
72645
|
+
* @returns 新建的图片元素
|
|
72646
|
+
*/ static createSprite(composition, parent = null, name = 'NewSprite') {
|
|
72647
|
+
const vfxItem = VFXItemFactory.createVFXItem(composition, parent, name, SpriteComponent);
|
|
72648
|
+
vfxItem.type = index$1.ItemType.sprite;
|
|
72649
|
+
return vfxItem;
|
|
72749
72650
|
}
|
|
72750
72651
|
/**
|
|
72751
|
-
*
|
|
72752
|
-
* @
|
|
72753
|
-
|
|
72754
|
-
|
|
72755
|
-
|
|
72756
|
-
|
|
72757
|
-
|
|
72758
|
-
|
|
72759
|
-
|
|
72760
|
-
loadingVFXItem.type = index$1.ItemType.effect;
|
|
72761
|
-
const effects = loadingVFXItem.getComponent(EffectComponent);
|
|
72762
|
-
const engine = this._owner.getEngine();
|
|
72763
|
-
const geometry = Geometry.create(engine, {
|
|
72764
|
-
attributes: {
|
|
72765
|
-
aPos: {
|
|
72766
|
-
size: 3,
|
|
72767
|
-
data: new Float32Array([
|
|
72768
|
-
-0.5,
|
|
72769
|
-
-0.5,
|
|
72770
|
-
0,
|
|
72771
|
-
0.5,
|
|
72772
|
-
-0.5,
|
|
72773
|
-
0,
|
|
72774
|
-
0.5,
|
|
72775
|
-
0.5,
|
|
72776
|
-
0,
|
|
72777
|
-
-0.5,
|
|
72778
|
-
0.5,
|
|
72779
|
-
0
|
|
72780
|
-
])
|
|
72781
|
-
},
|
|
72782
|
-
aUV: {
|
|
72783
|
-
size: 2,
|
|
72784
|
-
data: new Float32Array([
|
|
72785
|
-
0,
|
|
72786
|
-
0,
|
|
72787
|
-
1,
|
|
72788
|
-
0,
|
|
72789
|
-
1,
|
|
72790
|
-
1,
|
|
72791
|
-
0,
|
|
72792
|
-
1
|
|
72793
|
-
])
|
|
72794
|
-
}
|
|
72795
|
-
},
|
|
72796
|
-
indices: {
|
|
72797
|
-
data: new Uint16Array([
|
|
72798
|
-
0,
|
|
72799
|
-
1,
|
|
72800
|
-
2,
|
|
72801
|
-
0,
|
|
72802
|
-
2,
|
|
72803
|
-
3
|
|
72804
|
-
])
|
|
72805
|
-
},
|
|
72806
|
-
mode: glContext.TRIANGLES,
|
|
72807
|
-
drawCount: 6
|
|
72808
|
-
});
|
|
72809
|
-
// 步骤 2:使用当前配置创建动画材质。
|
|
72810
|
-
const material = Material.create(engine, {
|
|
72811
|
-
shader: {
|
|
72812
|
-
vertex: `
|
|
72813
|
-
precision highp float;
|
|
72814
|
-
attribute vec3 aPos;
|
|
72815
|
-
attribute vec2 aUV;
|
|
72816
|
-
uniform mat4 effects_MatrixVP;
|
|
72817
|
-
uniform mat4 effects_ObjectToWorld;
|
|
72818
|
-
varying vec2 vUV;
|
|
72819
|
-
void main() {
|
|
72820
|
-
vUV = aUV;
|
|
72821
|
-
gl_Position = effects_MatrixVP * effects_ObjectToWorld * vec4(aPos, 1.0);
|
|
72822
|
-
}`,
|
|
72823
|
-
fragment: this.config.loadingFragment
|
|
72824
|
-
}
|
|
72825
|
-
});
|
|
72826
|
-
// 步骤 3:绑定运行时几何与材质。
|
|
72827
|
-
// @ts-expect-error Effects 类型未暴露可写 geometry。
|
|
72828
|
-
effects.geometry = geometry;
|
|
72829
|
-
effects.material = material;
|
|
72830
|
-
return loadingVFXItem;
|
|
72831
|
-
}
|
|
72832
|
-
/** 释放 loading 渲染对象,并同步恢复 Selection 的命中过滤。 */ _disposeLoadingVFXItem(item) {
|
|
72833
|
-
this._owner.getSelection().deleteIgnoreIds([
|
|
72834
|
-
item.getInstanceId()
|
|
72835
|
-
]);
|
|
72836
|
-
item.dispose();
|
|
72652
|
+
* 创建文本元素。
|
|
72653
|
+
* @param composition 目标合成
|
|
72654
|
+
* @param parent 父元素
|
|
72655
|
+
* @param name 元素名称
|
|
72656
|
+
* @returns 新建的文本元素
|
|
72657
|
+
*/ static createText(composition, parent = null, name = 'NewText') {
|
|
72658
|
+
const vfxItem = VFXItemFactory.createVFXItem(composition, parent, name, TextComponent);
|
|
72659
|
+
vfxItem.type = index$1.ItemType.text;
|
|
72660
|
+
return vfxItem;
|
|
72837
72661
|
}
|
|
72838
72662
|
/**
|
|
72839
|
-
*
|
|
72840
|
-
* @param
|
|
72841
|
-
* @param
|
|
72842
|
-
|
|
72843
|
-
|
|
72844
|
-
|
|
72845
|
-
const
|
|
72846
|
-
|
|
72847
|
-
|
|
72848
|
-
const cameraInfo = this.getCameraInfo();
|
|
72849
|
-
const interactionPlane = new Plane(0, new Vector3(0, 0, 1));
|
|
72850
|
-
const worldPosition = viewPositionToWorld(viewPosition, cameraInfo, interactionPlane, containerSize);
|
|
72851
|
-
const viewSize = new Vector2(currentBox.max.x - currentBox.min.x, currentBox.max.y - currentBox.min.y);
|
|
72852
|
-
const worldSize = viewSizeToWorld(viewSize, containerSize, loadingItem.loadingVFXItem, cameraInfo, interactionPlane);
|
|
72853
|
-
if (worldPosition) {
|
|
72854
|
-
loadingItem.loadingVFXItem.transform.setPosition(worldPosition.x, worldPosition.y, 0);
|
|
72855
|
-
}
|
|
72856
|
-
loadingItem.loadingVFXItem.transform.setScale(worldSize.x, worldSize.y, 1);
|
|
72663
|
+
* 创建视频元素。
|
|
72664
|
+
* @param composition 目标合成
|
|
72665
|
+
* @param parent 父元素
|
|
72666
|
+
* @param name 元素名称
|
|
72667
|
+
* @returns 新建的视频元素
|
|
72668
|
+
*/ static createVideo(composition, parent = null, name = 'NewVideo') {
|
|
72669
|
+
const vfxItem = VFXItemFactory.createVFXItem(composition, parent, name, VideoComponent);
|
|
72670
|
+
vfxItem.type = index$1.ItemType.video;
|
|
72671
|
+
return vfxItem;
|
|
72857
72672
|
}
|
|
72858
72673
|
/**
|
|
72859
|
-
*
|
|
72860
|
-
* @param
|
|
72861
|
-
* @param
|
|
72862
|
-
|
|
72863
|
-
|
|
72864
|
-
|
|
72865
|
-
|
|
72866
|
-
|
|
72867
|
-
|
|
72868
|
-
|
|
72869
|
-
|
|
72870
|
-
|
|
72871
|
-
|
|
72872
|
-
|
|
72873
|
-
|
|
72874
|
-
|
|
72875
|
-
|
|
72876
|
-
|
|
72877
|
-
scrollWheelZoom: false,
|
|
72878
|
-
invertZoom: false,
|
|
72879
|
-
zoomStep: 0.1
|
|
72880
|
-
}
|
|
72881
|
-
});
|
|
72882
|
-
const viewportOverlayConfig = defineConfig({
|
|
72883
|
-
id: 'viewport.overlay',
|
|
72884
|
-
defaults: {
|
|
72885
|
-
boxColor: 0xFF0000,
|
|
72886
|
-
boxWidth: 1,
|
|
72887
|
-
outerMaskEnabled: true,
|
|
72888
|
-
markColor: 0x000000,
|
|
72889
|
-
markAlpha: 0.17,
|
|
72890
|
-
safeAreaEnabled: true,
|
|
72891
|
-
safeAreaBoxColor: 0x00FF00,
|
|
72892
|
-
safeAreaBoxAlpha: 0.3
|
|
72893
|
-
}
|
|
72894
|
-
});
|
|
72895
|
-
const selectionPreviewConfig = defineConfig({
|
|
72896
|
-
id: 'selection.preview',
|
|
72897
|
-
defaults: {
|
|
72898
|
-
videoPreSelectedPlay: true,
|
|
72899
|
-
preSelectedColor: 0x3b82f6,
|
|
72900
|
-
preSelectedWidth: 2,
|
|
72901
|
-
regionBoxColor: 0x3b82f6,
|
|
72902
|
-
regionBoxAlpha: 0.17,
|
|
72903
|
-
regionWireframeColor: 0x3b82f6,
|
|
72904
|
-
regionWireframeAlpha: 0.78,
|
|
72905
|
-
regionWireframeWidth: 1
|
|
72906
|
-
}
|
|
72907
|
-
});
|
|
72908
|
-
const selectionSnapConfig = defineConfig({
|
|
72909
|
-
id: 'selection.snap',
|
|
72910
|
-
defaults: {
|
|
72911
|
-
enabled: true,
|
|
72912
|
-
lineWidth: 0.8,
|
|
72913
|
-
lineColor: 0x0BD6FF,
|
|
72914
|
-
distance: 6
|
|
72915
|
-
}
|
|
72916
|
-
});
|
|
72917
|
-
const resizeSelectionConfig = defineConfig({
|
|
72918
|
-
id: 'resize-selection',
|
|
72919
|
-
defaults: {
|
|
72920
|
-
pixelRatio: 1,
|
|
72921
|
-
contentRatio: 1,
|
|
72922
|
-
wireframeColor: 0x3b82f6,
|
|
72923
|
-
wireframeAlpha: 1,
|
|
72924
|
-
wireframeWidth: 1.5,
|
|
72925
|
-
cornerFillColor: 0xFFFFFF,
|
|
72926
|
-
cornerLineColor: 0x3b82f6,
|
|
72927
|
-
cornerLineWidth: 1.5,
|
|
72928
|
-
cornerLineAlpha: 1,
|
|
72929
|
-
scaleCircleSize: 4,
|
|
72930
|
-
rotationCircleSize: 7,
|
|
72931
|
-
infoShowEnabled: true,
|
|
72932
|
-
sizeTextColor: 0x666666,
|
|
72933
|
-
nameTextColor: 0x666666,
|
|
72934
|
-
frameMoveLineColor: 0x3b82f6,
|
|
72935
|
-
frameMoveLineWidth: 2,
|
|
72936
|
-
pictureLogoUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*F2wVS7x0MfIAAAAAQBAAAAgAev-aAQ/original',
|
|
72937
|
-
groupLogoUrl: 'https://mdn.alipayobjects.com/huamei_ppzin5/afts/img/Yo69Sr7boqYAAAAAH3AAAAgADjdkAQFr/original',
|
|
72938
|
-
textLogoUrl: 'https://mdn.alipayobjects.com/huamei_ppzin5/afts/img/Yo69Sr7boqYAAAAAH3AAAAgADjdkAQFr/original',
|
|
72939
|
-
videoLogoUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*w1fnS4mq0VgAAAAAQCAAAAgAev-aAQ/original',
|
|
72940
|
-
frameLogoUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*DRF_RpndkjUAAAAAQDAAAAgAev-aAQ/original',
|
|
72941
|
-
effectsLogoUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*RMewR4ruUnYAAAAAQGAAAAgAev-aAQ/original'
|
|
72942
|
-
}
|
|
72943
|
-
});
|
|
72944
|
-
const pictureCutConfig = defineConfig({
|
|
72945
|
-
id: 'tool.picture-cut',
|
|
72946
|
-
defaults: {
|
|
72947
|
-
maskColor: 0xFFFFFF,
|
|
72948
|
-
maskAlpha: 0.5,
|
|
72949
|
-
cutBoxLineWidth: 2,
|
|
72950
|
-
cutBoxLineColor: 0x6A34FF,
|
|
72951
|
-
cutBoxLineAlpha: 1,
|
|
72952
|
-
itemBoxLineWidth: 1,
|
|
72953
|
-
itemBoxLineColor: 0x6A34FF,
|
|
72954
|
-
itemBoxLineAlpha: 1,
|
|
72955
|
-
cutBoxCornerRadius: 5,
|
|
72956
|
-
cutBoxCornerFillColor: 0xFFFFFF,
|
|
72957
|
-
cutBoxCornerLineWidth: 2,
|
|
72958
|
-
cutBoxCornerLineColor: 0x6A34FF,
|
|
72959
|
-
cutBoxCornerLineAlpha: 1,
|
|
72960
|
-
scaleInteractionDistance: 8,
|
|
72961
|
-
directionScaleInteractionDistance: 5,
|
|
72962
|
-
gridLineWidth: 1,
|
|
72963
|
-
gridLineColor: 0xFFFFFF,
|
|
72964
|
-
gridLineAlpha: 1,
|
|
72965
|
-
gridCount: 2
|
|
72966
|
-
}
|
|
72967
|
-
});
|
|
72968
|
-
const pictureExpandConfig = defineConfig({
|
|
72969
|
-
id: 'tool.picture-expand',
|
|
72970
|
-
defaults: {
|
|
72971
|
-
maskColor: 0x6A34FF,
|
|
72972
|
-
maskAlpha: 0.2,
|
|
72973
|
-
expandBoxLineWidth: 2,
|
|
72974
|
-
expandBoxLineColor: 0x6A34FF,
|
|
72975
|
-
expandBoxLineAlpha: 1,
|
|
72976
|
-
expandBoxCornerRadius: 5,
|
|
72977
|
-
expandBoxCornerLineWidth: 2,
|
|
72978
|
-
expandBoxCornerLineColor: 0x6A34FF,
|
|
72979
|
-
expandBoxCornerLineAlpha: 1,
|
|
72980
|
-
expandBoxCornerFillColor: 0xFFFFFF,
|
|
72981
|
-
scaleInteractionDistance: 8,
|
|
72982
|
-
directionScaleInteractionDistance: 5,
|
|
72983
|
-
gridLineWidth: 1,
|
|
72984
|
-
gridLineColor: 0xFFFFFF,
|
|
72985
|
-
gridLineAlpha: 1,
|
|
72986
|
-
gridCount: 2
|
|
72987
|
-
}
|
|
72988
|
-
});
|
|
72989
|
-
const maskConfig = defineConfig({
|
|
72990
|
-
id: 'tool.mask',
|
|
72991
|
-
defaults: {
|
|
72992
|
-
maskImage: '',
|
|
72993
|
-
brushSize: 20,
|
|
72994
|
-
brushColor: 0x6A34FF,
|
|
72995
|
-
brushAlpha: 0.5,
|
|
72996
|
-
maskColor: 0x00FF00,
|
|
72997
|
-
maskBackgroundColor: 0xFFFFFF,
|
|
72998
|
-
maskAlpha: 1,
|
|
72999
|
-
boxLineWidth: 1,
|
|
73000
|
-
boxLineColor: 0x6A34FF,
|
|
73001
|
-
boxLineAlpha: 1
|
|
73002
|
-
}
|
|
73003
|
-
});
|
|
73004
|
-
const spriteTextEditConfig = defineConfig({
|
|
73005
|
-
id: 'tool.sprite-text-edit',
|
|
73006
|
-
defaults: {
|
|
73007
|
-
textColor: 0xFFFFFF,
|
|
73008
|
-
preSelectedTextColor: 0xFFFFFF,
|
|
73009
|
-
boxLineWidth: 3,
|
|
73010
|
-
dashLineDash: 8,
|
|
73011
|
-
dashLineGap: 8,
|
|
73012
|
-
editBoxAlpha: 0.15,
|
|
73013
|
-
editBoxColor: 0x3B82F6,
|
|
73014
|
-
editBoxLineAlpha: 1,
|
|
73015
|
-
editBoxLineColor: 0x3B82F6,
|
|
73016
|
-
editBoxPreSelectedAlpha: 0.25,
|
|
73017
|
-
editBoxPreSelectedColor: 0x3B82F6,
|
|
73018
|
-
editBoxLinePreSelectedAlpha: 1,
|
|
73019
|
-
editBoxLinePreSelectedColor: 0x3B82F6,
|
|
73020
|
-
hasChangedEditBoxAlpha: 0.2,
|
|
73021
|
-
hasChangedEditBoxColor: 0x22C55E,
|
|
73022
|
-
hasChangedEditBoxLineAlpha: 1,
|
|
73023
|
-
hasChangedEditBoxLineColor: 0x22C55E,
|
|
73024
|
-
hasChangedEditBoxPreSelectedAlpha: 0.3,
|
|
73025
|
-
hasChangedEditBoxPreSelectedColor: 0x22C55E,
|
|
73026
|
-
hasChangedEditBoxLinePreSelectedAlpha: 1,
|
|
73027
|
-
hasChangedEditBoxLinePreSelectedColor: 0x22C55E,
|
|
73028
|
-
editBoxSelectedAlpha: 0.6,
|
|
73029
|
-
editBoxSelectedColor: 0xFFFF00
|
|
73030
|
-
}
|
|
73031
|
-
});
|
|
73032
|
-
const iconConfig = defineConfig({
|
|
73033
|
-
id: 'feedback.icon',
|
|
73034
|
-
defaults: {
|
|
73035
|
-
autoShow: true,
|
|
73036
|
-
videoPlayUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*ORMmSYYHIHUAAAAAJbAAAAgAev-aAQ/original',
|
|
73037
|
-
videoPlayShift: [
|
|
73038
|
-
20,
|
|
73039
|
-
20
|
|
73040
|
-
],
|
|
73041
|
-
videoPlayWidth: 20,
|
|
73042
|
-
videoPlayHeight: 20,
|
|
73043
|
-
imageGeneratorUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*bYB-TIEWLBkAAAAAQGAAAAgAev-aAQ/original',
|
|
73044
|
-
videoGeneratorUrl: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*6cTFT44CuKEAAAAAQCAAAAgAev-aAQ/original',
|
|
73045
|
-
generatorWidth: 200,
|
|
73046
|
-
generatorHeight: 200
|
|
73047
|
-
}
|
|
73048
|
-
});
|
|
73049
|
-
const itemCreateConfig = defineConfig({
|
|
73050
|
-
id: 'tool.item-create',
|
|
73051
|
-
defaults: {
|
|
73052
|
-
frameBorderColor: 0x2178FF,
|
|
73053
|
-
frameBorderWidth: 1,
|
|
73054
|
-
frameBorderAlpha: 0.8,
|
|
73055
|
-
frameFillColor: 0x2178FF,
|
|
73056
|
-
frameFillAlpha: 0.15,
|
|
73057
|
-
frameChildBoxAlpha: 0.35,
|
|
73058
|
-
frameChildBoxColor: 0x2178FF
|
|
72674
|
+
* 加载并创建特效合成元素。
|
|
72675
|
+
* @param composition 目标合成
|
|
72676
|
+
* @param effects 特效地址或场景数据
|
|
72677
|
+
* @param parent 父元素
|
|
72678
|
+
* @param urlname 元素名称
|
|
72679
|
+
* @returns 新建的特效元素
|
|
72680
|
+
*/ static async createEffects(composition, effects, parent = null, urlname = 'NewEffects') {
|
|
72681
|
+
const preComposition = await AssetManager$1.loadPrecomposition(effects, {
|
|
72682
|
+
autoplay: false
|
|
72683
|
+
});
|
|
72684
|
+
const vfxItem = PrecompositionManager.instantiate(preComposition, composition);
|
|
72685
|
+
vfxItem.name = urlname;
|
|
72686
|
+
vfxItem.type = index$1.ItemType.composition;
|
|
72687
|
+
vfxItem.getComponent(CompositionComponent).endBehavior = vfxItem.endBehavior;
|
|
72688
|
+
if (parent) {
|
|
72689
|
+
vfxItem.setParent(parent);
|
|
72690
|
+
}
|
|
72691
|
+
return vfxItem;
|
|
73059
72692
|
}
|
|
73060
|
-
}
|
|
72693
|
+
}
|
|
73061
72694
|
/**
|
|
73062
|
-
*
|
|
72695
|
+
* 播放视频元素。
|
|
73063
72696
|
* @param engine 引擎
|
|
73064
|
-
* @param
|
|
73065
|
-
|
|
73066
|
-
|
|
73067
|
-
|
|
73068
|
-
if (!composition) {
|
|
72697
|
+
* @param id 视频元素 id
|
|
72698
|
+
*/ function playVideoItem(engine, id) {
|
|
72699
|
+
const playerItem = getPlayerItemById(engine.compositions[0], id);
|
|
72700
|
+
if (playerItem?.type !== index$1.ItemType.video) {
|
|
73069
72701
|
return;
|
|
73070
72702
|
}
|
|
73071
|
-
const
|
|
73072
|
-
|
|
73073
|
-
const translation = new Vector2(camera.getViewportMatrix().elements[12], camera.getViewportMatrix().elements[13]);
|
|
73074
|
-
const result = scale + zoom;
|
|
73075
|
-
// 1. 反算缩放中心对应的世界坐标。
|
|
73076
|
-
const worldX = (center.x - translation.x) / scale;
|
|
73077
|
-
const worldY = (center.y - translation.y) / scale;
|
|
73078
|
-
// 2. 调整平移,使缩放中心在屏幕上的位置保持不变。
|
|
73079
|
-
const newNDCTranslation = new Vector2(center.x - worldX * result, center.y - worldY * result);
|
|
73080
|
-
const viewportMatrix = new Matrix4().compose(new Vector3(newNDCTranslation.x, newNDCTranslation.y, 0), new Quaternion(), new Vector3(result, result, 1));
|
|
73081
|
-
composition.camera.setViewportMatrix(viewportMatrix);
|
|
72703
|
+
const videoComponent = playerItem.getComponent(VideoComponent);
|
|
72704
|
+
videoComponent?.playVideo();
|
|
73082
72705
|
}
|
|
73083
72706
|
/**
|
|
73084
|
-
*
|
|
72707
|
+
* 暂停视频元素。
|
|
73085
72708
|
* @param engine 引擎
|
|
73086
|
-
* @param
|
|
73087
|
-
*/ function
|
|
73088
|
-
const
|
|
73089
|
-
if (
|
|
72709
|
+
* @param id 视频元素 id
|
|
72710
|
+
*/ function pauseVideoItem(engine, id) {
|
|
72711
|
+
const playerItem = getPlayerItemById(engine.compositions[0], id);
|
|
72712
|
+
if (playerItem?.type !== index$1.ItemType.video) {
|
|
73090
72713
|
return;
|
|
73091
72714
|
}
|
|
73092
|
-
const
|
|
73093
|
-
|
|
73094
|
-
const resultTranslation = new Vector2(camera.getViewportMatrix().elements[12], camera.getViewportMatrix().elements[13]).add(translation);
|
|
73095
|
-
const viewportMatrix = new Matrix4().compose(new Vector3(resultTranslation.x, resultTranslation.y, 0), new Quaternion(), new Vector3(scale, scale, 1));
|
|
73096
|
-
camera.setViewportMatrix(viewportMatrix);
|
|
72715
|
+
const videoComponent = playerItem.getComponent(VideoComponent);
|
|
72716
|
+
videoComponent?.pauseVideo();
|
|
73097
72717
|
}
|
|
73098
72718
|
/**
|
|
73099
|
-
*
|
|
73100
|
-
* @param
|
|
73101
|
-
* @param
|
|
73102
|
-
|
|
73103
|
-
|
|
73104
|
-
|
|
73105
|
-
if (
|
|
73106
|
-
console.warn(`Item ${frameItem.getInstanceId()} is not a frame item.`);
|
|
72719
|
+
* 播放特效元素。
|
|
72720
|
+
* @param engine 引擎
|
|
72721
|
+
* @param id 特效元素 id
|
|
72722
|
+
*/ function playEffectsItem(engine, id) {
|
|
72723
|
+
const controlItem = getPlayerItemById(engine.compositions[0], id);
|
|
72724
|
+
const compositionItem = controlItem?.children?.[0];
|
|
72725
|
+
if (compositionItem?.type !== index$1.ItemType.composition) {
|
|
73107
72726
|
return;
|
|
73108
72727
|
}
|
|
73109
|
-
|
|
73110
|
-
|
|
73111
|
-
|
|
73112
|
-
|
|
73113
|
-
|
|
73114
|
-
|
|
73115
|
-
|
|
73116
|
-
|
|
73117
|
-
|
|
73118
|
-
|
|
72728
|
+
compositionItem.getComponent(CompositionComponent).play();
|
|
72729
|
+
}
|
|
72730
|
+
/**
|
|
72731
|
+
* 暂停特效元素。
|
|
72732
|
+
* @param engine 引擎
|
|
72733
|
+
* @param id 特效元素 id
|
|
72734
|
+
*/ function pauseEffectsItem(engine, id) {
|
|
72735
|
+
const controlItem = getPlayerItemById(engine.compositions[0], id);
|
|
72736
|
+
const compositionItem = controlItem?.children?.[0];
|
|
72737
|
+
if (compositionItem?.type !== index$1.ItemType.composition) {
|
|
72738
|
+
return;
|
|
73119
72739
|
}
|
|
73120
|
-
|
|
73121
|
-
|
|
73122
|
-
|
|
73123
|
-
|
|
73124
|
-
|
|
73125
|
-
|
|
73126
|
-
|
|
73127
|
-
|
|
73128
|
-
|
|
73129
|
-
|
|
72740
|
+
compositionItem.getComponent(CompositionComponent).pause();
|
|
72741
|
+
}
|
|
72742
|
+
/** 信息标签默认字号。 */ const INFO_TEXT_FONT_SIZE = 14;
|
|
72743
|
+
/** 信息标签默认字体。 */ const INFO_TEXT_FONT_FAMILY = 'sans-serif';
|
|
72744
|
+
/** effects Control 字符 atlas 的固定单边留白。 */ const CONTROL_TEXT_GLYPH_PADDING = 4;
|
|
72745
|
+
/** 用于测量字体高度的代表字符。 */ const METRICS_STRING = '|ÉqÅ';
|
|
72746
|
+
/** 用于测量字体基线的字符。 */ const BASELINE_SYMBOL = 'M';
|
|
72747
|
+
/**
|
|
72748
|
+
* 颜色缓存:`hex_alpha` → effects Color。键到颜色映射不可变,全局缓存无需清理。
|
|
72749
|
+
*/ const colorCache = new Map();
|
|
72750
|
+
/**
|
|
72751
|
+
* 将十六进制色值 + alpha 转为 effects Color(带全局缓存)。
|
|
72752
|
+
* @param hex 0xRRGGBB 颜色
|
|
72753
|
+
* @param alpha 透明度 0..1
|
|
72754
|
+
* @returns effects Color
|
|
72755
|
+
*/ function toColor(hex, alpha = 1) {
|
|
72756
|
+
const key = `${hex}_${alpha}`;
|
|
72757
|
+
const cached = colorCache.get(key);
|
|
72758
|
+
if (cached) {
|
|
72759
|
+
return cached;
|
|
73130
72760
|
}
|
|
72761
|
+
const color = new Color((hex >> 16 & 0xff) / 255, (hex >> 8 & 0xff) / 255, (hex & 0xff) / 255, alpha);
|
|
72762
|
+
colorCache.set(key, color);
|
|
72763
|
+
return color;
|
|
73131
72764
|
}
|
|
73132
|
-
/**
|
|
73133
|
-
|
|
73134
|
-
|
|
72765
|
+
/**
|
|
72766
|
+
* 离屏文本测量上下文,懒加载并复用。
|
|
72767
|
+
*/ let _measureCtx = null;
|
|
72768
|
+
/**
|
|
72769
|
+
* 测量文本的宽度与可见字形边界。
|
|
72770
|
+
* @param text 文本内容
|
|
72771
|
+
* @param fontSize 字号
|
|
72772
|
+
* @param fontFamily 字体
|
|
72773
|
+
* @param fontWeight 字重
|
|
72774
|
+
* @param fontStyle 字体样式
|
|
72775
|
+
* @returns 文本测量结果
|
|
72776
|
+
*/ function measureTextMetrics(text, fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY, fontWeight, fontStyle) {
|
|
72777
|
+
_measureCtx ?? (_measureCtx = document.createElement('canvas').getContext('2d'));
|
|
72778
|
+
if (!_measureCtx) {
|
|
72779
|
+
return {
|
|
72780
|
+
width: 0,
|
|
72781
|
+
actualBoundingBoxAscent: 0,
|
|
72782
|
+
actualBoundingBoxDescent: 0
|
|
72783
|
+
};
|
|
73135
72784
|
}
|
|
73136
|
-
|
|
73137
|
-
|
|
72785
|
+
_measureCtx.font = `${''}${fontWeight ? `${fontWeight} ` : ''}${fontSize}px ${fontFamily}`;
|
|
72786
|
+
const metrics = _measureCtx.measureText(text);
|
|
72787
|
+
return {
|
|
72788
|
+
width: metrics.width,
|
|
72789
|
+
actualBoundingBoxAscent: metrics.actualBoundingBoxAscent ?? 0,
|
|
72790
|
+
actualBoundingBoxDescent: metrics.actualBoundingBoxDescent ?? 0
|
|
72791
|
+
};
|
|
72792
|
+
}
|
|
72793
|
+
/**
|
|
72794
|
+
* 计算 effects Control.drawText 实际使用的 atlas cell 高度。
|
|
72795
|
+
* @param fontSize 字号
|
|
72796
|
+
* @param fontFamily 字体
|
|
72797
|
+
* @param fontWeight 字重
|
|
72798
|
+
* @param fontStyle 字体样式
|
|
72799
|
+
* @param resolution 渲染分辨率
|
|
72800
|
+
* @returns 字符图集单元格高度
|
|
72801
|
+
*/ function measureControlTextCellHeight(fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY, fontWeight, fontStyle, resolution = 1) {
|
|
72802
|
+
const safeResolution = Number.isFinite(resolution) && resolution > 0 ? resolution : 1;
|
|
72803
|
+
const scaledFontSize = fontSize * safeResolution;
|
|
72804
|
+
const metrics = measureTextMetrics(METRICS_STRING + BASELINE_SYMBOL, scaledFontSize, fontFamily, fontWeight);
|
|
72805
|
+
const ascent = metrics.actualBoundingBoxAscent || scaledFontSize * 0.8;
|
|
72806
|
+
const descent = metrics.actualBoundingBoxDescent || scaledFontSize * 0.2;
|
|
72807
|
+
return Math.ceil(ascent + descent + CONTROL_TEXT_GLYPH_PADDING * 2 * safeResolution) / safeResolution;
|
|
72808
|
+
}
|
|
72809
|
+
/**
|
|
72810
|
+
* 测量文本宽度(逻辑像素),用于信息标签的溢出判断与右对齐定位。
|
|
72811
|
+
* @param text 文本内容
|
|
72812
|
+
* @param fontSize 字号
|
|
72813
|
+
* @param fontFamily 字体
|
|
72814
|
+
* @param fontWeight 字重
|
|
72815
|
+
* @returns 文本宽度
|
|
72816
|
+
*/ function measureTextWidth(text, fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY, fontWeight) {
|
|
72817
|
+
return measureTextMetrics(text, fontSize, fontFamily, fontWeight).width;
|
|
72818
|
+
}
|
|
72819
|
+
/**
|
|
72820
|
+
* 单行文本截断:逐字符累加,超出可用宽度时把最后两个字符替换为省略号。
|
|
72821
|
+
* @param text 文本内容
|
|
72822
|
+
* @param maxWidth 可用宽度
|
|
72823
|
+
* @param fontSize 字号
|
|
72824
|
+
* @param fontFamily 字体
|
|
72825
|
+
* @returns 截断后的文本
|
|
72826
|
+
*/ function truncateText(text, maxWidth, fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY) {
|
|
72827
|
+
if (measureTextWidth(text, fontSize, fontFamily) <= maxWidth) {
|
|
72828
|
+
return text;
|
|
73138
72829
|
}
|
|
73139
|
-
|
|
73140
|
-
|
|
73141
|
-
|
|
73142
|
-
|
|
73143
|
-
*/ setScaleRange(minScale, maxScale) {
|
|
73144
|
-
if (!Number.isFinite(minScale) || !Number.isFinite(maxScale) || minScale <= 0 || maxScale <= 0 || minScale > maxScale) {
|
|
73145
|
-
throw new RangeError('Viewport scale range must be finite, positive, and ordered.');
|
|
72830
|
+
let current = '';
|
|
72831
|
+
for (const char of text){
|
|
72832
|
+
if (measureTextWidth(current + char, fontSize, fontFamily) > maxWidth) {
|
|
72833
|
+
return current.length > 2 ? current.slice(0, -2) + '...' : '...';
|
|
73146
72834
|
}
|
|
73147
|
-
|
|
73148
|
-
this._maxScale = maxScale;
|
|
72835
|
+
current += char;
|
|
73149
72836
|
}
|
|
73150
|
-
|
|
73151
|
-
|
|
73152
|
-
|
|
73153
|
-
|
|
73154
|
-
|
|
73155
|
-
|
|
73156
|
-
|
|
73157
|
-
|
|
73158
|
-
|
|
73159
|
-
|
|
73160
|
-
|
|
73161
|
-
|
|
73162
|
-
|
|
73163
|
-
|
|
73164
|
-
|
|
73165
|
-
const
|
|
73166
|
-
|
|
73167
|
-
|
|
73168
|
-
|
|
72837
|
+
return current;
|
|
72838
|
+
}
|
|
72839
|
+
/**
|
|
72840
|
+
* 多行逐字符折行:行宽超出 maxWidth 时换行;行数达到 maxLines 时末行用省略号收尾。
|
|
72841
|
+
* @param text 文本内容
|
|
72842
|
+
* @param maxWidth 单行可用宽度
|
|
72843
|
+
* @param maxLines 最大行数
|
|
72844
|
+
* @param fontSize 字号
|
|
72845
|
+
* @param fontFamily 字体
|
|
72846
|
+
* @returns 折行后的文本行数组
|
|
72847
|
+
*/ function wrapText(text, maxWidth, maxLines, fontSize = INFO_TEXT_FONT_SIZE, fontFamily = INFO_TEXT_FONT_FAMILY) {
|
|
72848
|
+
const lines = [];
|
|
72849
|
+
let currentLine = '';
|
|
72850
|
+
const chars = text.split('');
|
|
72851
|
+
for(let i = 0; i < chars.length; i++){
|
|
72852
|
+
const char = chars[i];
|
|
72853
|
+
const testLine = currentLine + char;
|
|
72854
|
+
if (measureTextWidth(testLine, fontSize, fontFamily) > maxWidth) {
|
|
72855
|
+
lines.push(currentLine);
|
|
72856
|
+
if (lines.length >= maxLines) {
|
|
72857
|
+
const lastLine = lines[maxLines - 1];
|
|
72858
|
+
lines[maxLines - 1] = lastLine.length > 2 ? lastLine.slice(0, -2) + '...' : '...';
|
|
72859
|
+
break;
|
|
73169
72860
|
}
|
|
73170
|
-
|
|
73171
|
-
|
|
73172
|
-
|
|
73173
|
-
return this._zoomByShift(zoomShift, center, event.ctrlPressed && source === 'trackpad' ? 'pinch-zoom' : 'wheel-zoom');
|
|
73174
|
-
}
|
|
73175
|
-
if (delta.x === 0 && delta.y === 0) {
|
|
73176
|
-
return false;
|
|
73177
|
-
}
|
|
73178
|
-
return this.panByViewDelta(delta, center, 'wheel-pan');
|
|
73179
|
-
}
|
|
73180
|
-
/**
|
|
73181
|
-
* 按视图像素增量平移视口。
|
|
73182
|
-
* @param delta 视图像素位移
|
|
73183
|
-
* @param center 操作中心
|
|
73184
|
-
* @param source 操作来源
|
|
73185
|
-
* @returns 视口是否发生变化
|
|
73186
|
-
*/ panByViewDelta(delta, center, source) {
|
|
73187
|
-
if (!this._hasViewport() || delta.x === 0 && delta.y === 0) {
|
|
73188
|
-
return false;
|
|
72861
|
+
currentLine = char;
|
|
72862
|
+
} else {
|
|
72863
|
+
currentLine = testLine;
|
|
73189
72864
|
}
|
|
73190
|
-
|
|
73191
|
-
|
|
73192
|
-
return false;
|
|
72865
|
+
if (i === chars.length - 1 && currentLine) {
|
|
72866
|
+
lines.push(currentLine);
|
|
73193
72867
|
}
|
|
73194
|
-
panView(this._engine, viewSizeToNDC(delta, containerSize));
|
|
73195
|
-
this._emitChange(center, source);
|
|
73196
|
-
return true;
|
|
73197
72868
|
}
|
|
73198
|
-
|
|
73199
|
-
|
|
73200
|
-
|
|
73201
|
-
|
|
73202
|
-
|
|
73203
|
-
|
|
73204
|
-
|
|
73205
|
-
|
|
73206
|
-
|
|
73207
|
-
|
|
73208
|
-
|
|
73209
|
-
const nextScale = clamp(currentScale * factor, this._minScale, this._maxScale);
|
|
73210
|
-
if (nextScale === currentScale) {
|
|
73211
|
-
return false;
|
|
73212
|
-
}
|
|
73213
|
-
const containerSize = this._containerSize();
|
|
73214
|
-
if (containerSize.x <= 0 || containerSize.y <= 0) {
|
|
73215
|
-
return false;
|
|
73216
|
-
}
|
|
73217
|
-
zoomView(this._engine, nextScale - currentScale, viewPositionToNDC(center, containerSize));
|
|
73218
|
-
this._emitChange(center, source);
|
|
73219
|
-
return true;
|
|
72869
|
+
return lines.slice(0, maxLines);
|
|
72870
|
+
}
|
|
72871
|
+
/**
|
|
72872
|
+
* 绘制由 4 个角点构成的(可能非正交的)包围盒边框。
|
|
72873
|
+
* @param control 绘制控制器
|
|
72874
|
+
* @param corners 4 个角点(视图坐标,Y 向下)
|
|
72875
|
+
* @param color 边框颜色
|
|
72876
|
+
* @param width 线宽
|
|
72877
|
+
*/ function drawCorners(control, corners, color, width) {
|
|
72878
|
+
if (corners.length < 4) {
|
|
72879
|
+
return;
|
|
73220
72880
|
}
|
|
73221
|
-
|
|
73222
|
-
|
|
73223
|
-
|
|
73224
|
-
|
|
73225
|
-
this.off('change', listener);
|
|
73226
|
-
}
|
|
72881
|
+
for(let i = 0; i < 4; i++){
|
|
72882
|
+
const start = corners[i];
|
|
72883
|
+
const end = corners[(i + 1) % 4];
|
|
72884
|
+
control.drawLine(start.x, start.y, end.x, end.y, color, width);
|
|
73227
72885
|
}
|
|
73228
|
-
|
|
73229
|
-
|
|
73230
|
-
|
|
73231
|
-
|
|
73232
|
-
|
|
73233
|
-
|
|
73234
|
-
|
|
73235
|
-
|
|
73236
|
-
|
|
73237
|
-
|
|
73238
|
-
|
|
73239
|
-
|
|
72886
|
+
}
|
|
72887
|
+
/**
|
|
72888
|
+
* 绘制虚线包围盒,并在相邻边之间保持连续虚线相位。
|
|
72889
|
+
* @param control 绘制控制器
|
|
72890
|
+
* @param corners 四个视图角点
|
|
72891
|
+
* @param color 虚线颜色
|
|
72892
|
+
* @param width 线宽
|
|
72893
|
+
* @param dashLength 实线段长度
|
|
72894
|
+
* @param gapLength 间隔长度
|
|
72895
|
+
*/ function drawDashedCorners(control, corners, color, width, dashLength, gapLength) {
|
|
72896
|
+
// 1. 校验角点和虚线参数。
|
|
72897
|
+
if (corners.length < 4 || dashLength <= 0 || gapLength < 0) {
|
|
72898
|
+
return;
|
|
73240
72899
|
}
|
|
73241
|
-
|
|
73242
|
-
|
|
73243
|
-
|
|
73244
|
-
|
|
73245
|
-
|
|
73246
|
-
const
|
|
73247
|
-
|
|
73248
|
-
|
|
73249
|
-
|
|
73250
|
-
|
|
73251
|
-
|
|
73252
|
-
delta.y = -event.factor;
|
|
73253
|
-
break;
|
|
73254
|
-
case MouseButton.WheelLeft:
|
|
73255
|
-
delta.x = event.factor;
|
|
73256
|
-
break;
|
|
73257
|
-
case MouseButton.WheelRight:
|
|
73258
|
-
delta.x = -event.factor;
|
|
73259
|
-
break;
|
|
73260
|
-
}
|
|
73261
|
-
// Windows 下 Shift + 滚轮映射为水平平移。
|
|
73262
|
-
const isApplePlatform = typeof navigator !== 'undefined' && /Mac|iPhone|iPad|iPod/.test(navigator.platform);
|
|
73263
|
-
if (!isApplePlatform && event.shiftPressed && delta.x === 0) {
|
|
73264
|
-
delta.x = delta.y;
|
|
73265
|
-
delta.y = 0;
|
|
72900
|
+
const patternLength = dashLength + gapLength;
|
|
72901
|
+
let perimeterOffset = 0;
|
|
72902
|
+
// 2. 逐边计算虚线相位,并继承上一条边的周长偏移。
|
|
72903
|
+
for(let i = 0; i < 4; i++){
|
|
72904
|
+
const start = corners[i];
|
|
72905
|
+
const end = corners[(i + 1) % 4];
|
|
72906
|
+
const dx = end.x - start.x;
|
|
72907
|
+
const dy = end.y - start.y;
|
|
72908
|
+
const edgeLength = Math.hypot(dx, dy);
|
|
72909
|
+
if (edgeLength <= Number.EPSILON) {
|
|
72910
|
+
continue;
|
|
73266
72911
|
}
|
|
73267
|
-
|
|
73268
|
-
|
|
73269
|
-
|
|
73270
|
-
|
|
73271
|
-
|
|
73272
|
-
|
|
73273
|
-
|
|
73274
|
-
|
|
73275
|
-
|
|
73276
|
-
|
|
73277
|
-
|
|
73278
|
-
|
|
73279
|
-
|
|
73280
|
-
|
|
73281
|
-
const nextScale = roundNumber(clamp(currentScale + shift, this._minScale, this._maxScale), 2);
|
|
73282
|
-
if (nextScale === currentScale) {
|
|
73283
|
-
return false;
|
|
72912
|
+
let edgeOffset = 0;
|
|
72913
|
+
while(edgeOffset < edgeLength){
|
|
72914
|
+
const patternOffset = perimeterOffset % patternLength;
|
|
72915
|
+
const drawingDash = gapLength === 0 || patternOffset < dashLength;
|
|
72916
|
+
const phaseRemaining = drawingDash ? dashLength - patternOffset : patternLength - patternOffset;
|
|
72917
|
+
const segmentLength = Math.min(phaseRemaining, edgeLength - edgeOffset);
|
|
72918
|
+
// 3. 仅绘制当前相位中的实线段。
|
|
72919
|
+
if (drawingDash && segmentLength > Number.EPSILON) {
|
|
72920
|
+
const startRatio = edgeOffset / edgeLength;
|
|
72921
|
+
const endRatio = (edgeOffset + segmentLength) / edgeLength;
|
|
72922
|
+
control.drawLine(start.x + dx * startRatio, start.y + dy * startRatio, start.x + dx * endRatio, start.y + dy * endRatio, color, width);
|
|
72923
|
+
}
|
|
72924
|
+
edgeOffset += segmentLength;
|
|
72925
|
+
perimeterOffset += segmentLength;
|
|
73284
72926
|
}
|
|
73285
|
-
return this.zoomByFactor(nextScale / currentScale, center, source);
|
|
73286
|
-
}
|
|
73287
|
-
/**
|
|
73288
|
-
* 发出当前视口快照。
|
|
73289
|
-
* @param center 操作中心
|
|
73290
|
-
* @param source 操作来源
|
|
73291
|
-
*/ _emitChange(center, source) {
|
|
73292
|
-
const containerSize = this._containerSize();
|
|
73293
|
-
this.emit('change', {
|
|
73294
|
-
source,
|
|
73295
|
-
scale: GizmoViewportUtils.getViewScale(this._engine),
|
|
73296
|
-
translation: ndcSizeToViewSize(GizmoViewportUtils.getViewportTranslation(this._engine), containerSize),
|
|
73297
|
-
center: center.clone()
|
|
73298
|
-
});
|
|
73299
|
-
}
|
|
73300
|
-
/** @returns 当前画布容器尺寸。 */ _containerSize() {
|
|
73301
|
-
return GizmoViewportUtils.getContainerSize(this._engine.canvas.parentElement);
|
|
73302
|
-
}
|
|
73303
|
-
/** @returns 是否存在可操作的画布容器和相机。 */ _hasViewport() {
|
|
73304
|
-
return Boolean(this._engine.canvas.parentElement && this._engine.compositions[0]?.camera);
|
|
73305
|
-
}
|
|
73306
|
-
/**
|
|
73307
|
-
* @param owner Gizmo 宿主
|
|
73308
|
-
*/ constructor(owner){
|
|
73309
|
-
super(), _define_property(this, "_owner", void 0), _define_property(this, "_minScale", 0.01), _define_property(this, "_maxScale", 20), _define_property(this, "_configOff", void 0), _define_property(this, "_wheelSignature", ''), _define_property(this, "_wheelMode", 'pan');
|
|
73310
|
-
this._owner = owner;
|
|
73311
|
-
this._configOff = owner.getConfigManager().onChange(viewportNavigationConfig, ()=>{
|
|
73312
|
-
this._wheelSignature = '';
|
|
73313
|
-
});
|
|
73314
72927
|
}
|
|
73315
72928
|
}
|
|
72929
|
+
/**
|
|
72930
|
+
* 绘制包围盒边框(沿 4 个角点连边)。
|
|
72931
|
+
* @param control 绘制控制器
|
|
72932
|
+
* @param box 包围盒(视图坐标,Y 向下)
|
|
72933
|
+
* @param color 边框颜色
|
|
72934
|
+
* @param width 线宽
|
|
72935
|
+
*/ function drawBox(control, box, color, width) {
|
|
72936
|
+
drawCorners(control, box.corners, color, width);
|
|
72937
|
+
}
|
|
72938
|
+
/**
|
|
72939
|
+
* 填充轴对齐包围盒(实心矩形)。
|
|
72940
|
+
* @param control 绘制控制器
|
|
72941
|
+
* @param box 包围盒(视图坐标,Y 向下)
|
|
72942
|
+
* @param color 填充颜色
|
|
72943
|
+
*/ function fillBox(control, box, color) {
|
|
72944
|
+
const size = box.getSize();
|
|
72945
|
+
control.fillRect(box.min.x, box.min.y, size.x, size.y, color);
|
|
72946
|
+
}
|
|
72947
|
+
/**
|
|
72948
|
+
* 构造沿局部轴展开的旋转矩形角点。
|
|
72949
|
+
* @param center 矩形中心(视图坐标)
|
|
72950
|
+
* @param xAxis 局部 X 轴单位向量(视图坐标,宽方向)
|
|
72951
|
+
* @param yAxis 局部 Y 轴单位向量(视图坐标,高方向)
|
|
72952
|
+
* @param halfWidth 半宽(沿 xAxis,视图像素)
|
|
72953
|
+
* @param halfHeight 半高(沿 yAxis,视图像素)
|
|
72954
|
+
* @returns 4 个角点(视图坐标,环绕顺序)
|
|
72955
|
+
*/ function rotatedRectCorners(center, xAxis, yAxis, halfWidth, halfHeight) {
|
|
72956
|
+
const halfW = xAxis.clone().multiply(halfWidth);
|
|
72957
|
+
const halfH = yAxis.clone().multiply(halfHeight);
|
|
72958
|
+
return [
|
|
72959
|
+
center.clone().subtract(halfW).subtract(halfH),
|
|
72960
|
+
center.clone().add(halfW).subtract(halfH),
|
|
72961
|
+
center.clone().add(halfW).add(halfH),
|
|
72962
|
+
center.clone().subtract(halfW).add(halfH)
|
|
72963
|
+
];
|
|
72964
|
+
}
|
|
72965
|
+
/**
|
|
72966
|
+
* 填充沿局部轴展开的旋转矩形。
|
|
72967
|
+
* @param control 绘制控制器
|
|
72968
|
+
* @param center 矩形中心(视图坐标)
|
|
72969
|
+
* @param xAxis 局部 X 轴单位向量(视图坐标,宽方向)
|
|
72970
|
+
* @param yAxis 局部 Y 轴单位向量(视图坐标,高方向)
|
|
72971
|
+
* @param width 宽(沿 xAxis,视图像素)
|
|
72972
|
+
* @param height 高(沿 yAxis,视图像素)
|
|
72973
|
+
* @param color 填充颜色
|
|
72974
|
+
*/ function fillRotatedRect(control, center, xAxis, yAxis, width, height, color) {
|
|
72975
|
+
const ex = xAxis.clone();
|
|
72976
|
+
const ey = yAxis.clone();
|
|
72977
|
+
const anchor = center.clone();
|
|
72978
|
+
drawRotatedQuad(control, anchor, ex, ey, ()=>{
|
|
72979
|
+
control.fillRect(-width / 2, -height / 2, width, height, color);
|
|
72980
|
+
});
|
|
72981
|
+
}
|
|
72982
|
+
/**
|
|
72983
|
+
* 在由锚点和两个基向量定义的局部坐标系内执行绘制。
|
|
72984
|
+
* @param control 绘制控制器
|
|
72985
|
+
* @param anchor Control 绘制空间锚点(局部原点)
|
|
72986
|
+
* @param ex 局部 X 轴基向量(单位向量)
|
|
72987
|
+
* @param ey 局部 Y 轴基向量(单位向量)
|
|
72988
|
+
* @param drawFn 在局部坐标系内执行的绘制
|
|
72989
|
+
*/ function drawRotatedQuad(control, anchor, ex, ey, drawFn) {
|
|
72990
|
+
const graphics = control.engine.graphics;
|
|
72991
|
+
// 列优先:第一列 = ex,第二列 = ey,第三列 = anchor(平移)
|
|
72992
|
+
const matrix = Matrix3.fromColumnVectors(new Vector3(ex.x, ex.y, 0), new Vector3(ey.x, ey.y, 0), new Vector3(anchor.x, anchor.y, 1));
|
|
72993
|
+
graphics.pushTransform(matrix);
|
|
72994
|
+
drawFn();
|
|
72995
|
+
graphics.popTransform();
|
|
72996
|
+
}
|
|
73316
72997
|
/** 缩放角点的桌面端命中区域边长;视觉手柄仍由 scaleCircleSize 控制。 */ const SCALE_CORNER_HIT_SIZE = 14;
|
|
73317
72998
|
/** 单选信息标签中的类型图标尺寸。 */ const INFO_ICON_SIZE = 15;
|
|
73318
72999
|
/** 图标相对选框顶边的底部偏移。 */ const INFO_ICON_BOTTOM = 5.5;
|
|
@@ -74020,7 +73701,7 @@ const itemCreateConfig = defineConfig({
|
|
|
74020
73701
|
*/ _getIconUrl(type) {
|
|
74021
73702
|
const urlMap = {
|
|
74022
73703
|
'null': '',
|
|
74023
|
-
'image': this.config.
|
|
73704
|
+
'image': this.config.imageLogoUrl,
|
|
74024
73705
|
'group': this.config.groupLogoUrl,
|
|
74025
73706
|
'text': this.config.textLogoUrl,
|
|
74026
73707
|
'video': this.config.videoLogoUrl,
|
|
@@ -74038,7 +73719,7 @@ const itemCreateConfig = defineConfig({
|
|
|
74038
73719
|
const logoKeys = [
|
|
74039
73720
|
{
|
|
74040
73721
|
type: "image",
|
|
74041
|
-
key: '
|
|
73722
|
+
key: 'imageLogoUrl'
|
|
74042
73723
|
},
|
|
74043
73724
|
{
|
|
74044
73725
|
type: "group",
|
|
@@ -76204,6 +75885,12 @@ const itemCreateConfig = defineConfig({
|
|
|
76204
75885
|
this._session.syncTextArea(this.result, this.viewScale);
|
|
76205
75886
|
}
|
|
76206
75887
|
/**
|
|
75888
|
+
* 选中当前编辑文本的全部内容,并将焦点交给输入框。
|
|
75889
|
+
* @returns 当前存在可编辑文本时返回 true
|
|
75890
|
+
*/ selectAllText() {
|
|
75891
|
+
return this._session.selectAllText();
|
|
75892
|
+
}
|
|
75893
|
+
/**
|
|
76207
75894
|
* 绘制文本编辑包围盒。
|
|
76208
75895
|
* @param control 绘制控制器
|
|
76209
75896
|
*/ _drawEditBox(control) {
|
|
@@ -76487,16 +76174,16 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76487
76174
|
this._session = new TextEditSession(_owner);
|
|
76488
76175
|
}
|
|
76489
76176
|
}
|
|
76490
|
-
/** 图片裁切和扩边的交互类型。 */ var
|
|
76491
|
-
/** 无交互。 */
|
|
76492
|
-
/** 角点双轴缩放。 */
|
|
76493
|
-
/** 边中点单轴缩放。 */
|
|
76494
|
-
/** 移动交互框。 */
|
|
76495
|
-
return
|
|
76177
|
+
/** 图片裁切和扩边的交互类型。 */ var ImageInteractionType = /*#__PURE__*/ function(ImageInteractionType) {
|
|
76178
|
+
/** 无交互。 */ ImageInteractionType["NONE"] = "none";
|
|
76179
|
+
/** 角点双轴缩放。 */ ImageInteractionType["SCALE"] = "scale";
|
|
76180
|
+
/** 边中点单轴缩放。 */ ImageInteractionType["DIRECTION_SCALE"] = "direction-scale";
|
|
76181
|
+
/** 移动交互框。 */ ImageInteractionType["MOVE"] = "move";
|
|
76182
|
+
return ImageInteractionType;
|
|
76496
76183
|
}({});
|
|
76497
|
-
/** 在图片元素范围内移动或缩放归一化裁剪框。 */ class
|
|
76184
|
+
/** 在图片元素范围内移动或缩放归一化裁剪框。 */ class ImageCutGizmo extends Gizmo {
|
|
76498
76185
|
/** 当前图片裁剪配置。 */ get config() {
|
|
76499
|
-
return this._owner.getConfigManager().get(
|
|
76186
|
+
return this._owner.getConfigManager().get(imageCutConfig);
|
|
76500
76187
|
}
|
|
76501
76188
|
/** 当前选中的元素(直读 selection.getSelectedPlayerItems:id→VFXItem 已解析)。 */ get selectedItems() {
|
|
76502
76189
|
return this._owner.getSelection().getSelectedPlayerItems();
|
|
@@ -76532,7 +76219,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76532
76219
|
const inputPosition = new Vector2(event.position.x, event.position.y);
|
|
76533
76220
|
this.refreshInteractionType(hover, inputPosition);
|
|
76534
76221
|
this._owner.setCursor(this.cursorResult);
|
|
76535
|
-
if (event.buttonMask === MouseButtonMask.None && (this.interactionParam.type ===
|
|
76222
|
+
if (event.buttonMask === MouseButtonMask.None && (this.interactionParam.type === ImageInteractionType.SCALE || this.interactionParam.type === ImageInteractionType.DIRECTION_SCALE)) {
|
|
76536
76223
|
event.accept();
|
|
76537
76224
|
}
|
|
76538
76225
|
}
|
|
@@ -76561,9 +76248,9 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76561
76248
|
return;
|
|
76562
76249
|
}
|
|
76563
76250
|
this.interactionParam = {
|
|
76564
|
-
type:
|
|
76251
|
+
type: ImageInteractionType.NONE
|
|
76565
76252
|
};
|
|
76566
|
-
this.refreshCursorResult(
|
|
76253
|
+
this.refreshCursorResult(ImageInteractionType.NONE);
|
|
76567
76254
|
this._owner.setCursor(this.cursorResult);
|
|
76568
76255
|
}
|
|
76569
76256
|
/** @returns 是否仅选中了一个可裁剪元素。 */ _isApplicableSelection() {
|
|
@@ -76578,7 +76265,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76578
76265
|
if (!this._mouseGrabbed) {
|
|
76579
76266
|
return false;
|
|
76580
76267
|
}
|
|
76581
|
-
if (this.interactionParam.type ===
|
|
76268
|
+
if (this.interactionParam.type === ImageInteractionType.NONE) {
|
|
76582
76269
|
return true;
|
|
76583
76270
|
}
|
|
76584
76271
|
// 步骤 2:获取实时投影并计算指针位移。
|
|
@@ -76593,7 +76280,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76593
76280
|
const { min: itemBoxMin, max: itemBoxMax } = itemBox;
|
|
76594
76281
|
// 步骤 3:按移动、角点缩放或边缩放更新归一化裁剪框。
|
|
76595
76282
|
switch(this.interactionParam.type){
|
|
76596
|
-
case
|
|
76283
|
+
case ImageInteractionType.MOVE:
|
|
76597
76284
|
{
|
|
76598
76285
|
const { min: originMin, max: originMax } = box;
|
|
76599
76286
|
const xMinShift = itemBoxMin.x - originMin.x;
|
|
@@ -76606,7 +76293,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76606
76293
|
this.result.normalizeCutBox = getNormalizeBoxByBoxes(itemBox, resultBox);
|
|
76607
76294
|
break;
|
|
76608
76295
|
}
|
|
76609
|
-
case
|
|
76296
|
+
case ImageInteractionType.SCALE:
|
|
76610
76297
|
{
|
|
76611
76298
|
const { startCorner, anchor } = this.interactionParam;
|
|
76612
76299
|
const resultCorner = new Vector2();
|
|
@@ -76671,7 +76358,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76671
76358
|
}
|
|
76672
76359
|
break;
|
|
76673
76360
|
}
|
|
76674
|
-
case
|
|
76361
|
+
case ImageInteractionType.DIRECTION_SCALE:
|
|
76675
76362
|
{
|
|
76676
76363
|
var _this_interactionParam1;
|
|
76677
76364
|
const { index } = this.interactionParam;
|
|
@@ -76746,7 +76433,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76746
76433
|
const inputPosition = new Vector2(event.position.x, event.position.y);
|
|
76747
76434
|
this.refreshInteractionType(mouse, inputPosition);
|
|
76748
76435
|
this._owner.setCursor(this.cursorResult);
|
|
76749
|
-
if (this.interactionParam.type ===
|
|
76436
|
+
if (this.interactionParam.type === ImageInteractionType.NONE) {
|
|
76750
76437
|
return;
|
|
76751
76438
|
}
|
|
76752
76439
|
this._mouseGrabbed = true;
|
|
@@ -76843,7 +76530,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76843
76530
|
* @param angle 指针方向
|
|
76844
76531
|
*/ refreshCursorResult(activeType, angle = 0) {
|
|
76845
76532
|
switch(activeType){
|
|
76846
|
-
case
|
|
76533
|
+
case ImageInteractionType.SCALE:
|
|
76847
76534
|
{
|
|
76848
76535
|
this.cursorResult = {
|
|
76849
76536
|
type: GestureCursorType.SCALE,
|
|
@@ -76851,7 +76538,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76851
76538
|
};
|
|
76852
76539
|
break;
|
|
76853
76540
|
}
|
|
76854
|
-
case
|
|
76541
|
+
case ImageInteractionType.DIRECTION_SCALE:
|
|
76855
76542
|
{
|
|
76856
76543
|
this.cursorResult = {
|
|
76857
76544
|
type: GestureCursorType.SCALE,
|
|
@@ -76859,8 +76546,8 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76859
76546
|
};
|
|
76860
76547
|
break;
|
|
76861
76548
|
}
|
|
76862
|
-
case
|
|
76863
|
-
case
|
|
76549
|
+
case ImageInteractionType.MOVE:
|
|
76550
|
+
case ImageInteractionType.NONE:
|
|
76864
76551
|
{
|
|
76865
76552
|
this.cursorResult = {
|
|
76866
76553
|
type: GestureCursorType.NORMAL,
|
|
@@ -76910,7 +76597,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76910
76597
|
*/ refreshInteractionType(point, inputPosition) {
|
|
76911
76598
|
// 步骤 1:重置交互状态并获取实时裁剪投影。
|
|
76912
76599
|
this.interactionParam = {
|
|
76913
|
-
type:
|
|
76600
|
+
type: ImageInteractionType.NONE
|
|
76914
76601
|
};
|
|
76915
76602
|
const projection = this._projectSelection();
|
|
76916
76603
|
if (!projection) {
|
|
@@ -76927,7 +76614,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76927
76614
|
if (corner.distance(point) < this.config.scaleInteractionDistance) {
|
|
76928
76615
|
hasInteractionType = true;
|
|
76929
76616
|
this.interactionParam = {
|
|
76930
|
-
type:
|
|
76617
|
+
type: ImageInteractionType.SCALE,
|
|
76931
76618
|
box: cutBox.clone(),
|
|
76932
76619
|
startMouse: inputPosition,
|
|
76933
76620
|
startCorner: corner.clone(),
|
|
@@ -76952,7 +76639,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76952
76639
|
if (state) {
|
|
76953
76640
|
hasInteractionType = true;
|
|
76954
76641
|
this.interactionParam = {
|
|
76955
|
-
type:
|
|
76642
|
+
type: ImageInteractionType.DIRECTION_SCALE,
|
|
76956
76643
|
index,
|
|
76957
76644
|
box: cutBox.clone(),
|
|
76958
76645
|
startMouse: inputPosition
|
|
@@ -76963,7 +76650,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76963
76650
|
// 步骤 4:最后检测裁剪区域整体移动并刷新光标。
|
|
76964
76651
|
if (cutBox.containsPoint(point) && !hasInteractionType) {
|
|
76965
76652
|
this.interactionParam = {
|
|
76966
|
-
type:
|
|
76653
|
+
type: ImageInteractionType.MOVE,
|
|
76967
76654
|
box: cutBox.clone(),
|
|
76968
76655
|
startMouse: inputPosition
|
|
76969
76656
|
};
|
|
@@ -76975,8 +76662,8 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76975
76662
|
super(...args), _define_property(this, "result", {
|
|
76976
76663
|
normalizeCutBox: new Box2(new Vector2(), new Vector2(1, 1))
|
|
76977
76664
|
}), /** 仅供当前渲染帧使用的投影快照;输入不读取它。 */ _define_property(this, "_renderProjection", void 0), _define_property(this, "interactionParam", {
|
|
76978
|
-
type:
|
|
76979
|
-
}), _define_property(this, "type", GizmoType.
|
|
76665
|
+
type: ImageInteractionType.NONE
|
|
76666
|
+
}), _define_property(this, "type", GizmoType.IMAGE_CUT), /** 光标结果。 */ _define_property(this, "cursorResult", {
|
|
76980
76667
|
type: GestureCursorType.NORMAL,
|
|
76981
76668
|
angle: 0
|
|
76982
76669
|
}), _define_property(this, "_isLockScale", false), _define_property(this, "_mouseGrabbed", false);
|
|
@@ -76985,13 +76672,13 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
76985
76672
|
/** 图片裁切工具。 */ class ImageCutGizmoTool extends GizmoTool {
|
|
76986
76673
|
/** @returns 图片裁切 Gizmo 图。 */ createGizmos() {
|
|
76987
76674
|
return [
|
|
76988
|
-
new
|
|
76675
|
+
new ImageCutGizmo(this.owner)
|
|
76989
76676
|
];
|
|
76990
76677
|
}
|
|
76991
76678
|
}
|
|
76992
|
-
/** 在图片范围外移动或缩放归一化扩边框。 */ class
|
|
76679
|
+
/** 在图片范围外移动或缩放归一化扩边框。 */ class ImageExpandGizmo extends Gizmo {
|
|
76993
76680
|
/** 当前图片扩边配置。 */ get config() {
|
|
76994
|
-
return this._owner.getConfigManager().get(
|
|
76681
|
+
return this._owner.getConfigManager().get(imageExpandConfig);
|
|
76995
76682
|
}
|
|
76996
76683
|
/** 当前选中的元素(直读 selection.getSelectedPlayerItems:id→VFXItem 已解析)。 */ get selectedItems() {
|
|
76997
76684
|
return this._owner.getSelection().getSelectedPlayerItems();
|
|
@@ -77027,7 +76714,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77027
76714
|
const inputPosition = new Vector2(event.position.x, event.position.y);
|
|
77028
76715
|
this.refreshInteractionType(hover, inputPosition);
|
|
77029
76716
|
this._owner.setCursor(this.cursorResult);
|
|
77030
|
-
if (event.buttonMask === MouseButtonMask.None && (this.interactionParam.type ===
|
|
76717
|
+
if (event.buttonMask === MouseButtonMask.None && (this.interactionParam.type === ImageInteractionType.SCALE || this.interactionParam.type === ImageInteractionType.DIRECTION_SCALE)) {
|
|
77031
76718
|
event.accept();
|
|
77032
76719
|
}
|
|
77033
76720
|
}
|
|
@@ -77056,9 +76743,9 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77056
76743
|
return;
|
|
77057
76744
|
}
|
|
77058
76745
|
this.interactionParam = {
|
|
77059
|
-
type:
|
|
76746
|
+
type: ImageInteractionType.NONE
|
|
77060
76747
|
};
|
|
77061
|
-
this.refreshCursorResult(
|
|
76748
|
+
this.refreshCursorResult(ImageInteractionType.NONE);
|
|
77062
76749
|
this._owner.setCursor(this.cursorResult);
|
|
77063
76750
|
}
|
|
77064
76751
|
/** @returns 是否仅选中了一个可扩边元素。 */ _isApplicableSelection() {
|
|
@@ -77073,7 +76760,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77073
76760
|
if (!this._mouseGrabbed) {
|
|
77074
76761
|
return false;
|
|
77075
76762
|
}
|
|
77076
|
-
if (this.interactionParam.type ===
|
|
76763
|
+
if (this.interactionParam.type === ImageInteractionType.NONE) {
|
|
77077
76764
|
return true;
|
|
77078
76765
|
}
|
|
77079
76766
|
// 步骤 2:获取实时投影并计算指针位移。
|
|
@@ -77088,7 +76775,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77088
76775
|
const { min: itemBoxMin, max: itemBoxMax } = itemBox;
|
|
77089
76776
|
// 步骤 3:按移动、角点缩放或边缩放更新归一化扩边框。
|
|
77090
76777
|
switch(this.interactionParam.type){
|
|
77091
|
-
case
|
|
76778
|
+
case ImageInteractionType.MOVE:
|
|
77092
76779
|
{
|
|
77093
76780
|
const { min: originMin, max: originMax } = box;
|
|
77094
76781
|
const xMinShift = itemBoxMax.x - originMax.x;
|
|
@@ -77101,7 +76788,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77101
76788
|
this.result.normalizeExpandBox = getNormalizeBoxByBoxes(itemBox, resultBox);
|
|
77102
76789
|
break;
|
|
77103
76790
|
}
|
|
77104
|
-
case
|
|
76791
|
+
case ImageInteractionType.SCALE:
|
|
77105
76792
|
{
|
|
77106
76793
|
const { index } = this.interactionParam;
|
|
77107
76794
|
const { min, max } = box;
|
|
@@ -77160,7 +76847,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77160
76847
|
]));
|
|
77161
76848
|
break;
|
|
77162
76849
|
}
|
|
77163
|
-
case
|
|
76850
|
+
case ImageInteractionType.DIRECTION_SCALE:
|
|
77164
76851
|
{
|
|
77165
76852
|
var _this_interactionParam1;
|
|
77166
76853
|
const { index } = this.interactionParam;
|
|
@@ -77278,7 +76965,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77278
76965
|
const inputPosition = new Vector2(event.position.x, event.position.y);
|
|
77279
76966
|
this.refreshInteractionType(mouse, inputPosition);
|
|
77280
76967
|
this._owner.setCursor(this.cursorResult);
|
|
77281
|
-
if (this.interactionParam.type ===
|
|
76968
|
+
if (this.interactionParam.type === ImageInteractionType.NONE) {
|
|
77282
76969
|
return;
|
|
77283
76970
|
}
|
|
77284
76971
|
this._mouseGrabbed = true;
|
|
@@ -77365,7 +77052,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77365
77052
|
* @param angle 指针方向角度。
|
|
77366
77053
|
*/ refreshCursorResult(activeType, angle = 0) {
|
|
77367
77054
|
switch(activeType){
|
|
77368
|
-
case
|
|
77055
|
+
case ImageInteractionType.SCALE:
|
|
77369
77056
|
{
|
|
77370
77057
|
this.cursorResult = {
|
|
77371
77058
|
type: GestureCursorType.SCALE,
|
|
@@ -77373,7 +77060,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77373
77060
|
};
|
|
77374
77061
|
break;
|
|
77375
77062
|
}
|
|
77376
|
-
case
|
|
77063
|
+
case ImageInteractionType.DIRECTION_SCALE:
|
|
77377
77064
|
{
|
|
77378
77065
|
this.cursorResult = {
|
|
77379
77066
|
type: GestureCursorType.SCALE,
|
|
@@ -77381,8 +77068,8 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77381
77068
|
};
|
|
77382
77069
|
break;
|
|
77383
77070
|
}
|
|
77384
|
-
case
|
|
77385
|
-
case
|
|
77071
|
+
case ImageInteractionType.MOVE:
|
|
77072
|
+
case ImageInteractionType.NONE:
|
|
77386
77073
|
{
|
|
77387
77074
|
this.cursorResult = {
|
|
77388
77075
|
type: GestureCursorType.NORMAL,
|
|
@@ -77431,7 +77118,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77431
77118
|
* @param inputPosition Control 本地坐标鼠标位置
|
|
77432
77119
|
*/ refreshInteractionType(point, inputPosition) {
|
|
77433
77120
|
// 步骤 1:重置交互状态并获取实时扩边投影。
|
|
77434
|
-
this.interactionParam.type =
|
|
77121
|
+
this.interactionParam.type = ImageInteractionType.NONE;
|
|
77435
77122
|
const projection = this._projectSelection();
|
|
77436
77123
|
if (!projection) {
|
|
77437
77124
|
return;
|
|
@@ -77447,7 +77134,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77447
77134
|
if (corner.distance(point) < 20) {
|
|
77448
77135
|
hasInteractionType = true;
|
|
77449
77136
|
this.interactionParam = {
|
|
77450
|
-
type:
|
|
77137
|
+
type: ImageInteractionType.SCALE,
|
|
77451
77138
|
index,
|
|
77452
77139
|
box: expandBox.clone(),
|
|
77453
77140
|
startMouse: inputPosition
|
|
@@ -77471,7 +77158,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77471
77158
|
if (state) {
|
|
77472
77159
|
hasInteractionType = true;
|
|
77473
77160
|
this.interactionParam = {
|
|
77474
|
-
type:
|
|
77161
|
+
type: ImageInteractionType.DIRECTION_SCALE,
|
|
77475
77162
|
index,
|
|
77476
77163
|
box: expandBox.clone(),
|
|
77477
77164
|
startMouse: inputPosition
|
|
@@ -77482,7 +77169,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77482
77169
|
// 步骤 4:最后检测扩边区域整体移动并刷新光标。
|
|
77483
77170
|
if (expandBox.containsPoint(point) && !hasInteractionType) {
|
|
77484
77171
|
this.interactionParam = {
|
|
77485
|
-
type:
|
|
77172
|
+
type: ImageInteractionType.MOVE,
|
|
77486
77173
|
box: expandBox.clone(),
|
|
77487
77174
|
startMouse: inputPosition
|
|
77488
77175
|
};
|
|
@@ -77494,8 +77181,8 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77494
77181
|
super(...args), _define_property(this, "result", {
|
|
77495
77182
|
normalizeExpandBox: new Box2(new Vector2(), new Vector2(1, 1))
|
|
77496
77183
|
}), _define_property(this, "_renderProjection", void 0), _define_property(this, "interactionParam", {
|
|
77497
|
-
type:
|
|
77498
|
-
}), _define_property(this, "type", GizmoType.
|
|
77184
|
+
type: ImageInteractionType.NONE
|
|
77185
|
+
}), _define_property(this, "type", GizmoType.IMAGE_EXPAND), /** 光标结果。 */ _define_property(this, "cursorResult", {
|
|
77499
77186
|
type: GestureCursorType.NORMAL,
|
|
77500
77187
|
angle: 0
|
|
77501
77188
|
}), _define_property(this, "_isLockScale", false), _define_property(this, "_mouseGrabbed", false);
|
|
@@ -77504,7 +77191,7 @@ _define_property(SelectTextGizmo, "HOVER_GAP_LENGTH", 4);
|
|
|
77504
77191
|
/** 图片扩边工具。 */ class ImageExpandGizmoTool extends GizmoTool {
|
|
77505
77192
|
/** @returns 图片扩边 Gizmo 图。 */ createGizmos() {
|
|
77506
77193
|
return [
|
|
77507
|
-
new
|
|
77194
|
+
new ImageExpandGizmo(this.owner)
|
|
77508
77195
|
];
|
|
77509
77196
|
}
|
|
77510
77197
|
}
|
|
@@ -77748,7 +77435,7 @@ const EDIT_TEXT_FONT_FAMILY = 'sans-serif';
|
|
|
77748
77435
|
}), /** onMouseMove 命中缓存(坐标未变则跳过 _computePreSelected,避免每帧全 result 命中扫描)。 */ _define_property(this, "_cachedCoords", null);
|
|
77749
77436
|
}
|
|
77750
77437
|
}
|
|
77751
|
-
/**
|
|
77438
|
+
/** Sprite 精准文字编辑工具。 */ class SpriteTextEditGizmoTool extends GizmoTool {
|
|
77752
77439
|
/** @returns 精准文字编辑 Gizmo 图。 */ createGizmos() {
|
|
77753
77440
|
return [
|
|
77754
77441
|
new SpriteTextEditGizmo(this.owner)
|
|
@@ -78319,8 +78006,8 @@ const EDIT_TEXT_FONT_FAMILY = 'sans-serif';
|
|
|
78319
78006
|
void this._refreshMaskImage(this.config.maskImage);
|
|
78320
78007
|
}
|
|
78321
78008
|
}
|
|
78322
|
-
/**
|
|
78323
|
-
/** @returns
|
|
78009
|
+
/** 蒙版编辑工具。 */ class MaskGizmoTool extends GizmoTool {
|
|
78010
|
+
/** @returns 蒙版 Gizmo 图。 */ createGizmos() {
|
|
78324
78011
|
return [
|
|
78325
78012
|
new MaskGizmo(this.owner)
|
|
78326
78013
|
];
|
|
@@ -80466,6 +80153,348 @@ const cursorMap = {
|
|
|
80466
80153
|
console.error(e);
|
|
80467
80154
|
});
|
|
80468
80155
|
}
|
|
80156
|
+
const LOADING_TIP_FONT_SIZE = 16;
|
|
80157
|
+
const LOADING_TIP_FONT_FAMILY = 'sans-serif';
|
|
80158
|
+
const LOADING_TIP_COLOR = 0x000000;
|
|
80159
|
+
/** 为目标元素叠加加载动画与提示文案。 */ class LoadingGizmo extends Gizmo {
|
|
80160
|
+
/** 当前加载覆盖层配置。 */ get config() {
|
|
80161
|
+
return this._owner.getConfigManager().get(loadingConfig);
|
|
80162
|
+
}
|
|
80163
|
+
/** 当前 Gizmo 实例持有的可销毁渲染投影。 */ get idMap() {
|
|
80164
|
+
return this._idMap;
|
|
80165
|
+
}
|
|
80166
|
+
/** 当前 loading 元素 id 列表(由稳定 manager 派生)。 */ get loadingIds() {
|
|
80167
|
+
return this._manager.ids;
|
|
80168
|
+
}
|
|
80169
|
+
/**
|
|
80170
|
+
* 在片元着色器变化时重建加载动画对象。
|
|
80171
|
+
* @param change 加载配置变更。
|
|
80172
|
+
*/ _onConfigChange(change) {
|
|
80173
|
+
if (change.previous.loadingFragment === change.current.loadingFragment) {
|
|
80174
|
+
return;
|
|
80175
|
+
}
|
|
80176
|
+
for (const [id, loadingItem] of this._idMap){
|
|
80177
|
+
this._disposeLoadingVFXItem(loadingItem.loadingVFXItem);
|
|
80178
|
+
loadingItem.loadingVFXItem = this.createLoadingVFXItem();
|
|
80179
|
+
this.updateLoadingVFXItemTransform(id, loadingItem);
|
|
80180
|
+
}
|
|
80181
|
+
}
|
|
80182
|
+
/**
|
|
80183
|
+
* 订阅 loading 集合变化:add/delete 后回调当前全量 id 列表。
|
|
80184
|
+
* @param cb 收到当前全量 loading id 列表的回调
|
|
80185
|
+
* @returns 退订函数,调用后取消对应监听
|
|
80186
|
+
*/ onLoadingChange(cb) {
|
|
80187
|
+
return this._manager.on('change', (change)=>{
|
|
80188
|
+
if (change.addedIds.length > 0 || change.removedIds.length > 0) {
|
|
80189
|
+
cb(change.ids);
|
|
80190
|
+
}
|
|
80191
|
+
});
|
|
80192
|
+
}
|
|
80193
|
+
/**
|
|
80194
|
+
* 为指定元素添加 loading 覆盖层;同一元素不可重复添加。
|
|
80195
|
+
* @param id 目标元素 ID
|
|
80196
|
+
* @param options loading 配置(文案、位置、自定义区域、是否清空选中)
|
|
80197
|
+
*/ add(id, options) {
|
|
80198
|
+
this._manager.add(id, options);
|
|
80199
|
+
}
|
|
80200
|
+
/**
|
|
80201
|
+
* 移除指定元素的 loading 覆盖层并释放对应 VFXItem。
|
|
80202
|
+
* @param id 目标元素 ID
|
|
80203
|
+
*/ delete(id) {
|
|
80204
|
+
this._manager.delete(id);
|
|
80205
|
+
}
|
|
80206
|
+
/**
|
|
80207
|
+
* 更新指定 loading 元素的提示文案。
|
|
80208
|
+
* @param id 目标元素 ID
|
|
80209
|
+
* @param options 待合并更新的 LoadingTip 属性
|
|
80210
|
+
*/ updateItem(id, options) {
|
|
80211
|
+
this._manager.update(id, options);
|
|
80212
|
+
}
|
|
80213
|
+
/** 根据目标元素的实时包围盒刷新加载动画。 */ onPreRender() {
|
|
80214
|
+
if (this._idMap.size === 0) {
|
|
80215
|
+
return;
|
|
80216
|
+
}
|
|
80217
|
+
for (const [id, loadingItem] of this._idMap){
|
|
80218
|
+
this.updateLoadingVFXItemTransform(id, loadingItem);
|
|
80219
|
+
}
|
|
80220
|
+
}
|
|
80221
|
+
/**
|
|
80222
|
+
* 绘制所有加载提示文案。
|
|
80223
|
+
* @param control 绘制控制器。
|
|
80224
|
+
*/ draw(control) {
|
|
80225
|
+
if (this._idMap.size === 0) {
|
|
80226
|
+
return;
|
|
80227
|
+
}
|
|
80228
|
+
for (const [id, loadingItem] of this._idMap){
|
|
80229
|
+
if (!loadingItem.tip?.text) {
|
|
80230
|
+
continue;
|
|
80231
|
+
}
|
|
80232
|
+
const item = getPlayerItemById(this._owner.getEngine().compositions[0], id);
|
|
80233
|
+
const itemBox = item ? getItemViewBox(item, GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement)) : new Box2();
|
|
80234
|
+
const loadingBox = getBoxByNormalizeBox(itemBox, loadingItem.loadingBox);
|
|
80235
|
+
this.drawLoadingTip(control, loadingBox, itemBox, loadingItem.tip);
|
|
80236
|
+
}
|
|
80237
|
+
}
|
|
80238
|
+
/**
|
|
80239
|
+
* 绘制单条加载提示,并将文本限制在加载区域内。
|
|
80240
|
+
* @param control 绘制控制器。
|
|
80241
|
+
* @param loadingBox 加载区域。
|
|
80242
|
+
* @param itemBox 目标元素包围盒。
|
|
80243
|
+
* @param tip 加载提示配置。
|
|
80244
|
+
*/ drawLoadingTip(control, loadingBox, itemBox, tip) {
|
|
80245
|
+
// 步骤 1:计算提示位置与可用宽度。
|
|
80246
|
+
const { x: width, y: height } = loadingBox.getSize(this._loadingBoxSize);
|
|
80247
|
+
if (!tip.text || !Number.isFinite(width) || width <= 0) {
|
|
80248
|
+
return;
|
|
80249
|
+
}
|
|
80250
|
+
const positionedLeft = tip.position ? itemBox.min.x + (Number.isFinite(tip.position.x) ? tip.position.x : 0) : loadingBox.min.x;
|
|
80251
|
+
const left = tip.position ? Math.min(Math.max(positionedLeft, loadingBox.min.x), loadingBox.max.x) : loadingBox.min.x;
|
|
80252
|
+
const availableWidth = tip.position ? loadingBox.max.x - left : width;
|
|
80253
|
+
if (availableWidth <= 0) {
|
|
80254
|
+
return;
|
|
80255
|
+
}
|
|
80256
|
+
/**
|
|
80257
|
+
* 测量加载提示的粗体字形。
|
|
80258
|
+
* @param text 提示文本。
|
|
80259
|
+
* @param fontSize 字号。
|
|
80260
|
+
* @returns 文本字形尺寸。
|
|
80261
|
+
*/ const measureTipText = (text, fontSize)=>{
|
|
80262
|
+
const metrics = measureTextMetrics(text, fontSize, LOADING_TIP_FONT_FAMILY, 'bold');
|
|
80263
|
+
return {
|
|
80264
|
+
width: metrics.width || Array.from(text).length * fontSize,
|
|
80265
|
+
actualBoundingBoxAscent: metrics.actualBoundingBoxAscent || fontSize * 0.8,
|
|
80266
|
+
actualBoundingBoxDescent: metrics.actualBoundingBoxDescent || fontSize * 0.2
|
|
80267
|
+
};
|
|
80268
|
+
};
|
|
80269
|
+
// 步骤 2:测量文本并缩小字号以适应可用宽度。
|
|
80270
|
+
const measuredAtBaseSize = measureTipText(tip.text, LOADING_TIP_FONT_SIZE);
|
|
80271
|
+
let fontSize = measuredAtBaseSize.width > availableWidth ? LOADING_TIP_FONT_SIZE * availableWidth / measuredAtBaseSize.width : LOADING_TIP_FONT_SIZE;
|
|
80272
|
+
let textMetrics = measureTipText(tip.text, fontSize);
|
|
80273
|
+
if (textMetrics.width > availableWidth) {
|
|
80274
|
+
fontSize *= availableWidth / textMetrics.width;
|
|
80275
|
+
textMetrics = measureTipText(tip.text, fontSize);
|
|
80276
|
+
}
|
|
80277
|
+
// 步骤 3:按字形基线计算最终绘制位置。
|
|
80278
|
+
const textLeft = tip.position ? left : loadingBox.min.x + (width - textMetrics.width) / 2;
|
|
80279
|
+
const inkHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;
|
|
80280
|
+
const top = tip.position ? itemBox.min.y + (Number.isFinite(tip.position.y) ? tip.position.y : 0) : loadingBox.min.y + (height - inkHeight) / 2;
|
|
80281
|
+
const probeMetrics = measureTipText(METRICS_STRING + BASELINE_SYMBOL, fontSize);
|
|
80282
|
+
const inkTopFromCellTop = CONTROL_TEXT_GLYPH_PADDING + probeMetrics.actualBoundingBoxAscent - textMetrics.actualBoundingBoxAscent;
|
|
80283
|
+
control.drawText(textLeft, top - inkTopFromCellTop, tip.text, fontSize, toColor(LOADING_TIP_COLOR, 1), LOADING_TIP_FONT_FAMILY, 'bold');
|
|
80284
|
+
}
|
|
80285
|
+
/** 释放当前实例的渲染投影;稳定 LoadingManager 不随 Gizmo 重建清空。 */ dispose() {
|
|
80286
|
+
this._configOff?.();
|
|
80287
|
+
this._configOff = undefined;
|
|
80288
|
+
this._managerOff?.();
|
|
80289
|
+
this._managerOff = undefined;
|
|
80290
|
+
this._idMap.forEach((loadingItem)=>{
|
|
80291
|
+
this._disposeLoadingVFXItem(loadingItem.loadingVFXItem);
|
|
80292
|
+
});
|
|
80293
|
+
this._idMap.clear();
|
|
80294
|
+
super.dispose();
|
|
80295
|
+
}
|
|
80296
|
+
/** 将可销毁的渲染投影同步到当前加载状态。 */ _syncManager() {
|
|
80297
|
+
const activeIds = new Set(this._manager.ids);
|
|
80298
|
+
for (const [id, loadingItem] of this._idMap){
|
|
80299
|
+
if (!activeIds.has(id)) {
|
|
80300
|
+
this._disposeLoadingVFXItem(loadingItem.loadingVFXItem);
|
|
80301
|
+
this._idMap.delete(id);
|
|
80302
|
+
}
|
|
80303
|
+
}
|
|
80304
|
+
for (const id of activeIds){
|
|
80305
|
+
const state = this._manager.get(id);
|
|
80306
|
+
if (!state) {
|
|
80307
|
+
continue;
|
|
80308
|
+
}
|
|
80309
|
+
const existing = this._idMap.get(id);
|
|
80310
|
+
if (existing) {
|
|
80311
|
+
existing.tip = {
|
|
80312
|
+
...state.tip,
|
|
80313
|
+
position: state.tip.position ? state.tip.position.clone() : undefined
|
|
80314
|
+
};
|
|
80315
|
+
} else {
|
|
80316
|
+
this._createProjection(id, state);
|
|
80317
|
+
}
|
|
80318
|
+
}
|
|
80319
|
+
}
|
|
80320
|
+
/**
|
|
80321
|
+
* 为加载状态创建渲染投影。
|
|
80322
|
+
* @param id 目标元素 ID。
|
|
80323
|
+
* @param state 加载状态。
|
|
80324
|
+
*/ _createProjection(id, state) {
|
|
80325
|
+
const item = getPlayerItemById(this._owner.getEngine().compositions[0], id);
|
|
80326
|
+
const itemViewBox = item ? getItemViewBox(item, GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement)) : new Box2();
|
|
80327
|
+
const currentBox = state.loadingBox ? this.getViewBoxByBox(state.loadingBox) : itemViewBox;
|
|
80328
|
+
this._idMap.set(id, {
|
|
80329
|
+
loadingBox: getNormalizeBoxByBoxes(itemViewBox, currentBox),
|
|
80330
|
+
loadingVFXItem: this.createLoadingVFXItem(),
|
|
80331
|
+
tip: {
|
|
80332
|
+
...state.tip,
|
|
80333
|
+
position: state.tip.position ? state.tip.position.clone() : undefined
|
|
80334
|
+
}
|
|
80335
|
+
});
|
|
80336
|
+
this.updateLoadingVFXItemTransform(id, this._idMap.get(id));
|
|
80337
|
+
}
|
|
80338
|
+
/** @returns 当前相机信息。 */ getCameraInfo() {
|
|
80339
|
+
return GizmoViewportUtils.getCameraInfo(this._owner.getEngine());
|
|
80340
|
+
}
|
|
80341
|
+
/** @returns 当前视口的缩放、平移与尺寸。 */ getViewportParams() {
|
|
80342
|
+
const composition = this._owner.getEngine().compositions[0];
|
|
80343
|
+
const camera = composition?.camera;
|
|
80344
|
+
if (camera) {
|
|
80345
|
+
const viewportMatrix = camera.getViewportMatrix();
|
|
80346
|
+
const scale = viewportMatrix.elements[0];
|
|
80347
|
+
const translation = new Vector2(viewportMatrix.elements[12], viewportMatrix.elements[13]);
|
|
80348
|
+
const width = GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement).x;
|
|
80349
|
+
const height = GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement).y;
|
|
80350
|
+
return {
|
|
80351
|
+
scale,
|
|
80352
|
+
translation,
|
|
80353
|
+
width,
|
|
80354
|
+
height
|
|
80355
|
+
};
|
|
80356
|
+
}
|
|
80357
|
+
return {
|
|
80358
|
+
scale: 1,
|
|
80359
|
+
translation: new Vector2(),
|
|
80360
|
+
width: GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement).x,
|
|
80361
|
+
height: GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement).y
|
|
80362
|
+
};
|
|
80363
|
+
}
|
|
80364
|
+
/**
|
|
80365
|
+
* 将视图坐标下的包围盒按当前视口缩放与平移变换为实际渲染包围盒。
|
|
80366
|
+
* @param box 视图坐标下的包围盒
|
|
80367
|
+
* @returns 经视口缩放与平移后的实际渲染包围盒
|
|
80368
|
+
*/ getViewBoxByBox(box) {
|
|
80369
|
+
const { scale, translation, width, height } = this.getViewportParams();
|
|
80370
|
+
const center = new Vector2(width / 2, height / 2);
|
|
80371
|
+
const result = scaleBox(box.clone(), scale, center).translate(translation);
|
|
80372
|
+
return result;
|
|
80373
|
+
}
|
|
80374
|
+
/**
|
|
80375
|
+
* 创建承载加载动画的 VFXItem。
|
|
80376
|
+
* @returns 已绑定几何与材质的加载动画对象。
|
|
80377
|
+
*/ createLoadingVFXItem() {
|
|
80378
|
+
// 步骤 1:创建加载动画对象与平面几何。
|
|
80379
|
+
const composition = this._owner.getEngine().compositions[0];
|
|
80380
|
+
const loadingVFXItem = VFXItemFactory.createVFXItem(composition, null, 'LoadingVFXItem', EffectComponent);
|
|
80381
|
+
this._owner.getSelection().addIgnoreIds([
|
|
80382
|
+
loadingVFXItem.getInstanceId()
|
|
80383
|
+
]);
|
|
80384
|
+
loadingVFXItem.type = index$1.ItemType.effect;
|
|
80385
|
+
const effects = loadingVFXItem.getComponent(EffectComponent);
|
|
80386
|
+
const engine = this._owner.getEngine();
|
|
80387
|
+
const geometry = Geometry.create(engine, {
|
|
80388
|
+
attributes: {
|
|
80389
|
+
aPos: {
|
|
80390
|
+
size: 3,
|
|
80391
|
+
data: new Float32Array([
|
|
80392
|
+
-0.5,
|
|
80393
|
+
-0.5,
|
|
80394
|
+
0,
|
|
80395
|
+
0.5,
|
|
80396
|
+
-0.5,
|
|
80397
|
+
0,
|
|
80398
|
+
0.5,
|
|
80399
|
+
0.5,
|
|
80400
|
+
0,
|
|
80401
|
+
-0.5,
|
|
80402
|
+
0.5,
|
|
80403
|
+
0
|
|
80404
|
+
])
|
|
80405
|
+
},
|
|
80406
|
+
aUV: {
|
|
80407
|
+
size: 2,
|
|
80408
|
+
data: new Float32Array([
|
|
80409
|
+
0,
|
|
80410
|
+
0,
|
|
80411
|
+
1,
|
|
80412
|
+
0,
|
|
80413
|
+
1,
|
|
80414
|
+
1,
|
|
80415
|
+
0,
|
|
80416
|
+
1
|
|
80417
|
+
])
|
|
80418
|
+
}
|
|
80419
|
+
},
|
|
80420
|
+
indices: {
|
|
80421
|
+
data: new Uint16Array([
|
|
80422
|
+
0,
|
|
80423
|
+
1,
|
|
80424
|
+
2,
|
|
80425
|
+
0,
|
|
80426
|
+
2,
|
|
80427
|
+
3
|
|
80428
|
+
])
|
|
80429
|
+
},
|
|
80430
|
+
mode: glContext.TRIANGLES,
|
|
80431
|
+
drawCount: 6
|
|
80432
|
+
});
|
|
80433
|
+
// 步骤 2:使用当前配置创建动画材质。
|
|
80434
|
+
const material = Material.create(engine, {
|
|
80435
|
+
shader: {
|
|
80436
|
+
vertex: `
|
|
80437
|
+
precision highp float;
|
|
80438
|
+
attribute vec3 aPos;
|
|
80439
|
+
attribute vec2 aUV;
|
|
80440
|
+
uniform mat4 effects_MatrixVP;
|
|
80441
|
+
uniform mat4 effects_ObjectToWorld;
|
|
80442
|
+
varying vec2 vUV;
|
|
80443
|
+
void main() {
|
|
80444
|
+
vUV = aUV;
|
|
80445
|
+
gl_Position = effects_MatrixVP * effects_ObjectToWorld * vec4(aPos, 1.0);
|
|
80446
|
+
}`,
|
|
80447
|
+
fragment: this.config.loadingFragment
|
|
80448
|
+
}
|
|
80449
|
+
});
|
|
80450
|
+
// 步骤 3:绑定运行时几何与材质。
|
|
80451
|
+
// @ts-expect-error Effects 类型未暴露可写 geometry。
|
|
80452
|
+
effects.geometry = geometry;
|
|
80453
|
+
effects.material = material;
|
|
80454
|
+
return loadingVFXItem;
|
|
80455
|
+
}
|
|
80456
|
+
/** 释放 loading 渲染对象,并同步恢复 Selection 的命中过滤。 */ _disposeLoadingVFXItem(item) {
|
|
80457
|
+
this._owner.getSelection().deleteIgnoreIds([
|
|
80458
|
+
item.getInstanceId()
|
|
80459
|
+
]);
|
|
80460
|
+
item.dispose();
|
|
80461
|
+
}
|
|
80462
|
+
/**
|
|
80463
|
+
* 按目标元素的实时包围盒更新加载动画变换。
|
|
80464
|
+
* @param id 目标元素 ID。
|
|
80465
|
+
* @param loadingItem 对应的加载渲染投影。
|
|
80466
|
+
*/ updateLoadingVFXItemTransform(id, loadingItem) {
|
|
80467
|
+
const item = getPlayerItemById(this._owner.getEngine().compositions[0], id);
|
|
80468
|
+
const containerSize = GizmoViewportUtils.getContainerSize(this._owner.getEngine().canvas.parentElement);
|
|
80469
|
+
const itemViewBox = item ? getItemViewBox(item, containerSize) : new Box2();
|
|
80470
|
+
const currentBox = getBoxByNormalizeBox(itemViewBox, loadingItem.loadingBox);
|
|
80471
|
+
const viewPosition = new Vector2((currentBox.max.x + currentBox.min.x) / 2, (currentBox.max.y + currentBox.min.y) / 2);
|
|
80472
|
+
const cameraInfo = this.getCameraInfo();
|
|
80473
|
+
const interactionPlane = new Plane(0, new Vector3(0, 0, 1));
|
|
80474
|
+
const worldPosition = viewPositionToWorld(viewPosition, cameraInfo, interactionPlane, containerSize);
|
|
80475
|
+
const viewSize = new Vector2(currentBox.max.x - currentBox.min.x, currentBox.max.y - currentBox.min.y);
|
|
80476
|
+
const worldSize = viewSizeToWorld(viewSize, containerSize, loadingItem.loadingVFXItem, cameraInfo, interactionPlane);
|
|
80477
|
+
if (worldPosition) {
|
|
80478
|
+
loadingItem.loadingVFXItem.transform.setPosition(worldPosition.x, worldPosition.y, 0);
|
|
80479
|
+
}
|
|
80480
|
+
loadingItem.loadingVFXItem.transform.setScale(worldSize.x, worldSize.y, 1);
|
|
80481
|
+
}
|
|
80482
|
+
/**
|
|
80483
|
+
* 创建加载覆盖层。
|
|
80484
|
+
* @param owner Gizmo 宿主。
|
|
80485
|
+
* @param manager 加载状态管理器。
|
|
80486
|
+
*/ constructor(owner, manager){
|
|
80487
|
+
super(owner), _define_property(this, "type", 'loading'), _define_property(this, "_configOff", void 0), _define_property(this, "_managerOff", void 0), _define_property(this, "_manager", void 0), _define_property(this, "_loadingBoxSize", new Vector2()), _define_property(this, "_idMap", new Map());
|
|
80488
|
+
this._manager = manager;
|
|
80489
|
+
this._configOff = owner.getConfigManager().onChange(loadingConfig, (change)=>{
|
|
80490
|
+
this._onConfigChange(change);
|
|
80491
|
+
});
|
|
80492
|
+
this._managerOff = this._manager.on('change', ()=>{
|
|
80493
|
+
this._syncManager();
|
|
80494
|
+
});
|
|
80495
|
+
this._syncManager();
|
|
80496
|
+
}
|
|
80497
|
+
}
|
|
80469
80498
|
/** 通过中键或抓手模式拖拽平移视口。 */ class HandGizmo extends Gizmo {
|
|
80470
80499
|
/** @returns 是否正在平移或等待空格拖拽。 */ isPanning() {
|
|
80471
80500
|
return this._dragType === "pan" || this._panKeyPressed;
|
|
@@ -83966,7 +83995,6 @@ const identityFrameItemIdAdapter = {
|
|
|
83966
83995
|
},
|
|
83967
83996
|
sceneBindings: []
|
|
83968
83997
|
};
|
|
83969
|
-
const [width, height] = size;
|
|
83970
83998
|
const composition = {
|
|
83971
83999
|
id: resolvedCompositionId,
|
|
83972
84000
|
name: name + '_inner',
|
|
@@ -83990,9 +84018,7 @@ const identityFrameItemIdAdapter = {
|
|
|
83990
84018
|
0,
|
|
83991
84019
|
0,
|
|
83992
84020
|
0
|
|
83993
|
-
]
|
|
83994
|
-
// @ts-expect-error: aspect 属性在 CameraOptions 上有定义但类型声明不完善
|
|
83995
|
-
aspect: width / height
|
|
84021
|
+
]
|
|
83996
84022
|
},
|
|
83997
84023
|
components: [
|
|
83998
84024
|
{
|
|
@@ -86946,8 +86972,9 @@ const identityFrameItemIdAdapter = {
|
|
|
86946
86972
|
if (width === undefined || height === undefined) {
|
|
86947
86973
|
return box;
|
|
86948
86974
|
}
|
|
86949
|
-
|
|
86950
|
-
const
|
|
86975
|
+
// 文本元素的scale不作为计算包围盒尺寸运算
|
|
86976
|
+
const sx = schema.type === ArtisItemType.TEXT ? 1 : scale?.[0] ?? 1;
|
|
86977
|
+
const sy = schema.type === ArtisItemType.TEXT ? 1 : scale?.[1] ?? 1;
|
|
86951
86978
|
const rot = Array.isArray(rotation) ? rotation[2] : rotation ?? 0;
|
|
86952
86979
|
rotateBox(box.setFromCenterAndSize(new Vector2(...position ?? [
|
|
86953
86980
|
0,
|
|
@@ -88436,6 +88463,7 @@ const identityFrameItemIdAdapter = {
|
|
|
88436
88463
|
...this._newTransform,
|
|
88437
88464
|
...other._newTransform
|
|
88438
88465
|
};
|
|
88466
|
+
this.eventPayload.propertyKeys = Object.keys(this._newTransform);
|
|
88439
88467
|
return true;
|
|
88440
88468
|
}
|
|
88441
88469
|
/**
|
|
@@ -88444,6 +88472,7 @@ const identityFrameItemIdAdapter = {
|
|
|
88444
88472
|
*/ constructor(args){
|
|
88445
88473
|
_define_property$1(this, "id", void 0);
|
|
88446
88474
|
_define_property$1(this, "commandId", 'item.transform');
|
|
88475
|
+
_define_property$1(this, "eventPayload", void 0);
|
|
88447
88476
|
_define_property$1(this, "_itemId", void 0);
|
|
88448
88477
|
_define_property$1(this, "_newTransform", void 0);
|
|
88449
88478
|
_define_property$1(this, "_oldTransform", void 0);
|
|
@@ -88454,6 +88483,10 @@ const identityFrameItemIdAdapter = {
|
|
|
88454
88483
|
this._newTransform = args.newTransform;
|
|
88455
88484
|
this._itemService = args.itemService;
|
|
88456
88485
|
this._store = args.store;
|
|
88486
|
+
this.eventPayload = {
|
|
88487
|
+
itemId: args.itemId,
|
|
88488
|
+
propertyKeys: Object.keys(args.newTransform)
|
|
88489
|
+
};
|
|
88457
88490
|
}
|
|
88458
88491
|
}
|
|
88459
88492
|
|
|
@@ -89348,6 +89381,7 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
89348
89381
|
*/ constructor(args){
|
|
89349
89382
|
_define_property$1(this, "id", void 0);
|
|
89350
89383
|
_define_property$1(this, "commandId", 'item.updatePropertyBySchema');
|
|
89384
|
+
_define_property$1(this, "eventPayload", void 0);
|
|
89351
89385
|
_define_property$1(this, "completion", Promise.resolve());
|
|
89352
89386
|
_define_property$1(this, "_schemas", void 0);
|
|
89353
89387
|
_define_property$1(this, "_itemService", void 0);
|
|
@@ -89357,6 +89391,14 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
89357
89391
|
args.schema
|
|
89358
89392
|
];
|
|
89359
89393
|
this._itemService = args.itemService;
|
|
89394
|
+
this.eventPayload = {
|
|
89395
|
+
propertyChanges: this._schemas.flatMap((schema)=>schema.id ? [
|
|
89396
|
+
{
|
|
89397
|
+
itemId: schema.id,
|
|
89398
|
+
propertyKeys: Object.keys(schema.property)
|
|
89399
|
+
}
|
|
89400
|
+
] : [])
|
|
89401
|
+
};
|
|
89360
89402
|
}
|
|
89361
89403
|
}
|
|
89362
89404
|
|
|
@@ -89403,6 +89445,7 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
89403
89445
|
*/ constructor(args){
|
|
89404
89446
|
_define_property$1(this, "id", void 0);
|
|
89405
89447
|
_define_property$1(this, "commandId", 'item.setFontFamily');
|
|
89448
|
+
_define_property$1(this, "eventPayload", void 0);
|
|
89406
89449
|
_define_property$1(this, "completion", Promise.resolve());
|
|
89407
89450
|
_define_property$1(this, "_itemId", void 0);
|
|
89408
89451
|
_define_property$1(this, "_fontFamily", void 0);
|
|
@@ -89416,6 +89459,13 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
89416
89459
|
this._fontFamily = args.fontFamily;
|
|
89417
89460
|
this._fontUrl = args.fontUrl;
|
|
89418
89461
|
this._itemService = args.itemService;
|
|
89462
|
+
this.eventPayload = {
|
|
89463
|
+
itemId: args.itemId,
|
|
89464
|
+
propertyKeys: [
|
|
89465
|
+
'fontFamily',
|
|
89466
|
+
'fontUrl'
|
|
89467
|
+
]
|
|
89468
|
+
};
|
|
89419
89469
|
}
|
|
89420
89470
|
}
|
|
89421
89471
|
|
|
@@ -90769,7 +90819,7 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
90769
90819
|
}
|
|
90770
90820
|
}
|
|
90771
90821
|
|
|
90772
|
-
/** 提交图片裁切区域变化的可撤销命令。 */ class
|
|
90822
|
+
/** 提交图片裁切区域变化的可撤销命令。 */ class ImageCutCommitCommand {
|
|
90773
90823
|
/** 应用新的裁切包围盒 */ execute() {
|
|
90774
90824
|
this._applyBox(this._newBox);
|
|
90775
90825
|
}
|
|
@@ -90784,18 +90834,18 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
90784
90834
|
* @param args 命令参数(含新旧裁切包围盒与 applyBox 回调)
|
|
90785
90835
|
*/ constructor(args){
|
|
90786
90836
|
_define_property$1(this, "id", void 0);
|
|
90787
|
-
_define_property$1(this, "commandId", '
|
|
90837
|
+
_define_property$1(this, "commandId", 'imageCut.commit');
|
|
90788
90838
|
_define_property$1(this, "_newBox", void 0);
|
|
90789
90839
|
_define_property$1(this, "_oldBox", void 0);
|
|
90790
90840
|
_define_property$1(this, "_applyBox", void 0);
|
|
90791
|
-
this.id = `
|
|
90841
|
+
this.id = `imageCut.commit_${Date.now()}`;
|
|
90792
90842
|
this._newBox = args.newBox;
|
|
90793
90843
|
this._oldBox = args.oldBox;
|
|
90794
90844
|
this._applyBox = args.applyBox;
|
|
90795
90845
|
}
|
|
90796
90846
|
}
|
|
90797
90847
|
|
|
90798
|
-
/** 提交图片扩边区域变化的可撤销命令。 */ class
|
|
90848
|
+
/** 提交图片扩边区域变化的可撤销命令。 */ class ImageExpandCommitCommand {
|
|
90799
90849
|
/** 应用新的扩图包围盒 */ execute() {
|
|
90800
90850
|
this._applyBox(this._newBox);
|
|
90801
90851
|
}
|
|
@@ -90810,11 +90860,11 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
90810
90860
|
* @param args 命令参数(含新旧扩图包围盒与 applyBox 回调)
|
|
90811
90861
|
*/ constructor(args){
|
|
90812
90862
|
_define_property$1(this, "id", void 0);
|
|
90813
|
-
_define_property$1(this, "commandId", '
|
|
90863
|
+
_define_property$1(this, "commandId", 'imageExpand.commit');
|
|
90814
90864
|
_define_property$1(this, "_newBox", void 0);
|
|
90815
90865
|
_define_property$1(this, "_oldBox", void 0);
|
|
90816
90866
|
_define_property$1(this, "_applyBox", void 0);
|
|
90817
|
-
this.id = `
|
|
90867
|
+
this.id = `imageExpand.commit_${Date.now()}`;
|
|
90818
90868
|
this._newBox = args.newBox;
|
|
90819
90869
|
this._oldBox = args.oldBox;
|
|
90820
90870
|
this._applyBox = args.applyBox;
|
|
@@ -91042,13 +91092,29 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
91042
91092
|
|
|
91043
91093
|
/** 按元素类型静默替换在线资源且不写入历史的命令。 */ class ItemReplaceSourceCommand {
|
|
91044
91094
|
/** 替换为新资源(async,execute 契约为 void,故 fire-and-forget) */ execute() {
|
|
91045
|
-
|
|
91095
|
+
this.completion = this._replaceSource(this._newUrl);
|
|
91046
91096
|
}
|
|
91047
91097
|
/** 回退为旧资源 */ undo() {
|
|
91048
|
-
|
|
91098
|
+
this.completion = this._replaceSource(this._oldUrl);
|
|
91049
91099
|
}
|
|
91050
91100
|
/** 重新应用新资源 */ redo() {
|
|
91051
|
-
|
|
91101
|
+
this.completion = this._replaceSource(this._newUrl);
|
|
91102
|
+
}
|
|
91103
|
+
/** 仅在资源实际替换成功时保留属性变化载荷。 */ async _replaceSource(url) {
|
|
91104
|
+
if (this._propertyKey) {
|
|
91105
|
+
this.eventPayload = {
|
|
91106
|
+
itemId: this._itemId,
|
|
91107
|
+
propertyKeys: [
|
|
91108
|
+
this._propertyKey
|
|
91109
|
+
]
|
|
91110
|
+
};
|
|
91111
|
+
}
|
|
91112
|
+
const changed = await this._itemService.replaceSourceSilent(this._itemId, url);
|
|
91113
|
+
if (!changed) {
|
|
91114
|
+
this.eventPayload = {
|
|
91115
|
+
propertyChanges: []
|
|
91116
|
+
};
|
|
91117
|
+
}
|
|
91052
91118
|
}
|
|
91053
91119
|
/** 将在线换源标记为不可重放,避免复现流程依赖外部资源时序。 */ serializeReplay() {
|
|
91054
91120
|
return {
|
|
@@ -91067,9 +91133,12 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
91067
91133
|
_define_property$1(this, "commandId", 'item.replaceSource');
|
|
91068
91134
|
_define_property$1(this, "isHistorical", false);
|
|
91069
91135
|
_define_property$1(this, "params", void 0);
|
|
91136
|
+
_define_property$1(this, "eventPayload", void 0);
|
|
91137
|
+
_define_property$1(this, "completion", Promise.resolve());
|
|
91070
91138
|
_define_property$1(this, "_itemId", void 0);
|
|
91071
91139
|
_define_property$1(this, "_newUrl", void 0);
|
|
91072
91140
|
_define_property$1(this, "_oldUrl", void 0);
|
|
91141
|
+
_define_property$1(this, "_propertyKey", void 0);
|
|
91073
91142
|
_define_property$1(this, "_itemService", void 0);
|
|
91074
91143
|
_define_property$1(this, "_store", void 0);
|
|
91075
91144
|
this.id = `item.replaceSource_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
@@ -91084,12 +91153,15 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
91084
91153
|
switch(item.type){
|
|
91085
91154
|
case ArtisItemType.SPRITE:
|
|
91086
91155
|
this._oldUrl = typeof prop?.image === 'string' ? prop.image : '';
|
|
91156
|
+
this._propertyKey = 'image';
|
|
91087
91157
|
break;
|
|
91088
91158
|
case ArtisItemType.VIDEO:
|
|
91089
91159
|
this._oldUrl = typeof prop?.video === 'string' ? prop.video : '';
|
|
91160
|
+
this._propertyKey = 'video';
|
|
91090
91161
|
break;
|
|
91091
91162
|
case ArtisItemType.EFFECTS:
|
|
91092
91163
|
this._oldUrl = typeof prop?.effects === 'string' ? prop.effects : '';
|
|
91164
|
+
this._propertyKey = 'effects';
|
|
91093
91165
|
break;
|
|
91094
91166
|
default:
|
|
91095
91167
|
this._oldUrl = '';
|
|
@@ -91097,6 +91169,14 @@ const DEFAULT_GROUP_POSITION = [
|
|
|
91097
91169
|
} else {
|
|
91098
91170
|
this._oldUrl = '';
|
|
91099
91171
|
}
|
|
91172
|
+
if (this._propertyKey) {
|
|
91173
|
+
this.eventPayload = {
|
|
91174
|
+
itemId: this._itemId,
|
|
91175
|
+
propertyKeys: [
|
|
91176
|
+
this._propertyKey
|
|
91177
|
+
]
|
|
91178
|
+
};
|
|
91179
|
+
}
|
|
91100
91180
|
this.params = {
|
|
91101
91181
|
itemId: this._itemId,
|
|
91102
91182
|
newUrl: this._newUrl,
|
|
@@ -91991,7 +92071,7 @@ function hasTransformItemIds(event) {
|
|
|
91991
92071
|
* @param context 提供当前 Selection、ID 翻译与像素框查询
|
|
91992
92072
|
* @param normalizeBox Gizmo 维护的归一化编辑框
|
|
91993
92073
|
* @returns 图片像素坐标系下的包围盒;输入无效时返回空盒
|
|
91994
|
-
*/ const
|
|
92074
|
+
*/ const getSelectedImagePixelBox = (context, normalizeBox)=>{
|
|
91995
92075
|
const selectedId = context.selection.getSelectedIds()[0];
|
|
91996
92076
|
if (!normalizeBox || selectedId === undefined) {
|
|
91997
92077
|
return new Box2();
|
|
@@ -92003,14 +92083,14 @@ function hasTransformItemIds(event) {
|
|
|
92003
92083
|
* 创建图片裁剪事件绑定,以每个 Gizmo 实例的旧框快照生成可撤销命令。
|
|
92004
92084
|
* @param context Command 绑定所需的 Artis 服务上下文
|
|
92005
92085
|
* @returns 图片裁剪生命周期事件处理器
|
|
92006
|
-
*/ const
|
|
92086
|
+
*/ const createImageCutCommandBinding = (context)=>{
|
|
92007
92087
|
const oldBoxBySource = new WeakMap();
|
|
92008
92088
|
/**
|
|
92009
92089
|
* 读取当前裁剪框并转换为可序列化快照。
|
|
92010
|
-
* @param
|
|
92090
|
+
* @param imageCutGizmo 要读取的裁剪 Gizmo
|
|
92011
92091
|
* @returns 归一化裁剪框;Gizmo 尚无框时返回 undefined
|
|
92012
|
-
*/ const snapshot = (
|
|
92013
|
-
const box =
|
|
92092
|
+
*/ const snapshot = (imageCutGizmo)=>{
|
|
92093
|
+
const box = imageCutGizmo.getCutBox();
|
|
92014
92094
|
if (!box) {
|
|
92015
92095
|
return undefined;
|
|
92016
92096
|
}
|
|
@@ -92023,10 +92103,10 @@ function hasTransformItemIds(event) {
|
|
|
92023
92103
|
};
|
|
92024
92104
|
/**
|
|
92025
92105
|
* 将归一化快照恢复为 Gizmo 使用的 Box2。
|
|
92026
|
-
* @param
|
|
92106
|
+
* @param imageCutGizmo 要恢复的裁剪 Gizmo
|
|
92027
92107
|
* @param box 命令保存的归一化裁剪框
|
|
92028
|
-
*/ const apply = (
|
|
92029
|
-
|
|
92108
|
+
*/ const apply = (imageCutGizmo, box)=>{
|
|
92109
|
+
imageCutGizmo.setCutBox(new Box2(new Vector2(box.x, box.y), new Vector2(box.x + box.width, box.y + box.height)));
|
|
92030
92110
|
};
|
|
92031
92111
|
return {
|
|
92032
92112
|
/**
|
|
@@ -92044,8 +92124,8 @@ function hasTransformItemIds(event) {
|
|
|
92044
92124
|
* @param event 图片裁剪 actionupdate 事件
|
|
92045
92125
|
*/ actionupdate: (event)=>{
|
|
92046
92126
|
const normalizeBox = event.source.getCutBox();
|
|
92047
|
-
context.emit('
|
|
92048
|
-
box:
|
|
92127
|
+
context.emit('image-cut.change', {
|
|
92128
|
+
box: getSelectedImagePixelBox(context, normalizeBox)
|
|
92049
92129
|
});
|
|
92050
92130
|
},
|
|
92051
92131
|
/**
|
|
@@ -92059,7 +92139,7 @@ function hasTransformItemIds(event) {
|
|
|
92059
92139
|
if (!oldBox || !newBox) {
|
|
92060
92140
|
return;
|
|
92061
92141
|
}
|
|
92062
|
-
context.commandService.record(new
|
|
92142
|
+
context.commandService.record(new ImageCutCommitCommand({
|
|
92063
92143
|
newBox,
|
|
92064
92144
|
oldBox,
|
|
92065
92145
|
/**
|
|
@@ -92076,14 +92156,14 @@ function hasTransformItemIds(event) {
|
|
|
92076
92156
|
* 创建图片扩边事件绑定,以每个 Gizmo 实例的旧框快照生成可撤销命令。
|
|
92077
92157
|
* @param context Command 绑定所需的 Artis 服务上下文
|
|
92078
92158
|
* @returns 图片扩边生命周期事件处理器
|
|
92079
|
-
*/ const
|
|
92159
|
+
*/ const createImageExpandCommandBinding = (context)=>{
|
|
92080
92160
|
const oldBoxBySource = new WeakMap();
|
|
92081
92161
|
/**
|
|
92082
92162
|
* 读取当前扩边框并转换为可序列化快照。
|
|
92083
|
-
* @param
|
|
92163
|
+
* @param imageExpandGizmo 要读取的扩边 Gizmo
|
|
92084
92164
|
* @returns 归一化扩边框;Gizmo 尚无框时返回 undefined
|
|
92085
|
-
*/ const snapshot = (
|
|
92086
|
-
const box =
|
|
92165
|
+
*/ const snapshot = (imageExpandGizmo)=>{
|
|
92166
|
+
const box = imageExpandGizmo.getExpandBox();
|
|
92087
92167
|
if (!box) {
|
|
92088
92168
|
return undefined;
|
|
92089
92169
|
}
|
|
@@ -92117,8 +92197,8 @@ function hasTransformItemIds(event) {
|
|
|
92117
92197
|
* @param event 图片扩边 actionupdate 事件
|
|
92118
92198
|
*/ actionupdate: (event)=>{
|
|
92119
92199
|
const normalizeBox = event.source.getExpandBox();
|
|
92120
|
-
context.emit('
|
|
92121
|
-
box:
|
|
92200
|
+
context.emit('image-expand.change', {
|
|
92201
|
+
box: getSelectedImagePixelBox(context, normalizeBox)
|
|
92122
92202
|
});
|
|
92123
92203
|
},
|
|
92124
92204
|
/**
|
|
@@ -92132,7 +92212,7 @@ function hasTransformItemIds(event) {
|
|
|
92132
92212
|
if (!oldBox || !newBox) {
|
|
92133
92213
|
return;
|
|
92134
92214
|
}
|
|
92135
|
-
context.commandService.record(new
|
|
92215
|
+
context.commandService.record(new ImageExpandCommitCommand({
|
|
92136
92216
|
newBox,
|
|
92137
92217
|
oldBox,
|
|
92138
92218
|
/**
|
|
@@ -92193,8 +92273,8 @@ const builtinCommandBindingFactories = {
|
|
|
92193
92273
|
[GizmoType.BOX_SELECTION]: registerSelectionGizmoCommandBinding,
|
|
92194
92274
|
'item-create': createItemCreateCommandBinding,
|
|
92195
92275
|
mask: createMaskCommandBinding,
|
|
92196
|
-
'
|
|
92197
|
-
'
|
|
92276
|
+
'image-cut': createImageCutCommandBinding,
|
|
92277
|
+
'image-expand': createImageExpandCommandBinding
|
|
92198
92278
|
};
|
|
92199
92279
|
/**
|
|
92200
92280
|
* 将全部内置 Gizmo 类型与对应的 Command 绑定工厂注册到 Binder。
|
|
@@ -92403,8 +92483,8 @@ var BuiltinGizmoId = /*#__PURE__*/ function(BuiltinGizmoId) {
|
|
|
92403
92483
|
BuiltinGizmoId["LOADING"] = "loading";
|
|
92404
92484
|
BuiltinGizmoId["VIEWPORT_OVERLAY"] = "viewport-overlay";
|
|
92405
92485
|
BuiltinGizmoId["READONLY_SELECTION"] = "readonly-selection";
|
|
92406
|
-
BuiltinGizmoId["
|
|
92407
|
-
BuiltinGizmoId["
|
|
92486
|
+
BuiltinGizmoId["IMAGE_CUT"] = "image-cut";
|
|
92487
|
+
BuiltinGizmoId["IMAGE_EXPAND"] = "image-expand";
|
|
92408
92488
|
BuiltinGizmoId["SPRITE_TEXT_EDIT"] = "sprite-text-edit";
|
|
92409
92489
|
BuiltinGizmoId["MASK"] = "mask";
|
|
92410
92490
|
BuiltinGizmoId["ITEM_CREATE"] = "item-create";
|
|
@@ -101938,13 +102018,13 @@ var OfficialStateId = /*#__PURE__*/ function(OfficialStateId) {
|
|
|
101938
102018
|
}
|
|
101939
102019
|
/** 表示编辑图片文字内容的状态。 */ class ImageTextEditState extends BaseState {
|
|
101940
102020
|
/** 创建隔离其他交互的图片文字编辑状态。 */ constructor(){
|
|
101941
|
-
super(OfficialStateId.IMAGE_TEXT_EDIT, '精准改字',
|
|
102021
|
+
super(OfficialStateId.IMAGE_TEXT_EDIT, '精准改字', SpriteTextEditGizmoTool);
|
|
101942
102022
|
this.isolated = true;
|
|
101943
102023
|
}
|
|
101944
102024
|
}
|
|
101945
102025
|
/** 表示编辑图片蒙版的状态。 */ class ImageMaskState extends BaseState {
|
|
101946
102026
|
/** 创建隔离其他交互的图片蒙版状态。 */ constructor(){
|
|
101947
|
-
super(OfficialStateId.IMAGE_MASK, '图片蒙版',
|
|
102027
|
+
super(OfficialStateId.IMAGE_MASK, '图片蒙版', MaskGizmoTool);
|
|
101948
102028
|
this.isolated = true;
|
|
101949
102029
|
}
|
|
101950
102030
|
}
|
|
@@ -123706,6 +123786,12 @@ const CARD_HTML_EVENT_MESSAGE_SOURCE = 'vvfx-card-html-event';
|
|
|
123706
123786
|
this.artis.leaveTextEditing();
|
|
123707
123787
|
}
|
|
123708
123788
|
/**
|
|
123789
|
+
* 选中当前 TextGizmo 正在编辑的全部文字,并聚焦文本输入框。
|
|
123790
|
+
* @returns TextGizmo 已就绪且存在正在编辑的文本时返回 true
|
|
123791
|
+
*/ selectAllText() {
|
|
123792
|
+
return this.artis.gizmoManager.get('text')?.selectAllText() ?? false;
|
|
123793
|
+
}
|
|
123794
|
+
/**
|
|
123709
123795
|
* 应用笔刷大小后开启蒙版 Gizmo;非法尺寸回退为 1。
|
|
123710
123796
|
* @param brushSize 初始笔刷像素大小
|
|
123711
123797
|
*/ openMaskGizmo(brushSize) {
|
|
@@ -123753,69 +123839,69 @@ const CARD_HTML_EVENT_MESSAGE_SOURCE = 'vvfx-card-html-event';
|
|
|
123753
123839
|
*/ closeLoadingGizmo(id) {
|
|
123754
123840
|
this.artis.gestureHandler.getLoadingManager().delete(this.artis.item.toVfxItemId(id));
|
|
123755
123841
|
}
|
|
123756
|
-
/** 开启图片裁切 Gizmo。 */
|
|
123842
|
+
/** 开启图片裁切 Gizmo。 */ openImageCutGizmo() {
|
|
123757
123843
|
this.artis.states.activate('imageCut');
|
|
123758
123844
|
}
|
|
123759
123845
|
/**
|
|
123760
123846
|
* 设置图片裁切 Gizmo 是否锁定裁切框比例。
|
|
123761
123847
|
* @param lock 是否锁定比例
|
|
123762
|
-
*/
|
|
123763
|
-
const
|
|
123764
|
-
if (!
|
|
123765
|
-
console.warn('[Artis.operations]
|
|
123848
|
+
*/ setImageCutGizmoLockScale(lock) {
|
|
123849
|
+
const imageCutGizmo = this.artis.gizmoManager.get('image-cut');
|
|
123850
|
+
if (!imageCutGizmo) {
|
|
123851
|
+
console.warn('[Artis.operations] setImageCutGizmoLockScale:image-cut Gizmo 未注册或未启用。');
|
|
123766
123852
|
return;
|
|
123767
123853
|
}
|
|
123768
|
-
|
|
123854
|
+
imageCutGizmo.isLockScale = lock;
|
|
123769
123855
|
}
|
|
123770
123856
|
/**
|
|
123771
|
-
* 获取当前裁切信息(归一化裁切框 + 元素视图框),委托
|
|
123857
|
+
* 获取当前裁切信息(归一化裁切框 + 元素视图框),委托 ImageCutGizmo.getCutInfo。
|
|
123772
123858
|
* @returns 裁切信息;Gizmo 未就绪 / 未选中单个元素 / 结果无效时返回 undefined
|
|
123773
123859
|
*/ getCutInfo() {
|
|
123774
|
-
return this.artis.gizmoManager.get('
|
|
123860
|
+
return this.artis.gizmoManager.get('image-cut')?.getCutInfo();
|
|
123775
123861
|
}
|
|
123776
123862
|
/**
|
|
123777
|
-
* 获取归一化裁切包围盒(min/max ∈ [0,1],相对元素包围盒),委托
|
|
123863
|
+
* 获取归一化裁切包围盒(min/max ∈ [0,1],相对元素包围盒),委托 ImageCutGizmo.getCutBox。
|
|
123778
123864
|
* @returns 归一化裁切框;Gizmo 未就绪时返回 undefined
|
|
123779
123865
|
*/ getCutBox() {
|
|
123780
|
-
return this.artis.gizmoManager.get('
|
|
123866
|
+
return this.artis.gizmoManager.get('image-cut')?.getCutBox();
|
|
123781
123867
|
}
|
|
123782
123868
|
/**
|
|
123783
|
-
* 设置归一化裁切包围盒(入参为归一化坐标),委托
|
|
123869
|
+
* 设置归一化裁切包围盒(入参为归一化坐标),委托 ImageCutGizmo.setCutBox。
|
|
123784
123870
|
* @param normalizeBox 归一化裁切框
|
|
123785
123871
|
*/ setCutBox(normalizeBox) {
|
|
123786
|
-
this.artis.gizmoManager.get('
|
|
123872
|
+
this.artis.gizmoManager.get('image-cut')?.setCutBox(normalizeBox);
|
|
123787
123873
|
}
|
|
123788
|
-
/** 关闭图片裁切 Gizmo。 */
|
|
123874
|
+
/** 关闭图片裁切 Gizmo。 */ closeImageCutGizmo() {
|
|
123789
123875
|
this.artis.states.activate('move');
|
|
123790
123876
|
}
|
|
123791
123877
|
/**
|
|
123792
123878
|
* 设置图片扩边 Gizmo 是否锁定扩边框比例。
|
|
123793
123879
|
* @param lock 是否锁定比例
|
|
123794
|
-
*/
|
|
123795
|
-
const
|
|
123796
|
-
if (!
|
|
123797
|
-
console.warn('[Artis.operations]
|
|
123880
|
+
*/ setImageExpandGizmoLockScale(lock) {
|
|
123881
|
+
const imageExpandGizmo = this.artis.gizmoManager.get('image-expand');
|
|
123882
|
+
if (!imageExpandGizmo) {
|
|
123883
|
+
console.warn('[Artis.operations] setImageExpandGizmoLockScale:image-expand Gizmo 未注册或未启用。');
|
|
123798
123884
|
return;
|
|
123799
123885
|
}
|
|
123800
|
-
|
|
123886
|
+
imageExpandGizmo.isLockScale = lock;
|
|
123801
123887
|
}
|
|
123802
123888
|
/**
|
|
123803
|
-
* 获取当前扩边信息(归一化扩边框 + 元素视图框),委托
|
|
123889
|
+
* 获取当前扩边信息(归一化扩边框 + 元素视图框),委托 ImageExpandGizmo.getExpandInfo。
|
|
123804
123890
|
* @returns 扩边信息;Gizmo 未就绪 / 未选中单个元素 / 结果无效时返回 undefined
|
|
123805
123891
|
*/ getExpandInfo() {
|
|
123806
|
-
return this.artis.gizmoManager.get('
|
|
123892
|
+
return this.artis.gizmoManager.get('image-expand')?.getExpandInfo();
|
|
123807
123893
|
}
|
|
123808
123894
|
/**
|
|
123809
|
-
* 获取归一化扩边包围盒(相对元素包围盒,可超出 [0,1]),委托
|
|
123895
|
+
* 获取归一化扩边包围盒(相对元素包围盒,可超出 [0,1]),委托 ImageExpandGizmo.getExpandBox。
|
|
123810
123896
|
* @returns 归一化扩边框;Gizmo 未就绪时返回 undefined
|
|
123811
123897
|
*/ getExpandBox() {
|
|
123812
|
-
return this.artis.gizmoManager.get('
|
|
123898
|
+
return this.artis.gizmoManager.get('image-expand')?.getExpandBox();
|
|
123813
123899
|
}
|
|
123814
123900
|
/**
|
|
123815
|
-
* 设置归一化扩边包围盒(入参为归一化坐标),委托
|
|
123901
|
+
* 设置归一化扩边包围盒(入参为归一化坐标),委托 ImageExpandGizmo.setExpandBox。
|
|
123816
123902
|
* @param normalizeBox 归一化扩边框
|
|
123817
123903
|
*/ setExpandBox(normalizeBox) {
|
|
123818
|
-
this.artis.gizmoManager.get('
|
|
123904
|
+
this.artis.gizmoManager.get('image-expand')?.setExpandBox(normalizeBox);
|
|
123819
123905
|
}
|
|
123820
123906
|
/**
|
|
123821
123907
|
* 绑定操作门面所属的 Artis 实例。
|
|
@@ -123900,8 +123986,8 @@ const CARD_HTML_EVENT_MESSAGE_SOURCE = 'vvfx-card-html-event';
|
|
|
123900
123986
|
createSelectionInteractionGizmo(owner)
|
|
123901
123987
|
];
|
|
123902
123988
|
});
|
|
123903
|
-
this.registry.register(BuiltinGizmoId.
|
|
123904
|
-
this.registry.register(BuiltinGizmoId.
|
|
123989
|
+
this.registry.register(BuiltinGizmoId.IMAGE_CUT, ({ owner })=>new ImageCutGizmo(owner));
|
|
123990
|
+
this.registry.register(BuiltinGizmoId.IMAGE_EXPAND, ({ owner })=>new ImageExpandGizmo(owner));
|
|
123905
123991
|
this.registry.register(BuiltinGizmoId.SPRITE_TEXT_EDIT, ({ owner })=>new SpriteTextEditGizmo(owner));
|
|
123906
123992
|
this.registry.register(BuiltinGizmoId.MASK, ({ owner })=>new MaskGizmo(owner));
|
|
123907
123993
|
this.registry.register(BuiltinGizmoId.ITEM_CREATE, ({ owner })=>{
|
|
@@ -124429,6 +124515,26 @@ const CARD_HTML_EVENT_MESSAGE_SOURCE = 'vvfx-card-html-event';
|
|
|
124429
124515
|
});
|
|
124430
124516
|
}
|
|
124431
124517
|
|
|
124518
|
+
/** 从 Command 的标准载荷中提取单个或批量属性变化。 */ function getItemPropertyChanges(payload) {
|
|
124519
|
+
const changes = Array.isArray(payload?.propertyChanges) ? payload.propertyChanges : [
|
|
124520
|
+
payload
|
|
124521
|
+
];
|
|
124522
|
+
return changes.flatMap((change)=>{
|
|
124523
|
+
if (!change || typeof change !== 'object') {
|
|
124524
|
+
return [];
|
|
124525
|
+
}
|
|
124526
|
+
const { itemId, propertyKeys, propertyKey } = change;
|
|
124527
|
+
const keys = Array.isArray(propertyKeys) ? propertyKeys.filter((key)=>typeof key === 'string') : typeof propertyKey === 'string' ? [
|
|
124528
|
+
propertyKey
|
|
124529
|
+
] : [];
|
|
124530
|
+
return typeof itemId === 'string' && keys.length > 0 ? [
|
|
124531
|
+
{
|
|
124532
|
+
itemId,
|
|
124533
|
+
propertyKeys: keys
|
|
124534
|
+
}
|
|
124535
|
+
] : [];
|
|
124536
|
+
});
|
|
124537
|
+
}
|
|
124432
124538
|
/**
|
|
124433
124539
|
* 注册内置 Command,并配置命令执行后、历史变化后的同步与事件转发。
|
|
124434
124540
|
* @param ctx 已完成核心服务装配的 wiring 上下文
|
|
@@ -124464,22 +124570,20 @@ const CARD_HTML_EVENT_MESSAGE_SOURCE = 'vvfx-card-html-event';
|
|
|
124464
124570
|
}
|
|
124465
124571
|
ctx.emit('history.change', operation);
|
|
124466
124572
|
});
|
|
124467
|
-
//
|
|
124573
|
+
// 任何提供标准属性载荷的命令,在实际投影完成后共用同一事件出口。
|
|
124468
124574
|
ctx.commandService.onCommandLifecycle(({ command, payload })=>{
|
|
124469
|
-
if (
|
|
124470
|
-
return;
|
|
124471
|
-
}
|
|
124472
|
-
const id = payload?.itemId;
|
|
124473
|
-
const propertyKeys = Array.isArray(payload?.propertyKeys) ? payload.propertyKeys.filter((key)=>typeof key === 'string') : typeof payload?.propertyKey === 'string' ? [
|
|
124474
|
-
payload.propertyKey
|
|
124475
|
-
] : [];
|
|
124476
|
-
if (typeof id !== 'string' || propertyKeys.length === 0) {
|
|
124575
|
+
if (getItemPropertyChanges(payload).length === 0) {
|
|
124477
124576
|
return;
|
|
124478
124577
|
}
|
|
124479
|
-
|
|
124480
|
-
|
|
124481
|
-
propertyKeys
|
|
124482
|
-
|
|
124578
|
+
void (command.completion ?? Promise.resolve()).then(()=>{
|
|
124579
|
+
const changes = getItemPropertyChanges(command.eventPayload ?? payload);
|
|
124580
|
+
for (const { itemId, propertyKeys } of changes){
|
|
124581
|
+
ctx.emit('item.propertychange', {
|
|
124582
|
+
id: itemId,
|
|
124583
|
+
propertyKeys
|
|
124584
|
+
});
|
|
124585
|
+
}
|
|
124586
|
+
}).catch(()=>undefined);
|
|
124483
124587
|
});
|
|
124484
124588
|
}
|
|
124485
124589
|
|
|
@@ -125697,8 +125801,8 @@ const BUILTIN_DEFS = [
|
|
|
125697
125801
|
this._emitBase('item.dragend', type);
|
|
125698
125802
|
break;
|
|
125699
125803
|
}
|
|
125700
|
-
case '
|
|
125701
|
-
case '
|
|
125804
|
+
case 'image-cut.change':
|
|
125805
|
+
case 'image-expand.change':
|
|
125702
125806
|
this._handleVariantGizmoEvent(event, args);
|
|
125703
125807
|
break;
|
|
125704
125808
|
default:
|
|
@@ -125985,9 +126089,9 @@ const BUILTIN_DEFS = [
|
|
|
125985
126089
|
* @param args 事件携带的参数列表
|
|
125986
126090
|
* @internal
|
|
125987
126091
|
*/ _handleVariantGizmoEvent(event, args) {
|
|
125988
|
-
if (event === '
|
|
126092
|
+
if (event === 'image-cut.change') {
|
|
125989
126093
|
this.emit(event, ...args);
|
|
125990
|
-
} else if (event === '
|
|
126094
|
+
} else if (event === 'image-expand.change') {
|
|
125991
126095
|
this.emit(event, ...args);
|
|
125992
126096
|
}
|
|
125993
126097
|
}
|
|
@@ -125997,11 +126101,11 @@ const BUILTIN_DEFS = [
|
|
|
125997
126101
|
* @internal
|
|
125998
126102
|
*/ _handleStateChange(event) {
|
|
125999
126103
|
const { previousState, currentState, source } = event;
|
|
126000
|
-
const
|
|
126001
|
-
const
|
|
126104
|
+
const isImageCut = currentState.id === OfficialStateId.IMAGE_CUT;
|
|
126105
|
+
const isImageExpand = currentState.id === OfficialStateId.IMAGE_EXPAND;
|
|
126002
126106
|
const isItemCreate = currentState.id === OfficialStateId.ITEM_CREATE;
|
|
126003
126107
|
let eventSource = source;
|
|
126004
|
-
if (
|
|
126108
|
+
if (isImageCut || isImageExpand) {
|
|
126005
126109
|
const selectedId = this.selection.getSelectedIds()[0];
|
|
126006
126110
|
eventSource = {
|
|
126007
126111
|
...source,
|
|
@@ -126871,7 +126975,9 @@ exports.HistoryManager = HistoryManager;
|
|
|
126871
126975
|
exports.IOPlugin = IOPlugin;
|
|
126872
126976
|
exports.IdentityStrategy = IdentityStrategy;
|
|
126873
126977
|
exports.IgnoredInteractionError = IgnoredInteractionError;
|
|
126978
|
+
exports.ImageCutCommitCommand = ImageCutCommitCommand;
|
|
126874
126979
|
exports.ImageCutState = ImageCutState;
|
|
126980
|
+
exports.ImageExpandCommitCommand = ImageExpandCommitCommand;
|
|
126875
126981
|
exports.ImageExpandState = ImageExpandState;
|
|
126876
126982
|
exports.ImageMaskState = ImageMaskState;
|
|
126877
126983
|
exports.ImageTextEditState = ImageTextEditState;
|
|
@@ -126908,8 +127014,6 @@ exports.PageAutoLayoutCommand = PageAutoLayoutCommand;
|
|
|
126908
127014
|
exports.PerfMonitor = PerfMonitor;
|
|
126909
127015
|
exports.PerfMonitorFacade = PerfMonitorFacade;
|
|
126910
127016
|
exports.PerfPhase = PerfPhase;
|
|
126911
|
-
exports.PictureCutCommitCommand = PictureCutCommitCommand;
|
|
126912
|
-
exports.PictureExpandCommitCommand = PictureExpandCommitCommand;
|
|
126913
127017
|
exports.Plane = Plane;
|
|
126914
127018
|
exports.PlayerAdapter = PlayerAdapter;
|
|
126915
127019
|
exports.PluginSystem = PluginSystem;
|
|
@@ -127028,6 +127132,8 @@ exports.getUniqueName = getUniqueName;
|
|
|
127028
127132
|
exports.getVector2Angle = getVector2Angle;
|
|
127029
127133
|
exports.gizmoConfig = gizmoConfig;
|
|
127030
127134
|
exports.globalAutoLayout = globalAutoLayout;
|
|
127135
|
+
exports.imageCutConfig = imageCutConfig;
|
|
127136
|
+
exports.imageExpandConfig = imageExpandConfig;
|
|
127031
127137
|
exports.isBaseItem = isBaseItem;
|
|
127032
127138
|
exports.isCardItem = isCardItem;
|
|
127033
127139
|
exports.isEffectsItem = isEffectsItem;
|
|
@@ -127050,8 +127156,6 @@ exports.moveItemOutOfFrame = moveItemOutOfFrame;
|
|
|
127050
127156
|
exports.moveItemToFrame = moveItemToFrame;
|
|
127051
127157
|
exports.moveItemToRoot = moveItemToRoot;
|
|
127052
127158
|
exports.parseCombo = parseCombo;
|
|
127053
|
-
exports.pictureCutConfig = pictureCutConfig;
|
|
127054
|
-
exports.pictureExpandConfig = pictureExpandConfig;
|
|
127055
127159
|
exports.registerCommands = registerCommands;
|
|
127056
127160
|
exports.removeItemInfoFromScene = removeItemInfoFromScene;
|
|
127057
127161
|
exports.resetSubCompositionItemId = resetSubCompositionItemId;
|