1) 뒤로 가기(작업 취소), 앞으로 가기(복구)
뒤로 가기, 앞으로 가기 기능을 구현하기 위해서 back, front 2개의 스택을 만들었다. back 스택은 작업 실행 전 상태들을 담아놓고, front 스택은 앞으로 가기를 실행했을 때 현재 상태, 즉 복구되기전 상태들을 담아 놓았다.
각 스택에는 편집기의 배경, 이미지, 텍스트 정보가 들어간다. (nodes, background)
매 작업(ex: 배경색 변경, 노드 속성 변경 등)을 실행할 때마다 현재 상태와 이전 상태를 저장했다. (saveHistory, saveNow 메소드 실행) 즉 작업전 상태가 back 스택에 쌓이고 현재 상태는 nowState라는 변수에 담았다. 또, front 스택을 비워서 뒤로 가기 후 다른 작업을 실행할 시 앞으로 가기가 불가능하도록 했다.
뒤로 가기 버튼을 누르면 nodes와 background를 backStack에서 pop한 애로 갱신했다. (직전 상태) 그리고 현재 상태를 그 pop한 애로 바꿨다.
앞으로 가기 버튼을 클릭 시 frontStack에서 pop해와서 nowState , nodes, background를 변경했다.
각 버튼은 front, back 스택의 길이가 0인 경우 disabled 되게 해서 사용자가 혼란을 겪지 않게 했다. 각 스택의 길이는 50으로 고정해서 너무 많은 용량이 낭비되지 않게 했다.
function historyManagerBuilder() {
let backStack = []
let frontStack = []
let nowState;
const SIZE_LIMIT = 50
function pushWithSizeLimit(item) {
// console.log(this)
this.push(item)
if(this.length > SIZE_LIMIT) {
this.splice(0,1)
}
}
// 오류처리해야됌
// 뒤로갈거 없을때, 앞으로 갈거 없을때 버튼 비활성화
backStack.pushWithSizeLimit = pushWithSizeLimit.bind(backStack)
// console.log(backStack.pushWithSizeLimit)
frontStack.pushWithSizeLimit = pushWithSizeLimit.bind(frontStack)
return {
saveHistory : (beforeState) => {
backStack.pushWithSizeLimit(beforeState)
},
saveNow : (_nowState) => {
nowState = _nowState
frontStack.splice(0, frontStack.length)
// console.log(frontStack)
},
goBack : () => {
if(backStack.length === 0) return []
const lastState = backStack.pop()
frontStack.pushWithSizeLimit(nowState)
nowState = lastState
// console.log("backStack: ", backStack, "frontStack:", frontStack)
return lastState
},
goFront : () => {
if(frontStack.length === 0) return []
const frontLastState = frontStack.pop()
backStack.pushWithSizeLimit(nowState)
nowState = frontLastState
return frontLastState
},
isEmptyBackStack : () => {
// console.log(backStack.length)
return backStack.length === 0
},
isEmptyFrontStack : () => {
// console.log(frontStack.length)
return frontStack.length === 0
},
flush: () => {
while(frontStack.length > 0) {
frontStack.pop()
}
while(backStack.length > 0) {
backStack.pop()
}
}
}
}
const historyManager = new historyManagerBuilder
export default historyManager
2) 노드 Z-index 변경
Konva의 경우 jsx에서 컴포넌트 순서대로 그림을 그린다.
즉, nodes.forEach(node => <Node {...node}/> 이 코드는 아래 그림처럼 그려지게 된다.
여기서 0 노드를 1노드의 앞에 보이게 하고 싶다면 배열에서 두 원소를 서로 교환하면 된다.
이런 식으로 노드의 Z-index 변경 (맨 앞으로 보내기, 맨 뒤로 보내기, 앞으로 보내기, 뒤로 보내기)를 구현했다.
export function arrayOfObjectReducer(state, action) {
let newState = state.slice()
let target = null
let targetIdx
let maxId
if(action.id) {
target = newState.find(each => each.id === action.id)
}
// console.log(action)
switch (action.type) {
// ...생략
case 'MOVE__FRONT':
newState = [...newState.filter(each => each.id !== action.id), target]
break
case 'MOVE__REAR':
newState = [target, ...newState.filter(each => each.id !== action.id)]
break
case 'MOVE__FORWARD':
// 인덱스 한칸 뒤로 +1
targetIdx = newState.findIndex(each => each.id === action.id)
// 이미 맨뒤에있으면 안함
if(targetIdx === newState.length-1) break
// 서로 바꾸기
change(newState, targetIdx, targetIdx+1)
break
case 'MOVE__BACKWARD':
// 인덱스 한칸 앞으로 -1
targetIdx = newState.findIndex(each => each.id === action.id)
if(targetIdx === 0) break
change(newState, targetIdx, targetIdx-1)
break
default:
break
}
return newState
}
'프로젝트 회고 > 이미지편집기 개발' 카테고리의 다른 글
7. 정리 - 끝 (0) | 2022.07.20 |
---|---|
5. 개발 - 상태 관리 리팩토링 - useReducer (0) | 2022.07.18 |
4. 개발 - 편집기 주 화면 구현, 배경, 노드(이미지, 텍스트) 추가, 편집 기능 (0) | 2022.06.30 |
3. 개발 - 기본 구조 (0) | 2022.06.19 |
2. 액션 정의하기, 앱 구조와 화면 레이아웃 잡기 (0) | 2022.06.15 |