Skip to content

Commit ff50a30

Browse files
committed
#171 MovieClipへの変換処理を実装(WIP)
1 parent e7f6f38 commit ff50a30

8 files changed

Lines changed: 190 additions & 28 deletions

File tree

src/css/convert-movie-clip.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
background-color : #363636;
1212
color : whitesmoke;
1313
border : 1px solid #72777D;
14-
z-index : 9999;
14+
z-index : 65535;
1515
overflow : hidden;
1616
border-radius : 4px;
1717
box-shadow : 0 0 5px rgba(245, 245, 245, 0.25);

src/js/external/core/application/ExternalLayer/usecase/ExternalLayerAddCharacterUseCase.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Character } from "@/core/domain/model/Character";
22
import type { Layer } from "@/core/domain/model/Layer";
33
import type { MovieClip } from "@/core/domain/model/MovieClip";
44
import type { WorkSpace } from "@/core/domain/model/WorkSpace";
5-
import type { ExternalCharacter } from "@/external/core/domain/model/ExternalCharacter";
5+
import { ExternalCharacter } from "@/external/core/domain/model/ExternalCharacter";
66
import { execute as timelineLayerFrameAddKeyframeHistoryUseCase } from "@/history/application/timeline/application/TimelineLayerFrame/AddKeyframe/usecase/TimelineLayerFrameAddKeyframeHistoryUseCase";
77
import { execute as cacheRemoveService } from "@/cache/service/CacheRemoveService";
88
import { execute as viewTimelineLayerFrameAddKeyFrameUseCase } from "@/view/timeline/TimelineLayerFrame/usecase/ViewTimelineLayerFrameAddKeyFrameUseCase";
@@ -16,7 +16,7 @@ import { execute as externalTimelineLayerFrameSplitToEmptyUseCase } from "@/exte
1616
* @param {Layer} layer
1717
* @param {ExternalCharacter} external_character
1818
* @param {boolean} [receiver=false]
19-
* @return {Promise<void>}
19+
* @return {Promise<ExternalCharacter>}
2020
* @method
2121
* @public
2222
*/
@@ -27,7 +27,7 @@ export const execute = async (
2727
external_character: ExternalCharacter,
2828
depth: number = 0,
2929
receiver: boolean = false
30-
): Promise<void> => {
30+
): Promise<ExternalCharacter> => {
3131

3232
const character = new Character(external_character.id);
3333
character.load(external_character.toObject());
@@ -82,4 +82,11 @@ export const execute = async (
8282
layer,
8383
character
8484
);
85+
86+
return new ExternalCharacter(
87+
work_space,
88+
movie_clip,
89+
layer,
90+
character
91+
);
8592
};

