Search code examples
javascriptcss-transitionskineticjs

In KineticJS, on a canvas rotated using css3, the events don't seem to be working properly


I'm using Kineticjs for a rotating pie-chart widget. When I try to draw on a rotated canvas element (the parent node is rotated 60deg using CSS3), the events don't seem to be working properly. For example, the hover event on a 15 deg clockwise rotated canvas is 15 deg off. Any Ideas?


Solution

  • The answer to your question is not trivial---here’s why:

    Your DOM container is in transformed space.

    Your Kinetic objects react as if they are in non-transformed space.

    Your kinetic objects are mis-responding because the browser is feeding them transformed mouse positions.

    The simple fix: Leave the DOM container untransformed and do all rotations inside KineticJS

    The difficult fix: convert rotated DOM mousepoints into unrotated points for Kinetic to use.

    Here's the difficult fix:

    The default rotation-point of CSS transforms is 50%,50% (the middle of the element) so find the center of the Kinetic stage

    var cx=stage.getWidth()/2;
    var cy=stage.getHeight()/2;
    

    Given a mouseX/mouseY in transformed space (DOM space), you need to find the untransformed point (KineticJS space)

    var unrotatedPoint = unrotatedXY(cx,cy, mouseX,mouseY, cssDegreeRotation);
    

    Here’s the function that does that calculation:

    function unrotatedXY(cx,cy, mouseX,mouseY, cssDegreeRotation) {
    
        var dx=mouseX-cx;
        var dy=mouseY-cy;
        var r=Math.sqrt(dx*dx+dy*dy);
        var cssRadianAngle = cssDegreeRotation * Math.PI/180;
    
        // calc the angle of the mouse position
        var rotatedAngle = Math.atan2(dy,dx);
    
        // unrotate the mouse position by the css rotation
        var unrotatedAngle = rotatedAngle -= cssRadianAngle;
    
        // normalize the angle
        if(unrotatedAngle<0){ unrotatedAngle+=Math.PI*2; }
    
        // calc the unrotated XY
        unrotatedX = cx+ r * Math.cos(unrotatedAngle);
        unrotatedY = cy+ r * Math.sin(unrotatedAngle);
    
        return({x:unrotatedX,y:unrotatedY});
    }
    

    The mouseX/mouseY above are coming from the document, not KineticJS.

    This means you must listen for mouse events on the document (or your container element), not in KineticJS itself.

    $(document).mousemove(function(e){handleMouseMove(e);});
    
    function handleMouseMove(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
    
     // convert the DOM mousepoint to a Kinetic mousepoint
    var unrotatedPoint = unrotatedXY(cx,cy, mouseX,mouseY, cssDegreeRotation);
    
    // Now you can check for hovers, etc against your Kinetic nodes …
    
    }
    

    To tie back into KineticJS, you might use node.fire to trigger events using custom event objects containing your converted mouse coordinates.