Search code examples
javascriptkineticjs

KineticJS Drag event is preventing Double-click event from firing


I have a node in KineticJs that has both a drag handler and double-click handler on it. When a user attempts to double-click on the object and moves slightly during the initial click, the drag handler intercepts what would be the double-click, breaking the experience. I have googled this extensively and have tried many solutions to no avail. This issue is captured in the link below but no update has been made to kinetic.

https://github.com/ericdrowell/KineticJS/issues/243

Example Code:

shape.on("dblclick dbltap", function (pos) {
    ModalWindow(this.parent.data,pos); //Loads a modal window
});

shape.on("mousedown",function(e) {
    this.setDraggable(false);
    var that = this;
    console.log("Drag Off");
    setTimeout(function(){
        that.setDraggable(true);
        console.log("Drag On");
    },1000);
});

Solution

  • Determining Drag vs Click is a common problem.

    One common way to handle the conflict is:

    • on dragstart save the starting position of the node
    • on dragend check if the node has moved less than 10 pixels (or whatever distance)
    • if <10 pixels, move the node back to its starting position
    • if <10 pixels, fire the click event

    Here's example code and a Demo: http://jsfiddle.net/m1erickson/yh67y/

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>Prototype</title>
        <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
        <script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.1.min.js"></script>
    <style>
    body{padding:20px;}
    #container{
      border:solid 1px #ccc;
      margin-top: 10px;
      width:350px;
      height:350px;
    }
    </style>        
    <script>
    $(function(){
    
        $p=$("#event");
    
        var stage = new Kinetic.Stage({
            container: 'container',
            width: 350,
            height: 350
        });
        var layer = new Kinetic.Layer();
        stage.add(layer);
    
        var circle1 = new Kinetic.Circle({
            x:100,
            y:100,
            radius: 30,
            fill: 'red',
            stroke: 'black',
            strokeWidth: 4,
            draggable: true
        });
        circle1.startingPos;
        circle1.referredEvent="";
        circle1.on("dragstart",function(){
            this.startingPos=this.position();
        });
        circle1.on("dragend",function(){
            var endingPos=this.position();
            var dx=Math.abs(endingPos.x-this.startingPos.x);
            var dy=Math.abs(endingPos.y-this.startingPos.y);
            if(dx<10 && dy<10){
                this.position(this.startingPos);
                this.referredEvent="--from drag";
                this.fire("click");
                layer.draw();
            }
        });
        circle1.on("click",function(){
            $p.text("clicked"+this.referredEvent);
            this.referredEvent="";
        });
        layer.add(circle1);
        layer.draw();
    
    }); // end $(function(){});
    </script>       
    </head>
    <body>
        <h4>Drags less than 10 pixels will cause click<br>instead of drag.</h4>
        <p id="event">Drag or Click the circle</p>
        <div id="container"></div>
    </body>
    </html>
    

    Now for double-tapping:

    Since the referred click won't count towards one of the taps of a double-tap, you will have to save the time each click occurs (in the click event handler). If you have 2 clicks within a half-second (or whatever time limit) then trigger the doubletap event--this.fire("doubletap");

    And Yes...this all sounds like a hack.

    But the fact is a mousedown can equally signal a click or a drag.

    This is the best workaround we've got.