diff --git a/frontend/src/common/command/CommandManager.ts b/frontend/src/common/command/CommandManager.ts index 29a0282b..09b6713d 100644 --- a/frontend/src/common/command/CommandManager.ts +++ b/frontend/src/common/command/CommandManager.ts @@ -15,7 +15,7 @@ class CommandManager { command.execute(); this.undoList.push(command); this.redoList = []; - storeChannel.publish(StoreChannelType.EDIT_TOOLS_CHANNEL, ''); + storeChannel.publish(StoreChannelType.EDIT_TOOLS_CHANNEL, null); } undo() { @@ -24,7 +24,7 @@ class CommandManager { if (command) { this.redoList.push(command); command.undo(); - storeChannel.publish(StoreChannelType.EDIT_TOOLS_CHANNEL, ''); + storeChannel.publish(StoreChannelType.EDIT_TOOLS_CHANNEL, null); } } } @@ -35,7 +35,7 @@ class CommandManager { if (command) { this.undoList.push(command); command.execute(); - storeChannel.publish(StoreChannelType.EDIT_TOOLS_CHANNEL, ''); + storeChannel.publish(StoreChannelType.EDIT_TOOLS_CHANNEL, null); } } } diff --git a/frontend/src/common/command/DeleteCommand.ts b/frontend/src/common/command/DeleteCommand.ts index c52ebbfc..bcd3bfd2 100644 --- a/frontend/src/common/command/DeleteCommand.ts +++ b/frontend/src/common/command/DeleteCommand.ts @@ -1,8 +1,9 @@ import ICommand from './ICommand' import { Controller } from '@controllers' import { TrackSection } from '@model' +import { CopyUtil } from '@util' -export default class DeleteCommand extends ICommand { +export class DeleteCommand extends ICommand { private deleteList: TrackSection[]; constructor() { @@ -17,17 +18,7 @@ export default class DeleteCommand extends ICommand { if (focusList.length === 0) return; this.deleteList = focusList.map(focus => { const trackSection = focus.trackSection; - return new TrackSection({ - id: trackSection.id, - sourceId: trackSection.sourceId, - trackId: trackSection.trackId, - channelStartTime: trackSection.channelStartTime, - channelEndTime: trackSection.channelEndTime, - parsedChannelStartTime: trackSection.parsedChannelStartTime, - parsedChannelEndTime: trackSection.parsedChannelEndTime, - trackStartTime: trackSection.trackStartTime, - audioStartTime: trackSection.audioStartTime - }) + return CopyUtil.copySection(trackSection); }); } diff --git a/frontend/src/common/command/PasteCommand.ts b/frontend/src/common/command/PasteCommand.ts new file mode 100644 index 00000000..8f6cec2c --- /dev/null +++ b/frontend/src/common/command/PasteCommand.ts @@ -0,0 +1,55 @@ +import ICommand from './ICommand' +import { Controller } from '@controllers' +import { StoreChannelType } from '@types' +import { storeChannel } from '@store' +import { TrackSection, Track } from '@model' +import { CopyUtil } from '@util' + +export class PasteCommand extends ICommand { + private beforeTrack: Track; + private addTrackSection: TrackSection; + + constructor(track: Track, trackSection: TrackSection) { + super(); + this.beforeTrack = track; + this.addTrackSection = trackSection; + } + + execute() { + const newTrack: Track = CopyUtil.copyTrack(this.beforeTrack); + const newSection: TrackSection = CopyUtil.copySection(this.addTrackSection); + + const endTime: number = newSection.trackStartTime + newSection.length; + const firstDelayIndex: number = newTrack.trackSectionList.findIndex(section => section.trackStartTime >= newSection.trackStartTime && section.trackStartTime < endTime); + + if (firstDelayIndex !== -1) { + const deleyTime = endTime - newTrack.trackSectionList[firstDelayIndex].audioStartTime; + + newTrack.trackSectionList = newTrack.trackSectionList.map((cur, idx) => { + if (idx >= firstDelayIndex) { + cur.trackStartTime += deleyTime; + } + return cur; + }) + Controller.setTrack(newTrack); + Controller.addTrackSection(newTrack.id, newSection); + } else { + Controller.addTrackSection(this.beforeTrack.id, newSection); + } + + }; + + undo() { + const newTrack = CopyUtil.copyTrack(this.beforeTrack); + Controller.setTrack(newTrack); + + storeChannel.publish(StoreChannelType.TRACK_SECTION_LIST_CHANNEL, { + trackId: newTrack.id, + trackSectionList: newTrack.trackSectionList + }); + + storeChannel.publish(StoreChannelType.TRACK_CHANNEL, newTrack.trackSectionList); + }; + +} + diff --git a/frontend/src/common/command/SplitCommand.ts b/frontend/src/common/command/SplitCommand.ts new file mode 100644 index 00000000..3478a143 --- /dev/null +++ b/frontend/src/common/command/SplitCommand.ts @@ -0,0 +1,64 @@ +import ICommand from './ICommand' +import { Controller } from '@controllers' +import { StoreChannelType } from '@types' +import { storeChannel } from '@store' +import { Track, TrackSection } from '@model' +import { CopyUtil, PlayBarUtil } from '@util' + +export class SplitCommand extends ICommand { + private beforeTrack: Track; + private cursorPosition: number; + private trackContainerElement: HTMLElement | null; + private targetSection: TrackSection; + + constructor(cursorPosition: number, currentTrack: Track, targetSection: TrackSection) { + super(); + this.cursorPosition = cursorPosition; + this.beforeTrack = currentTrack; + this.targetSection = targetSection; + this.trackContainerElement = document.querySelector('.audi-main-audio-track-container'); + } + + execute() { + if (!this.trackContainerElement) return; + const startX = this.trackContainerElement.getBoundingClientRect().left; + const endX = this.trackContainerElement.getBoundingClientRect().right; + const [minute, second, milsecond, location, totalCursorTime] = PlayBarUtil.getCursorPosition(startX, this.cursorPosition, endX - startX); + + const splitTime = totalCursorTime - this.targetSection.trackStartTime; + const leftSection = CopyUtil.copySection(this.targetSection); + leftSection.id = 0; + leftSection.channelEndTime = splitTime; + leftSection.parsedChannelEndTime = splitTime; + leftSection.length = splitTime; + + const rightSection = CopyUtil.copySection(this.targetSection); + rightSection.id = 0; + rightSection.channelStartTime = splitTime; + rightSection.parsedChannelStartTime = splitTime; + rightSection.trackStartTime += splitTime; + rightSection.length -= splitTime; + + const sectionIndex = this.beforeTrack.trackSectionList.findIndex(section => section.id === this.targetSection.id); + + if (sectionIndex === -1) return; + const trackId = this.beforeTrack.id; + Controller.removeSection(trackId, sectionIndex); + Controller.addTrackSection(trackId, leftSection); + Controller.addTrackSection(trackId, rightSection); + }; + + undo() { + const newTrack = CopyUtil.copyTrack(this.beforeTrack); + Controller.setTrack(newTrack); + + storeChannel.publish(StoreChannelType.TRACK_SECTION_LIST_CHANNEL, { + trackId: newTrack.id, + trackSectionList: newTrack.trackSectionList + }); + + storeChannel.publish(StoreChannelType.TRACK_CHANNEL, newTrack.trackSectionList); + }; +} + + diff --git a/frontend/src/common/command/index.js b/frontend/src/common/command/index.js new file mode 100644 index 00000000..f2166cba --- /dev/null +++ b/frontend/src/common/command/index.js @@ -0,0 +1,3 @@ +export { DeleteCommand } from './DeleteCommand'; +export { PasteCommand } from './PasteCommand'; +export { SplitCommand } from './SplitCommand'; \ No newline at end of file diff --git a/frontend/src/common/types/eventTypes.ts b/frontend/src/common/types/eventTypes.ts index 2ee34e0b..957a9eda 100644 --- a/frontend/src/common/types/eventTypes.ts +++ b/frontend/src/common/types/eventTypes.ts @@ -22,9 +22,9 @@ enum EventKeyType { AUDIO_SKIP_NEXT = 'AUDIO_SKIP_NEXT', AUDIO_TRACK_CONTAINER_MULTIPLE = 'AUDIO_TRACK_CONTAINER_MULTIPLE', SOURCE_LIST_MULTIPLE = 'SOURCE_LIST_MULTIPLE', - AUDIO_TRACK_SECTION_CLICK = 'AUDIO_TRACK_SECTION_CLICK', + AUDIO_TRACK_SECTION_MULTIPLE = 'AUDIO_TRACK_SECTION_MULTIPLE', FOCUS_RESET_CLICK = 'FOCUS_RESET_CLICK', - EDIT_TOOLS_CLICK = 'EDIT_TOOLS_CLICK' + EDIT_TOOLS_CLICK = 'EDIT_TOOLS_CLICK', } enum EventType { @@ -39,10 +39,11 @@ enum EventType { drop = 'drop', change = 'change', input = 'input', - mousemove = 'mousemove' + mousemove = 'mousemove', + mouseout = 'mouseout' } -const eventTypes = ['click', 'dblclick', 'keyup', 'dragstart', 'dragover', 'dragenter', 'dragleave', 'drop', 'change', 'input', 'mousemove', 'dragend']; +const eventTypes = ['click', 'dblclick', 'keyup', 'dragstart', 'dragover', 'dragenter', 'dragleave', 'drop', 'change', 'input', 'mousemove', 'dragend', 'mouseout']; interface EventTargetDataType { listener: EventListener; diff --git a/frontend/src/common/types/keyType.ts b/frontend/src/common/types/keyType.ts index 33f728e0..6d07e33d 100644 --- a/frontend/src/common/types/keyType.ts +++ b/frontend/src/common/types/keyType.ts @@ -8,7 +8,9 @@ enum KeyBoard { CTRL = 17, LEFT = 37, RIGHT = 39, - SPACE = 32 + SPACE = 32, + LEFT_BRACKET = 219, + RIGHT_BRACKET = 221, } export { KeyBoard } \ No newline at end of file diff --git a/frontend/src/common/util/CopyUtil.ts b/frontend/src/common/util/CopyUtil.ts index 960f066f..552ee179 100644 --- a/frontend/src/common/util/CopyUtil.ts +++ b/frontend/src/common/util/CopyUtil.ts @@ -2,17 +2,28 @@ import { Source, Track, TrackSection } from '@model'; const copySection = (trackSection: TrackSection): TrackSection => { const newTrackSection = new TrackSection({ + id: trackSection.id, sourceId: trackSection.sourceId, trackId: trackSection.trackId, channelStartTime: trackSection.channelStartTime, channelEndTime: trackSection.channelEndTime, parsedChannelStartTime: trackSection.parsedChannelStartTime, parsedChannelEndTime: trackSection.parsedChannelEndTime, - trackStartTime: 0, - audioStartTime: 0 + trackStartTime: trackSection.trackStartTime, + // effectList: trackSection.effectList, // Effect 기능 구현시 추가 + audioStartTime: trackSection.audioStartTime }); return newTrackSection; } -export { copySection } \ No newline at end of file +const copyTrack = (track: Track): Track => { + const newTrack = new Track({ + id: track.id, + trackSectionList: track.trackSectionList.map(section => copySection(section)) + }); + + return newTrack; +} + +export { copySection, copyTrack } \ No newline at end of file diff --git a/frontend/src/common/util/PlayBarUtil.ts b/frontend/src/common/util/PlayBarUtil.ts index 6ca297bb..a0b12c38 100644 --- a/frontend/src/common/util/PlayBarUtil.ts +++ b/frontend/src/common/util/PlayBarUtil.ts @@ -39,7 +39,7 @@ const getCursorPosition = (defaultStartX: number, currentX: number, mainWidth: n const [newMinute, newSecond, newMilsecond] = setTime(cursorTime); - return [newMinute, newSecond, newMilsecond, differenceWidth, cursorTime]; + return [newMinute, newSecond, newMilsecond, differenceWidth, cursorTime]; // {} 으로 retrun해도 좋을 것 같아요 }; const getStringTime = (time: number): string[] => { diff --git a/frontend/src/components/App/App.ts b/frontend/src/components/App/App.ts index b4a9ff8d..909c5fac 100644 --- a/frontend/src/components/App/App.ts +++ b/frontend/src/components/App/App.ts @@ -38,7 +38,6 @@ import { Controller } from "@controllers"; } KeyDownListener(e): void { - const { } = Controller const isCtrl = Controller.getCtrlIsPressed(); if (e.which === KeyBoard.CTRL) { @@ -60,6 +59,12 @@ import { Controller } from "@controllers"; else if (e.which === KeyBoard.RIGHT && !isCtrl) { // console.log('오른쪽'); } + else if (e.which === KeyBoard.LEFT_BRACKET && !isCtrl) { + // console.log('시작지점'); + } + else if (e.which === KeyBoard.RIGHT_BRACKET && !isCtrl) { + // console.log('마지막지점'); + } else if (e.which === KeyBoard.SPACE && !isCtrl) { // console.log('스페이스바'); } @@ -67,10 +72,10 @@ import { Controller } from "@controllers"; Controller.setClipBoard(); } else if (e.which === KeyBoard.X && isCtrl) { - // console.log('잘라내기'); + Controller.cutCommand(); } else if (e.which === KeyBoard.V && isCtrl) { - // console.log('붙여넣기'); + Controller.pasteCommand(); } else if (e.which === KeyBoard.Z && isCtrl) { Controller.undoCommand(); diff --git a/frontend/src/components/AudioTrack/AudioTrack.scss b/frontend/src/components/AudioTrack/AudioTrack.scss index 6a195f97..cadb4027 100644 --- a/frontend/src/components/AudioTrack/AudioTrack.scss +++ b/frontend/src/components/AudioTrack/AudioTrack.scss @@ -8,7 +8,7 @@ audi-audio-track:not(:defined) { position: relative; width: 100%; min-height: 150px; - padding: 10px; + padding: 10px 0px; background-color: #000000; box-sizing: border-box; @@ -38,6 +38,18 @@ audi-audio-track:not(:defined) { box-shadow: rgb(43, 130, 211) 0 0 15px 5px; } } + .cut-line { + position: absolute; + width: 1px; + height: 100%; + + top:0; + left: 0px; + border-left: 1px dashed white; + z-index: 999; + + pointer-events: none; + } } &:last-of-type{ diff --git a/frontend/src/components/AudioTrack/AudioTrack.ts b/frontend/src/components/AudioTrack/AudioTrack.ts index 72f54822..37be5ee8 100644 --- a/frontend/src/components/AudioTrack/AudioTrack.ts +++ b/frontend/src/components/AudioTrack/AudioTrack.ts @@ -53,11 +53,12 @@ import "./AudioTrack.scss"; ${this.getTrackSectionList()}
+ `; } - + getTrackSectionList(): string { return this.trackSectionList.reduce((acc, trackSection, idx) => acc += `