2

bacon.js と FRP を理解しようとしているので、簡単なドラッグ アンド ドロップの例を作成しようとしましたが、1 つのコードの遅延評価に問題があります。をストリームに追加する.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があります

私はこれについてすべて間違っているのではないかと考えていますが、これは正しいアプローチですか? クリックされたときにブロックの位置を通過したいのですが、更新する必要がmousedownありますが、mousemove.

4

1 に答える 1

2

あなたが説明している動作は、遅延評価とはほとんど関係がありません。問題の根本は実行順序です。

あなたのコードでは( on なしlog()block_pos_at_mousedownmouseDeltaFromClick前に変更されているようですblock_pos_at_mousedown(順序がどのように正確に変更されるかはわかりませんlog())。我慢しましょう。

メソッドは最初の引数としてobservable.combine期待します -あなたが渡したものは自動的に変換されます。現在、またはが変更されるたびに変更されます (したがって、すべてのコールバックが起動されます) 。PropertyEventStreammouseDeltaAndBlockPosmouseDeltaFromClick block_pos_at_mousedown

そのため、コードの最後のコールバックが新しいデルタで呼び出されるmouseDeltaFromClick前に発火するが、古いブロック位置で呼び出されます(が に変換されたため)。古い値は、クリックするたびにブロックが左上隅にスナップするようになっています。block_pos_at_mousedownback_pos_at_mousedownProperty[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/

編集: コメントでライモハンスカは次を使用して別の解決策を提案していましたflatMap: http://jsfiddle.net/TFPge/1/

于 2013-06-23T22:36:29.510 に答える