src/js/external/core/domain/model/ExternalLayer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,8 @@ export class ExternalLayer
274274
async addCharacter (
275275
external_character: ExternalCharacter,
276276
depth: number = 0
277-
): Promise<void> {
278-
await externalLayerAddCharacterUseCase(
277+
): Promise<ExternalCharacter> {
278+
return await externalLayerAddCharacterUseCase(
279279
this._$workSpace,
280280
this._$movieClip,
281281
this._$layer,

src/js/menu/application/ConvertMovieClipModal/ConvertMovieClipModalUtil.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11

22
/**
3-
* @description ConvertMovieClipModalの中心点の入力チェック状態
4-
* Input check state of ConvertMovieClipModal center point
3+
* @description ConvertMovieClipModalの選択した中心点のElement ID
4+
* Selected center point Element ID of ConvertMovieClipModal
55
*
6-
* @type {boolean}
6+
* @type {string}
77
* @private
88
*/
9-
let $selected: boolean = false;
9+
let $selectedElementId: string = "";
1010

1111
/**
1212
* @description ConvertMovieClipModalの入力チェック状態
@@ -17,6 +17,19 @@ let $selected: boolean = false;
1717
*/
1818
let $validValue: boolean = false;
1919

20+
/**
21+
* @description ConvertMovieClipModalの選択した中心点のElement IDを取得
22+
* Get the selected center point Element ID of ConvertMovieClipModal
23+
*
24+
* @return {string}
25+
* @method
26+
* @public
27+
*/
28+
export const $getSelectedElementId = (): string =>
29+
{
30+
return $selectedElementId;
31+
};
32+
2033
/**
2134
* @description ConvertMovieClipModalの入力チェック状態を初期化
2235
* Initialize the input check state of ConvertMovieClipModal
@@ -28,20 +41,21 @@ let $validValue: boolean = false;
2841
export const $resetState = (): void =>
2942
{
3043
$validValue = false;
31-
$selected = false;
44+
$selectedElementId = "";
3245
};
3346

3447
/**
3548
* @description ConvertMovieClipModalの入力チェック状態を取得
3649
* Get the input check state of ConvertMovieClipModal
3750
*
51+
* @param {string} selected_element_id
3852
* @return {void}
3953
* @method
4054
* @public
4155
*/
42-
export const $selectReference = (): void =>
56+
export const $selectReference = (selected_element_id: string): void =>
4357
{
44-
$selected = true;
58+
$selectedElementId = selected_element_id;
4559
};
4660

4761
/**
@@ -68,5 +82,5 @@ export const $verifyValue = (valid: boolean): void =>
6882
*/
6983
export const $canProceed = (): boolean =>
7084
{
71-
return $selected && $validValue;
85+
return $selectedElementId !== "" && $validValue;
7286
};
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { Character } from "@/core/domain/model/Character";
2+
import type { IPosition } from "@/interface/IPosition";
3+
import type { IBounds } from "@/interface/IBounds";
4+
import { $getSelectedElementId } from "../ConvertMovieClipModalUtil";
5+
6+
/**
7+
* @description アンカー位置の割合
8+
* Anchor position ratio
9+
*
10+
* @type {Record<string, [number, number]>}
11+
* @private
12+
*/
13+
const $anchorFrac: Record<string, [number, number]> = {
14+
"top-left": [0, 0],
15+
"middle-left": [0.5, 0],
16+
"bottom-left": [1, 0],
17+
"top-center": [0, 0.5],
18+
"middle-center": [0.5, 0.5],
19+
"bottom-center": [1, 0.5],
20+
"top-right": [0, 1],
21+
"middle-right": [0.5, 1],
22+
"bottom-right": [1, 1]
23+
};
24+
25+
/**
26+
* @description ConvertMovieClipModalの選択した中心点に合わせてDisplayObjectの位置を計算
27+
* Calculate the position of the DisplayObject according to the selected center point of ConvertMovieClipModal
28+
*
29+
* @param {Character} character
30+
* @return {IPosition | null}
31+
* @method
32+
* @public
33+
*/
34+
export const execute = (character: Character, bounds: IBounds): IPosition =>
35+
{
36+
const [ax, ay] = $anchorFrac[$getSelectedElementId() as keyof typeof $anchorFrac];
37+
const offsetX = bounds.xMin + Math.abs(bounds.xMax - bounds.xMin) * ax;
38+
const offsetY = bounds.yMin + Math.abs(bounds.yMax - bounds.yMin) * ay;
39+
40+
return {
41+
"x": character.x - offsetX,
42+
"y": character.y - offsetY
43+
};
44+
};

src/js/menu/application/ConvertMovieClipModal/usecase/ConvertMovieClipModalButtonPointerDownEventUseCase.ts

Lines changed: 105 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
import type { Layer } from "@/core/domain/model/Layer";
2+
import { Character } from "@/core/domain/model/Character";
3+
import { ExternalCharacter } from "@/external/core/domain/model/ExternalCharacter";
4+
import { ExternalLayer } from "@/external/core/domain/model/ExternalLayer";
15
import { $CONVERT_MOVIE_CLIP_INPUT_ID } from "@/config/ConvertMovieClipConfig";
26
import { $getCurrentWorkSpace } from "@/core/application/CoreUtil";
37
import { execute as convertMovieClipModalHideUseCase } from "./ConvertMovieClipModalHideUseCase";
48
import { execute as externalLibraryAddNewMovieClipUseCase } from "@/external/controller/application/ExternalLibrary/usecase/ExternalLibraryAddNewMovieClipUseCase";
9+
import { execute as convertMovieClipModalCalcPositionService } from "../service/ConvertMovieClipModalCalcPositionService";
10+
import { execute as screenAreaCalcSelectedBoundsService } from "@/screen/application/ScreenArea/service/ScreenAreaCalcSelectedBoundsService";
511

612
/**
713
* @description 指定の名前のMovieClipを作成して、選択中のDisplayObjectを配置
@@ -17,9 +23,7 @@ export const execute = async (event: PointerEvent): Promise<void> =>
1723
const movieClip = workspace.scene;
1824

1925
// 選択中のDisplayObjectがない場合は処理を終了する
20-
if (!movieClip.selectedDepths.size
21-
|| movieClip.selectedDepths.size > 1
22-
) {
26+
if (movieClip.selectedDepths.size !== 1) {
2327
return;
2428
}
2529

@@ -29,23 +33,115 @@ export const execute = async (event: PointerEvent): Promise<void> =>
2933
return;
3034
}
3135

36+
const layer = movieClip.getLayer(
37+
movieClip.selectedDepths.keys().next().value as number
38+
);
39+
if (!layer) {
40+
return ;
41+
}
42+
43+
const bounds = screenAreaCalcSelectedBoundsService(movieClip);
44+
if (!bounds) {
45+
return ;
46+
}
47+
3248
// イベントの伝播を停止する
3349
event.stopPropagation();
3450

35-
// 指定の名前でMovieClipを作成
36-
const name = inputElement.value;
37-
3851
// ライブラリにMovieClipを追加
3952
const newMovieClip = await externalLibraryAddNewMovieClipUseCase(
4053
workspace,
4154
movieClip,
42-
name
55+
inputElement.value
4356
);
4457

4558
// MovieClipに選択中のDisplayObjectを配置
59+
const depths = movieClip.selectedDepths.values().next().value as number[];
60+
61+
// 昇順にソート
62+
depths.sort((a, b) => a - b);
63+
64+
// 移動先のMovieClipのレイヤーを取得
65+
const targetExternalLayer = new ExternalLayer(
66+
workspace,
67+
newMovieClip,
68+
newMovieClip.layers[0] as NonNullable<Layer>
69+
);
70+
71+
// キーフレームの開始位置と終了位置
72+
let startFrame = 1;
73+
let endFrame = 2;
74+
75+
// 削除した際に深度が変わるので、先に配列にキャッシュする
76+
const characters = [];
77+
const frame = movieClip.currentFrame;
78+
for (let idx = 0; idx < depths.length; idx += 1) {
79+
const character = layer.getCharacter(frame, depths[idx]);
80+
if (!character) {
81+
continue;
82+
}
83+
84+
if (idx === 0) {
85+
startFrame = character.startFrame;
86+
endFrame = character.endFrame;
87+
}
88+
89+
characters.push(character);
90+
}
4691

47-
// MovieClipをレイヤーに配置
48-
console.log(newMovieClip);
92+
// 選択しているDisplayObjectを現在のMovieClipから削除して、新しいMovieClipに移動
93+
for (let idx = 0; idx < characters.length; idx += 1) {
94+
95+
const character = characters[idx];
96+
if (!character) {
97+
continue;
98+
}
99+
100+
// 選択中のDisplayObjectを削除
101+
const externalCharacter = new ExternalCharacter(
102+
workspace,
103+
movieClip,
104+
layer,
105+
character
106+
);
107+
await externalCharacter.remove();
108+
109+
// 1フレームだけのDisplayObjectに変更
110+
externalCharacter.startFrame = 1;
111+
externalCharacter.endFrame = 2;
112+
113+
// 新しいMovieClipのレイヤーにDisplayObjectを追加
114+
const newExternalCharacter = await targetExternalLayer.addCharacter(
115+
externalCharacter,
116+
idx
117+
);
118+
119+
// 中心点に合わせて座標を移動
120+
const position = convertMovieClipModalCalcPositionService(character, bounds);
121+
await newExternalCharacter.setX(position.x);
122+
await newExternalCharacter.setY(position.y);
123+
}
124+
125+
// 新しいMovieClipのDisplayObjectを作成して配置
126+
const newCharacter = new Character();
127+
newCharacter.libraryId = newMovieClip.id;
128+
newCharacter.startFrame = startFrame;
129+
newCharacter.endFrame = endFrame;
130+
131+
// todo: xy position
132+
133+
const currentExternalLayer = new ExternalLayer(
134+
workspace,
135+
movieClip,
136+
layer
137+
);
138+
139+
await currentExternalLayer.addCharacter(new ExternalCharacter(
140+
workspace,
141+
movieClip,
142+
layer,
143+
newCharacter
144+
));
49145

50146
// モーダルを非表示にする
51147
convertMovieClipModalHideUseCase();

src/js/menu/application/ConvertMovieClipModal/usecase/ConvertMovieClipModalChildPointerDownEventUseCase.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@ import { execute as convertMovieClipModalUpdateButtonService } from "../service/
1313
*/
1414
export const execute = (event: PointerEvent): void =>
1515
{
16-
// 親のイベントを抑制する
17-
event.stopPropagation();
18-
1916
const element = event.currentTarget as HTMLElement;
2017
if (!element) {
2118
return;
2219
}
2320

21+
// 親のイベントを抑制する
22+
event.stopPropagation();
23+
2424
// 全ての子要素のactiveクラスを削除する
2525
convertMovieClipModalChildInactiveService();
2626

2727
// 選択された要素にactiveクラスを追加する
2828
element.classList.add("active");
2929

3030
// 参照選択状態をセットする
31-
$selectReference();
31+
$selectReference(element.dataset.position as string);
3232

3333
// 変換ボタンの状態を更新
3434
convertMovieClipModalUpdateButtonService();

src/js/menu/application/ScreenMenu/usecase/ScreenMenuConvertToMovieClipUseCase.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ export const execute = (event: PointerEvent | KeyboardEvent): void =>
1919
const workSpace = $getCurrentWorkSpace();
2020
const movieClip = workSpace.scene;
2121

22-
if (!movieClip.selectedDepths.size) {
22+
// レイヤー選択は1つのみ許可
23+
if (movieClip.selectedDepths.size !== 1) {
2324
return;
2425
}
2526

0 commit comments

Comments
 (0)