Javascript Bacon.js懒惰求值,鼠标拖动示例在没有log()语句的情况下中断
我试图理解bacon.js和FRP,因此尝试制作一个简单的拖放示例,但我在对一段代码进行惰性计算时遇到了问题。当我将Javascript Bacon.js懒惰求值,鼠标拖动示例在没有log()语句的情况下中断,javascript,reactive-programming,frp,bacon.js,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(x
.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"
});
});
这里有一个
我在想我可能做得不对,这是正确的方法吗?我想通过单击时块的位置,该位置应在
mousedown
上更新,但不能随mousemove
一起更新。您描述的行为与惰性评估几乎没有关系:问题的根源是执行顺序
在你的代码中(没有log()
onblock\u pos\u at mousedown
)mousedeltaffomclick
似乎在block\u pos\u at mousedown
之前发生了变化(我必须说我不知道log()
到底是如何改变顺序的)。等一下
observable.combine
方法期望将属性
作为第一个参数-EventStream
将自动转换您传递的。现在,mouseDeltaAndBlockPos
在mousedeltafarmclick
或block\u pos\u mousedown
更改时更改(并因此触发所有回调)
因此,当mousedeltaffomclick
在block\u pos\u at mousedown
之前触发时,代码末尾的回调将使用新的增量调用,但使用旧的块位置(因为back\u pos\u at mousedown
已转换为属性)。旧值为[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:
编辑:在评论中,raimohanska
提出了另一种使用flatMap
的解决方案:我同意,如果您的代码依赖于在特定时间调用的map函数,那么延迟求值会导致意外行为。卢卡斯的回答在我看来仍然很好。除了我会使用flatMap,比如Wolfflow的绘图示例:在Google群组中也有一个相关的线程:实际上这有点坏了。这里有一个更好的:漂亮的!我已在答案中添加了此解决方案。