http://jsbin.com/geqige/edit?js,console,output
선 소스.
( 조금 더 자세한 구현 : http://jsbin.com/ziraga/edit?html,js,output)
먼저 Drag and Drop이라는게 어떤 절차인지 생각해보면
100,100 위치에 있는 A라는 대상을 200,200까지 Drag and Drop을 하는 과정을 표로 옮기면
과 같은 과정을 수행할 것이다.
눈치챘을지도 모르겠지만 mousemove 구간이 바로 Stream이다!
Rx 기준으로 그러면 다시 Drag and Drop을 재정의하면
그러면 재료를 모아보자.
mousedown
mouseup
mousemove
이 세 개를 Observable 로 만들자.
선 소스.
( 조금 더 자세한 구현 : http://jsbin.com/ziraga/edit?html,js,output)
- 이동하고자 하는 대상에 MouseDown
- 이동하고자 하는 지점까지 MouseMove
- 이동하고자 하는 지점에서 MouseUp
100,100 위치에 있는 A라는 대상을 200,200까지 Drag and Drop을 하는 과정을 표로 옮기면
시간(seq) | 행동 | 좌표 |
---|---|---|
1 | mousedown | (150,150) |
2 | mousemove | (150,150) |
3 | mousemove | (160,160) |
4 | mousemove | (170,170) |
5 | mousemove | (..., ...) |
(n-1)th | mousemove | (250, 250) |
(n)th | mouseup | (250, 250) |
과 같은 과정을 수행할 것이다.
눈치챘을지도 모르겠지만 mousemove 구간이 바로 Stream이다!
Rx 기준으로 그러면 다시 Drag and Drop을 재정의하면
mousedown 이후 시점으로부터 mouseup이 되기전까지 mousemove 를 Observe 하는 것인것이다.
그러면 재료를 모아보자.
mousedown
mouseup
mousemove
이 세 개를 Observable 로 만들자.
const down$ = Rx.Observable.fromEvent(box, 'mousedown');
const move$ = Rx.Observable.fromEvent(document, 'mousemove');
const up$ = Rx.Observable.fromEvent(document, 'mouseup');
별거 없다. 이게 전부.
move$랑 up$은 왜 box가 아닌가 싶은데 실제로 순간 움직임이 box의 범위를 넘어갈 수도 있기 때문에 document 전체와 바인딩하는 편이 좋다.
move$랑 up$은 왜 box가 아닌가 싶은데 실제로 순간 움직임이 box의 범위를 넘어갈 수도 있기 때문에 document 전체와 바인딩하는 편이 좋다.
실제로 해야하는 것은 move$를 subscribe 하는 것이다.
subscribe 해서 뭘 해야하나? 당연히 대상의 좌표를 수정해야한다.
move$.subscribe(e=>{
box.style.left = e.clientX + 'px';
box.style.top = e.clientY + 'px';
});
물론 이렇게만 하면 마우스의 위치가 대상의 왼쪽 끝을 가르키기 때문에 오른쪽 아래로 밖에 움직일 수 없다.
약간의 보정이 필요한데 최초 mousedown시점에 x,y와 대상의 x,y와 거리만큼 대상은 좌상으로 이동하면 된다.
이는 down$ 시점에 한번만 계산하면 되니
const down$ = Rx.Observable.fromEvent(box, 'mousedown')
.map(({clientX, clientY})=>({
deltaX: clientX - box.offsetLeft,
deltaY: clientY - box.offsetTop
}));
으로 map을 한번 해주자. 그러면 down$ 을 move$ 에 적용할 차례인데
그전에 앞서 mousemove stream은 언제부터 해야할까?
당연히 down$ 이후 시점 부터 Observe 해야할 것이다.
그럼, 지금 필요한 것은
- move$ 를 down$ 이후 것만 filter 하기
- move$ subscriber 에서 down$ 의 값도 사용하기
인데, 이 두 가지를 한방에 해결해주는 Operator 가 있다.
오오 combineLatest 오오
위의 그림을 보면 가로축을 시간축으로 보고 x 는 1,2,3,4... , y는 A, B, C, D 와 같이 값이 들어온다고 했을 때 combineLatest(x,y,(x,y)=>(x+y)) 로 한 예다.
동작을 보면 A, B 의 상태는 mousedown이 일어나기 전의 mousemove의 스트림이라고 볼 수 있다. 이는 무시해야한다.
1을 mousedown 시점의 deltaX, deltaY라고 하면 move$에서도 쓸 수 있을 것이다.
Rx.Observable.combineLatest(
down$,
move$,
(down, move)=>({down, move})
)
이렇게 모으면
.subscribe(({down, move})=> {
box.style.left = (move.clientX - down.deltaX) + 'px';
box.style.top = (move.clientY - down.deltaY) + 'px';
});
여기서 쓸 수 있다.
이를 위한 Operator 가 있다. takeUntil 이다.
우리는 항상 그만 두어야할 때를 꼭 알아야한다.
takeUntil은 Observable을 인자로 갖는데 여기에 무언가 들어올 때 Subscribe를 종료하고 Complete을 호출한다.
마지막으로 이 모든 과정이 완료되고 난 다음 Repeat()을 사용해 재가입하는 것을 잊지말자.
남은 건 combineLatest를 포함한 이 drag작업을 언제까지 할 것인가인데 당연히 up$ 스트림에 "무언가가" 들어오는 시점이 되겠다.mousedown 이후 시점으로부터mouseup이 되기전까지mousemove 를 Observe 하는 것
이를 위한 Operator 가 있다. takeUntil 이다.
우리는 항상 그만 두어야할 때를 꼭 알아야한다.
takeUntil은 Observable을 인자로 갖는데 여기에 무언가 들어올 때 Subscribe를 종료하고 Complete을 호출한다.
마지막으로 이 모든 과정이 완료되고 난 다음 Repeat()을 사용해 재가입하는 것을 잊지말자.
댓글
댓글 쓰기