Bacon.js懒惰评估,鼠标拖动示例中断没有log()语句

时间:2013-05-16 15:14:25

标签: javascript reactive-programming frp bacon.js

我试图理解bacon.js和FRP,所以试图做一个简单的拖放示例,但我对一段代码的懒惰评估有问题。当我在流中添加.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一起更新。

1 个答案:

答案 0 :(得分:2)

您所描述的行为与延迟评估几乎没有关系:问题的根源是执行顺序。

在您的代码中(log()上没有block_pos_at_mousedownmouseDeltaFromClick似乎在block_pos_at_mousedown之前发生了变化(我必须说我不确切地知道{{1}改变顺序)。让我们坚持下去。

log()方法期望observable.combine作为第一个参数 - 您传递的Property将自动转换。现在,只要EventStream mouseDeltaAndBlockPos发生变化,mouseDeltaFromClick就会发生变化(从而触发所有回调)。

因此,当block_pos_at_mousedownmouseDeltaFromClick之前触发时,代码末尾的回调会使用新的delta 调用,但具有旧的阻止位置(因为block_pos_at_mousedown是转换为back_pos_at_mousedown)。旧值为Property,因此每次点击时该块都会捕捉到左上角。

如何解决?安全的方法是不假设不相关的回调的执行顺序,并记住这一点再次写它。我想出了这个:

[0,0]

并且jsFiddle:http://jsfiddle.net/aknNh/25/

编辑:在评论中 raimohanska 使用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" }); }); }); 建议了另一种解决方案:http://jsfiddle.net/TFPge/1/