Skip to content

Commit

Permalink
feat: [FE] 구간 편집기능(잘라내기, 붙여넣기, 구간 자르기) 구현 (#101)
Browse files Browse the repository at this point in the history
* refactor: [FE] auto increment 초기값 1로 수정

- clipBoard에 저장되는 TrackSection과 구분하기 위하여 auto increment의 초기값을 1로 수정

* refactor: [FE] DeleteCommand CopyUtil 적용

- CopyUtil로 deleteList에 저장하도록 수정

* refactor: [FE] CopyUtil 파라미터 수정

- 파라미터로 id 정보를 받도록 수정했습니다.
- 기존에는 clipBoard에 저장하는 용도로 만들었지만 DelelteCommand에서는
  id 정보가 필요해서 파라미터로 추가했습니다.

* feat: [FE] controller cutCommand() 구현

- 기존에 구현한 copy와 delete 기능을 합쳐서 cutCommand 함수를
  구현했습니다.
- 잘라내기는 섹션을 1개 선택했을 때만 가능하기 때문에 setClipBoard()의
  return값을 boolean으로 수정하여 유효성 체크를 했습니다.

* feat: [FE] EditTools 잘라내기 이벤트 등록

- 선택한 구간 잘라내기 기능을 위한 이벤트를 등록했습니다.

* feat: [FE] 잘라내기 단축키 구현

- 1개의 구간을 선택하고 ctrl + X 를 누르면 cutCommand()가 실행되도록
  구현했습니다.

* feat: [FE] icons에 iconInfo 추가

- IconButton에 대한 hover 기능을 위해 아이콘 정보를 알려주는 iconInfo를
  구현했습니다.
- iconInfo는 iconType에 대한 상세정보를 알려줍니다.

* style: [FE] hover시 icon 상세정보가 나오도록 style 적용

- visibility 속성을 이용하여 hover시에 아이콘에 대한 정보가 나오도록
  style 적용했습니다.
- transition-delay로 0.3초 후에 정보가 나오도록 했습니다.

* feat: [FE] 시작위치, 종료위치 이동 단축키 추가

- 시작위치로 이동은 '[', 종료위치로 이동은 ']' 키로 동작하도록 keyType과
  keyDown 이벤트를 추가했습니다.

* refactor: [FE] publish 빈 인자값 명시적으로 표현

- 빈 인자를 넘기는 경우 null로 표현

* refacot: [FE] Command 클래스 index에서 export하도록 변경

- import하기 쉽게 index.js에서 export 관리

* feat: [FE] CopyUtil copyTrack 함수 구현

- Track을 deep copy하기 위한 함수 구현

* refactor: [FE] store setTrack, setTrackSection 수정

- setTrack은 파라미터의 따라 다르게 동작하도록 변경
  - newTrack의 id값이 이미 TrackList에 존재하는 경우에는 값만 변경
  - 존재하지 않는 경우에는 TrackList에 새로 추가
- setTrackSection
  - clipBoard에 복사된 trackSection은 0의 값을 가지므로 새로 id값을 변경
  - undo에서 그전에 저장된 상태 그대로 변경해야 되기 때문에 trackSection id값을 그대로
    사용

* feat: [FE] controller pasteCommand 함수 구현

- 붙여넣기는 현재 clipBoard에 복사된 구간의 정보를 사용합니다.
- 현재는 1개의 구간을 선택한 경우에만 붙여넣기를 할 수 있습니다.
  - 추후 마커위치 관련해서 붙여넣기 기능 리팩토링 필요
- pasteCommand()에 대한 설명
  - 1) focusList의 길이가 1인지 확인합니다.
  - 2) 붙여넣기의 대상이 되는 track과 clipBoard를 deep copy합니다.
  - 3) copySection의 trackStartTime을 현재 focus한
    trackSection의 endTime으로 변경합니다.
  - 4) PasteCommand의 인자로 넘겨줍니다.

* feat: [FE] PasteCommand 클래스 구현

- beforeTrack은 execute가 실행되기 전 저장하는 상태값입니다.
- execute()
  - 1) track과 section의 정보를 deep copy합니다.
  - 2) track에서 newSection이 종료되는 시점인 endTime을 구합니다.
  - 3) newSection의 종료시간 전에 시작하는 section이 있는지 findIndex를
    통해 index를 구합니다.
  - 4-1) firstDelayIndex가 -1이 아니라면 (존재)
    - endTime에서 해당 section의 시작시간을 빼서 얼마나 delayTime을
      구합니다.
    - firstDelayIndex와 그 뒤에 존재하는 section들의 trackStartTime에
      delayTime을 더해줍니다.
  - 4-2) firstDelayIndex가 -1인 경우 (존재x)
    - 바로 트랙에 newSection을 추가합니다.
