简体   繁体   中英

Rotate element to click position

I have a project with a circle that, when clicked, rotates to a predefined position. It is almost there, but the last requirement is that it always rotates clockwise to the marker. I just can't seem to figure out how to get the right value so that when i set css transform:rotate(Xdeg), it will always go clockwise. Keeping the angle between 0 and 360 would also be a plus for another piece of this, but not necessary.

See this fiddle, javascript below as well Rotation

$(function () {
$('body').on('click', '#graph1', function (e) {

    console.log('********************');
    //get mouse position relative to div and center of div for polar origin
    var pos = getMousePosAndCenter(e, 'graph1');

    //get the current degrees of rotation from the css
    var currentRotationDegrees = getCSSRotation('#graph1');
    console.log('CSS Rotation Value: ' + currentRotationDegrees);

    //current rotation in radians
    var currentRotationRadians = radians(currentRotationDegrees);

    //radians where clicked
    var clickRadiansFromZero = Math.atan2(pos.y - pos.originY, pos.x - pos.originX);

    //degrees the click is offset from 0 origin
    var offsetDegrees = degrees(clickRadiansFromZero);

    //how many degrees to rotate in css to put the mouse click at 0
    var degreesToZero;
    if (offsetDegrees >= 0)
        degreesToZero = currentRotationDegrees - Math.abs(offsetDegrees);
    else
        degreesToZero = currentRotationDegrees + Math.abs(offsetDegrees);

    console.log("Degrees to Zero: " + degreesToZero);

    //distance in pixels from origin
    var distance = calculateDistance(pos.originX, pos.originY, pos.x, pos.y);

    console.log("Distance From Origin(px): " + distance);

    $('#graph1').css('transform','rotate(' + degreesToZero + 'deg)')
});

});

function getMousePosAndCenter(e, id) {
    var rect = document.getElementById(id).getBoundingClientRect();
    return {
        x: (((e.clientX - rect.left) / rect.width) * rect.width) + 0.5 << 0,
        y: (((e.clientY - rect.top) / rect.height) * rect.height) + 0.5 << 0,
        originY: (rect.height / 2),
        originX: (rect.width / 2)
    };
}

function radians(degrees) {
    return degrees * Math.PI / 180;
};

function degrees(radians) {
    return radians * 180 / Math.PI;
};

function calculateDistance(originX, originY, mouseX, mouseY) {
    return Math.floor(Math.sqrt(Math.pow(mouseX - originX, 2) + Math.pow(mouseY - originY, 2)));
}

function getCSSRotation(id) {
    var matrix = $(id).css('transform');
    var values = matrix.split('(')[1],
    values = values.split(')')[0],
    values = values.split(',');

    var a = values[0];
    var b = values[1];
    var c = values[2];
    var d = values[3];

    var cssRotation = degrees(Math.atan2(b, a));
    return cssRotation;
}

UPDATE: Figuring it would be easy to do, I found it a little harder than I thought. The other answer with jQuery.animate works, but animate doesn't have the fluid framerate that css animation does (it runs on the GPU).

Here's a modified fiddle with a CSS solution: http://jsfiddle.net/2g17cjuL/2/

Keeping the angle between 0 and 360 would also be a plus

You cannot keep going forward (ie rotating by a positive number) and keep the rotation positive, however, in my fiddle offsetDegrees (the number of degrees additional rotated), or the remainder of totalDegrees divided by 360 should give you what you need to use elsewhere.

Think out of the box:
We can CSS3 rotate an element with transform to ie: 720° ...
it will make 2 clockwise turns. (OK, in our UI it can only do max a 359 turn but let's follow the math)
If we than animate it to 810° ... it just means that it'll do a 90° clockwise move !

So all we need to do is always increase a degree variable to insanity!

HEY! If at some point you want to keep track of the current normalized 0-360 degree...
you can always retrieve that value doing ourCurrentInsanelyHighDegree % 360 = UIdegrees

Here's a jsBin demo

and this is all the JS you need.

 function getCSSRotation( $el ) { var matrix = $el.css('transform'), v = matrix.split('(')[1].split(')')[0].split(','), rds = Math.atan2(v[1], v[0]); return rds*180/Math.PI <<0; // Degrees } var $EL = $("#graph1"), w = $EL.width(), r = w/2, // Radius x = parseInt($EL.css("left"), 10), y = parseInt($EL.css("top"), 10), d = getCSSRotation( $EL ); // Initial degree (ONLY ONCE!) $EL.on("click", function(e){ var mx = e.clientX-xr, // Click coord X my = e.clientY-yr, // Click coord Y rds = Math.atan2(-my, -mx), // Radians md = (rds*180/Math.PI<<0) + 180; // Mouse Degrees d += (360-md); // always increment to insanity!! $(this).css({transform:"rotate("+ d +"deg)"}); }); 
 #graph1 { position:absolute; top:10px; left:30px; width:200px; height:200px; background:url(//placehold.it/200x200&text=IMAGE); transition:transform 2s ease; transform:rotate(30deg); transform-origin:50% 50%; border-radius:50%; } #marker { position: absolute; top:110px; left:230px; border-top:1px solid black; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="graph1"></div> <div id="marker">Wherever you click, it rotates to here</div> 

Requrement: That it always rotates clockwise.

One thing: If you use CSS transitions, it'll calculate the shortest route for you. You want a bit more control over rotational direction, so I commented out the transition:transform 1s ease; in your CSS because we'll control this manually.

JAVASCRIPT

I borrowed this JQuery function and modified it so we can feed it a starting angle, and ending angle and it'll animate #graph1 for us. (Read the link to change duration, easing, and to use the complete callback)

$.fn.animateRotate = function(angle, start, duration, easing, complete) {
  var args = $.speed(duration, easing, complete);
  var step = args.step;
  return this.each(function(i, e) {
    args.complete = $.proxy(args.complete, e);
    args.step = function(now) {
      $.style(e, 'transform', 'rotate(' + now + 'deg)');
      if (step) return step.apply(e, arguments);
    };

    $({deg: start}).animate({deg: angle}, args);
  });
};

I also modified your JQuery so it won't rotate counter-clockwise: when currentRotationDegrees is greater than degreesToZero , it'll subtract 360, and then use this new value as the starting position for `animateRotate().

if(currentRotationDegrees > degreesToZero){
    currentRotationDegrees -= 360;
}

$('#graph1').animateRotate(degreesToZero, currentRotationDegrees);

Here it is in action.

http://jsfiddle.net/q4nad31t/1/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM