2013-05-16 5 views
2

bacon.js와 FRP를 이해하려고 노력하면서 간단한 드래그 앤 드롭 예제를 만들려고 노력했지만 느린 평가에 문제가 있습니다. 한 조각의 코드. 스트림에 .log()을 추가하면 모양이 좋아 보이지만 제대로 작동하는 것처럼 보입니다. 그러나 제거하면 업데이트되지 않습니다. 여기에 내가 뭘하는지입니다 :Bacon.js lazy evaluation, 마우스 드래그 예제가 log() 문없이 끊어짐

// UI streams 
block_mousedown = block_el.asEventStream('mousedown').map(xyFromEvent); 
global_mousemove = html.asEventStream('mousemove').map(xyFromEvent); 
global_mouseup = html.asEventStream('mouseup'); 

// Composites 
isDragging = block_mousedown.merge(global_mouseup.map(0)); 
mouseDragging = Bacon.combineAsArray(isDragging, global_mousemove) 
    .filter(function(v){ return notZero(v[0]) }) 

mouseDeltaFromClick = mouseDragging 
    .map(getDelta) 

// Block offset when it was clicked on 
block_pos_at_mousedown = block_mousedown 
    .map(function(a,b){ return block_el.offset();}) 
    .map(function(e){ return [e.left, e.top]; }) 
    // If I remove this log(), it doesn't evaluate 
    .log(); 

// merge mouse delta with block position when clicked  
mouseDeltaAndBlockPos = mouseDeltaFromClick 
    .combine(block_pos_at_mousedown, '.concat') 
    .onValue(function(e){ 
     block_el.css({ 
      top : e[3]+e[1]+"px", 
      left : e[2]+e[0]+"px" 
     }); 
    });  

을 그리고 여기가 jsFiddle of it

가 내가 잘못이 모든에 대해 갈 수있다 생각하고

가,이 경우에도 올바른 접근 방식이다 입니까? mousedown에서 업데이트해야하지만 클릭하면 mousemove과 함께 업데이트되지 않는 블록의 위치를 ​​통과하려고합니다.

답변

2

설명하는 동작은 지연 평가와 관련이 거의 없습니다. 문제의 루트는 실행 순서입니다. (block_pos_at_mousedownlog()없이) 코드 mouseDeltaFromClick에서

block_pos_at_mousedown이 (내가 log()이 순서를 변경하는 방법을 정확히 모르는 말을해야) 전에 변경 보인다. 그걸 붙잡자.

메서드는 첫 번째 인수로 Property을 전달합니다. EventStream은 자동으로 변환됩니다. 자, mouseDeltaFromClick또는block_pos_at_mousedown이 변경 될 때마다 mouseDeltaAndBlockPos이 변경됩니다 (결과적으로 모든 콜백이 발생 함).

그래서, (back_pos_at_mousedownProperty로 전환 되었기 때문에) 코드의 끝 부분에 block_pos_at_mousedown 전에 mouseDeltaFromClick 화재 콜백이 새로운 델타 로하지만 기존 블록 위치로 호출 될 때. 이전 값은 [0,0]이므로 블록이 클릭 할 때마다 왼쪽 상단 모서리에 스냅됩니다.

문제를 해결하는 방법은 무엇입니까? 안전한 방법은 무관 한 콜백의 실행 순서에 대해 아무 것도 가정하지 않고 이것을 염두에두고 다시 작성하는 것입니다. 나는이 함께했다 :

function xyFromEvent(v){ return [v.clientX, v.clientY]; } 

function getDelta(t){ 
    var a = t[1]; 
    var b = t[0]; 
    return [a[0]-b[0], a[1]-b[1]]; 
} 

function add(p1, p2) { 
    return [p1[0] + p2[0], p1[1] + p2[1]]; 
} 

$().ready(function() { 
    var block = $("#clickable-block"); 
    var html = $("html"); 

    var blockDragging = block.asEventStream('mousedown').map(true) 
          .merge(html.asEventStream('mouseup').map(false)) 
          .toProperty(false); 

    var deltas = html.asEventStream('mousemove').map(xyFromEvent).slidingWindow(2,2).map(getDelta); 

    // Just like deltas, but [0,0] when user is not dragging the box. 
    var draggingDeltas = Bacon.combineWith(function(delta, dragging) { 
     if(!dragging) { 
      return [0, 0]; 
     } 
     return delta; 
    }, deltas, blockDragging); 

    var blockPosition = draggingDeltas.scan([0,0], add); 

    blockPosition.onValue(function(pos) { 
     block.css({ 
      top : pos[1] + "px", 
      left : pos[0] + "px" 
     }); 
    }); 
}); 

그리고 jsFiddle : http://jsfiddle.net/aknNh/25/

편집이 : 댓글에서 raimohanskaflatMap를 사용하여 다른 솔루션을 제안했다 : http://jsfiddle.net/TFPge/1/

+0

게으른 평가와 평가의 순서를 손에 손을 이동합니다. .. –

+0

당신의 코드가 특정 시간에 호출되는 * map * 함수에 의존 할 때, 그 lazy evaluation은 예기치 않은 행동을 일으키는 것에 동의합니다. Lukasz의 대답은 나에게 꽤 잘 보인다. 다음을 제외하고는 Wolfflow의 그림과 같이 flatMap을 사용했을 것입니다. http://codepen.io/wolfflow/pen/cudiJ Google 그룹스에 관련 스레드가 있습니다. https://groups.google.com/forum/ #! topic/baconjs/OR0C2jPCF9g – raimohanska

+0

그리고 여기 flatMap을 가진 나의 바이올린 : http://jsfiddle.net/TFPge/ – raimohanska