我试图理解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
一起更新。
答案 0 :(得分:2)
您所描述的行为与延迟评估几乎没有关系:问题的根源是执行顺序。
在您的代码中(log()
上没有block_pos_at_mousedown
)mouseDeltaFromClick
似乎在block_pos_at_mousedown
之前发生了变化(我必须说我不确切地知道{{1}改变顺序)。让我们坚持下去。
log()
方法期望observable.combine
作为第一个参数 - 您传递的Property
将自动转换。现在,只要EventStream
或 mouseDeltaAndBlockPos
发生变化,mouseDeltaFromClick
就会发生变化(从而触发所有回调)。
因此,当block_pos_at_mousedown
在mouseDeltaFromClick
之前触发时,代码末尾的回调会使用新的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/