- undo()
  - 저장된 beforeTrack을 deep copy하여 setTrack으로 덮어씌우고
    publish합니다.

* feat: [FE] EditTools 붙여넣기 이벤트 등록

- 붙여넣기를 위한 click 이벤트 등록

* feat: [FE] 붙여넣기 단축키 기능 구현

- 클립보드에 복사된 상태에서 ctrl + v 를 누르면 붙여넣기 기능이
  실행되도록 구현

* style: [FE] PlayBarUtil 주석 추가

- 나중에 리팩토링을 위한 주석추가
- PR merge하면서 지우겠습니다

* feat: [FE] SplitCommand 구현

- 구간 자르기 기능을 위한 SplitCommand 구현
- 클릭한 곳의 시간과 클릭한 trackSection의 trackStartTime으로 splitTime을 구했습니다.
- splitTime을 기준으로 왼쪽 오른쪽 섹션을 생성하여 기존의 trackSection을 지우고 새로 track에 추가했습니다.
- 기준이 되는 element는 audi-main-audio-track-container div로
  설정했습니다.

* feat: [FE] AudioTrackSection 컴포넌트 click 이벤트 분기처리

- cursorMode에 따라 focus되거나 split 하도록 조건문을 추가했습니다

* feat: [FE] controller splitCommand() 구현

- SplitCommand를 실행하는 함수를 구현했습니다.
- getTrack은 자주 쓰일 것 같아서 구현했습니다.

* feat: [FE] AudioTrack 자르기 모드 커트라인 구현

- Track 별로 cut-line 관리하도록 구현했습니다.
- 평소에는 class="hide"로 되어있다가 cursorMode가 cut일 때,
  trackSection의 mousemove 이벤트로 hide가 해체됩니다.

* feat: [FE] EventType mouseout 추가

- 마우스가 canvas 밖으로 나갔을 때 cut-line이 다시 hide 되도록 mouseout
  이벤트를 추가했습니다.

* feat: [FE] AudioTrackSection mousemove, mouseout 이벤트 구현

- TrackSection위에서  cursorMode가 자르기 모드일 경우 마우스 커서 위치에
  따라 cut-line이 나오도록 구현했습니다.
- mouseout은 커서가 TrackSection 밖 나갔을 경우 다시 hide하기 위한
  기능입니다.

* style: [FE] image cursor 위치 조정

- 커트라인과 마우스 커서의 위치의 싱크가 잘 맞지 않아서 수정했습니다.

close #65, close #68, close #73, close #99, close #100
  • Loading branch information
Songwonseok committed Dec 7, 2020
1 parent 15e39fd commit 086caab
Show file tree
Hide file tree
Showing 21 changed files with 452 additions and 118 deletions.
6 changes: 3 additions & 3 deletions frontend/src/common/command/CommandManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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);
}
}
}
Expand All @@ -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);
}
}
}
Expand Down
15 changes: 3 additions & 12 deletions frontend/src/common/command/DeleteCommand.ts
Original file line number Diff line number Diff line change
@@ -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() {
Expand All @@ -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);
});
}

Expand Down
55 changes: 55 additions & 0 deletions frontend/src/common/command/PasteCommand.ts
Original file line number Diff line number Diff line change
@@ -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);
};

}

64 changes: 64 additions & 0 deletions frontend/src/common/command/SplitCommand.ts
Original file line number Diff line number Diff line change
@@ -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);
};
}


3 changes: 3 additions & 0 deletions frontend/src/common/command/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { DeleteCommand } from './DeleteCommand';
export { PasteCommand } from './PasteCommand';
export { SplitCommand } from './SplitCommand';
9 changes: 5 additions & 4 deletions frontend/src/common/types/eventTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/common/types/keyType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ enum KeyBoard {
CTRL = 17,
LEFT = 37,
RIGHT = 39,
SPACE = 32
SPACE = 32,
LEFT_BRACKET = 219,
RIGHT_BRACKET = 221,
}

export { KeyBoard }
17 changes: 14 additions & 3 deletions frontend/src/common/util/CopyUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
const copyTrack = (track: Track): Track => {
const newTrack = new Track({
id: track.id,
trackSectionList: track.trackSectionList.map(section => copySection(section))
});

return newTrack;
}

export { copySection, copyTrack }
2 changes: 1 addition & 1 deletion frontend/src/common/util/PlayBarUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] => {
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/components/App/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import { Controller } from "@controllers";
}

KeyDownListener(e): void {
const { } = Controller
const isCtrl = Controller.getCtrlIsPressed();

if (e.which === KeyBoard.CTRL) {
Expand All @@ -60,17 +59,23 @@ 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('스페이스바');
}
else if (e.which === KeyBoard.C && isCtrl) {
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();
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/components/AudioTrack/AudioTrack.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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{
Expand Down
Loading

0 comments on commit 086caab

Please sign in to comment.