Michael Palm
Michael Palm

Reputation: 357

KinectJS: Rotate an image about an absolute rotate point per drag and drop

I'd like to rotate an Image about an absolute rotate point with kinectJS. For example about

var rotatePoint = {x: 500, y: 500}

The rotation schould be initialised by clicking at the image and moving the mouse, i.e. by dragging and dropping the image. Therby it schould rotate about the same angle, the mouse is drawing.

Yesterday I worked all day at this problem an coudn't find a solution.

Any ideas?

Thanks!

Upvotes: 0

Views: 626

Answers (2)

Michael Palm
Michael Palm

Reputation: 357

Thank you!

I finaly got the clue. In the following sample-code the image imgObj rotates about

imgObj.rotPoint = {x: 500, y: 500}

There where several problems to solve: You can't just set an absolute rotate point, you have to change the offset relative to its standard-position (upper left edge of the image). Then you have to move the image back, because the change of the offset moved the image.

For the roation I enabled dragging and used a dragBoundFunc.

Setting

return {x: this.getAbsolutePosition().x, y: this.getAbsolutePosition().y};

will asure you, that there will be no real dragging - just rotation.

For the rotation itself you need six values: Three values at the beginning of the dragging:

  • posMouseStart: x-y-position of the mouse
  • radMouseStart: angle between the positive x-axis and the vector from the rotation point to posMouseStart in radians
  • radImageStart: the current rotation of the image (maybe it is already rotated?)

I get these values by binding onmousedown and by just using these values if there is a dragging and dropping.

Three values that change all the time while dragging and dropping:

  • posMouseNow: x-y-position of the mouse right in that moment
  • radMouseNow: angle between the positive x-axis and the vector from the rotation point to posMouseStart in radians right in that moment
  • radImageNow: the current rotation of the image (maybe it is already rotated?) right in that moment

I get these values in my dragBoundFunc.

While using Math.atan2() for getting the angles, you have to concern, that you're not in an x-y-coordinatesystem but in an x-(-y)-coordinatesystem - so use: Math.atan2(-y,x).

By subtracting radMouseStart from radMouseNow you get the angle you would have to rotate about, to get the image from the start-position to the now-position. But, when we would rotate about this angle, the image would rotate like crazy. Why is it like that? - There are several miliseconds between "start" and "now" where the image is already rotating. So: when you're "now" rotating, you don't beginn at radMouseStart but at radImageNow - radImageStart + radMouseStart.

\o/ all problems are solved \o/

The code:

var imgObj = new Kinetic.Image
                 ({
                        image: YourImg,
                        x: YourSize.x,
                        y: YourSize.y,
                        draggable: true
                 });
imgObj.rotPoint = {x: 500, y: 500};
imgObj.setOffset
(
    imgObj.rotPoint.x - imgObj.getAbsolutePosition().x,
    imgObj.rotPoint.y - imgObj.getAbsolutePosition().y
);
imgObj.move(imgObj.getOffsetX(),imgObj.getOffsetY());
var o = {x: imgObj.rotPoint.x, y: imgObj.rotPoint.y}; // shortcut
imgObj.on('mousedown', function()
                         {
                             posMouseStart = stage.getMousePosition();
                             radMouseStart = Math.atan2(-(posMouseStart.y - o.y), posMouseStart.x - o.x);
                             radImageStart = this.getRotation();
                         });
imgObj.setDragBoundFunc(function(pos)
                        {
                            var posMouseNow = stage.getMousePosition();
                            var radMouseNow = Math.atan2(-(posMouseNow.y - o.y), posMouseNow.x - o.x);
                            var radImageNow = this.getRotation();

                            var radMouseDiff = -(radMouseNow - radMouseStart);

                            this.rotate(radImageStart + radMouseDiff - radImageNow);

                            return {x: this.getAbsolutePosition().x, y: this.getAbsolutePosition().y};
                        });

Upvotes: 1

markE
markE

Reputation: 105015

You can move an image around a fixed rotation point using some trigonometry

Calculate the angle of the mouse position relative to the rotation point using Math.atan2:

    var dx=mouseX-rotationPointX;
    var dy=mouseY-rotationPointY;
    var radianAngle=Math.atan2(dy,dx);

Move the image around the rotationPoint using Math.cos and Math.sin:

    var x=rotationPointX+radius*Math.cos(radianAngle)-imgWidth/2;
    var y=rotationPointY+radius*Math.sin(radianAngle)-imgHeight/2;
    image.setPosition(x,y);

Here's working example code:

<!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-v4.5.4.min.js"></script>

<style>
body{ padding:15px; }
#container{
  border:solid 1px #ccc;
  margin-top: 10px;
  width:300px;
  height:300px;
}
</style>        
<script>
$(function(){

    var stage = new Kinetic.Stage({
        container: 'container',
        width: 300,
        height: 300
    });
    var layer = new Kinetic.Layer();
    stage.add(layer);

    var rx=150;
    var ry=150;
    var radius=75;

    var image;;
    var imgWidth=50;
    var imgHeight=50;

    var rotationPoint=new Kinetic.Circle({
        x:rx,
        y:ry,
        radius:10,
        fill:"green"
    });
    layer.add(rotationPoint);


    $(stage.getContent()).on('mousemove', function (event) {

        // get the current mouse position on the stage
        var pos=stage.getMousePosition();
        var mouseX=parseInt(pos.x);
        var mouseY=parseInt(pos.y);

        // calculate the mouse angle relative 
        // to the rotation point [rx,ry]
        var dx=mouseX-rx;
        var dy=mouseY-ry;
        var radianAngle=Math.atan2(dy,dx);

        // "normalize" the angle so it always is in a proper range (0-2*PI)
        radianAngle=(radianAngle+Math.PI*2)%(Math.PI*2);

        // calculate the new image x,y 
        // based on the angle
        var x=rx+radius*Math.cos(radianAngle)-imgWidth/2;
        var y=ry+radius*Math.sin(radianAngle)-imgHeight/2;
        image.setPosition(x,y);

        layer.draw();
    });


    var img=new Image();
    img.onload=function(){
        image=new Kinetic.Image({
            image:img,
            x:0,
            y:0,
            width:imgWidth,
            height:imgHeight,
        });
        layer.add(image);
        layer.draw();
    }
    img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house-icon.png";


}); // end $(function(){});

</script>       
</head>

<body>
    <p>Move mouse to rotate image around</p>
    <p>the green dot which is located at</p>
    <p>the rotation point</p>
    <div id="container"></div>
</body>
</html>

Upvotes: 0

Related Questions