简体   繁体   中英

Get relative x and y div after rotation

So here's one which I wished I paid more attention in maths class for:

http://i.imgur.com/noKxg.jpg

I have a big container div and inside of this I have a few content divs which are rotated.

What I'm trying to do is animate between the child divs by moving the Main Div around in the viewport. This would be really easy if I was just doing it based upon simple x/y (top/left) calculations, however when there's rotation involved, my maths just breaks down.

I have tried a few things and haven't really cracked it.

Here's a simplified version of my results sort far, please feel free to fiddle:

http://jsfiddle.net/dXKJH/3/

I really can't figure this out!

[EDIT]

I'm going with this solution as a preference simply because I've already worked a way of making the rotate plugin work with MSIE6+.

Howver I have to say that although I follow all the math functions and they seem clean the results are not pixel perfect is this something to do with a PI calculation? It seems that the bigger and more spaced out i make the boxes the less likely they are to match up to the top left? Oddness.

Also can anyone remind me of what trig thing I need to do if the angle is more than 45 degress, I can't find a reference I remember this from maths class years ago when there were 4 quadrants or something... rr how i wish i paid more attention.. :-)

Thank you hugely for all the help so far!!

Well, this is how far I got... I made everything smaller so as to see better where the div s end up, and also added scrollbars for this reason.

Important points:

  • Math.sin / Math.cos require radians, not degrees
  • CSS rotates around the midpoint of the element, not (0, 0) (this applies to both the main div and the box div s; first translate -width / 2 , -height / 2 , rotate, and then translate back)
  • Use parseInt(x, 10) to make sure you're using base 10

Final code: http://jsfiddle.net/pimvdb/dXKJH/10/ . This solution needs hardcoded positions and rotations in HTML as well since .css was having some quirks with rotated elements.

$("#div-1").rotate("-10deg");
$("#div-2").rotate("10deg");
$("#div-3").rotate("15deg");
$("#div-4").rotate("75deg");

$("#main").click(function() {
    console.log($('body').scrollLeft(), $('body').scrollTop());
});

$("a").click(function(e){
    goTo($(this).attr("id").substring(4,5));
    return false;
});


function n(x) {
    return parseInt(x, 10);
}

function sin(x) {
    return Math.sin(x / 180 * Math.PI);
}

function cos(x) {
    return Math.cos(x / 180 * Math.PI);
}

function rotate(x, y, a) {
    var x2 = cos(a) * x - sin(a) * y;
    var y2 = sin(a) * x + cos(a) * y;
    return [x2, y2];
}


var offsets = [null,
              [0,100,-10],
              [100,200, 10],
              [300,100, 15],
              [400,100, 75]].map(function(v) {
                  if(!v) return v;
                  var rotated = rotate(-50, -50, v[2]);
                  rotated[0] += v[0] + 50;
                  rotated[1] += v[1] + 50;
                  return rotated.concat(v[2]);
               });


function goTo(num){
    var obj = $("#div-" + num);
    var angle = -n(obj.rotate());
    var pointX = offsets[num][0] - 500;
    var pointY = offsets[num][1] - 500;
    var rotated = rotate(pointX, pointY, angle);
    var newX = rotated[0] + 500;
    var newY = rotated[1] + 500;

    $("#main").animate({rotate: angle + "deg"}, 1000);  
    $("body").animate({scrollLeft: newX,
                       scrollTop:  newY}, 1000)
}

I'd like to suggest a different approach:

  1. Move to div-2 from the starting state (reposition Main Div using left/top styles)
  2. Set transform-origin of Main Div to center of view (also center of div-2)
  3. Rotate Main Div by the opposite amount of div-2 (div-2 should be centered correctly)
  4. Rotate Main Div back to 0 degrees.
  5. Move to div-3
  6. Set transform-origin of Main Div to center of view (also center of div-3)
  7. Rotate Main Div by the opposite amount of div-3 (div-3 should be centered correctly)
  8. Rinse and repeat

This may be easier and result in less complex (more maintainable) code. Note that the moves and rotates could be animated. Also, if you use translations instead of left/top styles this solution will be far more complicated due to the transform-origin changes.

It looks like you are trying to mimic Apple's iPhone website: http://www.apple.com/iphone/ . I've emulated this technique here:

http://jsfiddle.net/Yw9SK/2/ (uses Webkit transitions)

This really doesn't require much math, don't over-engineer it. You just need to cancel the rotation of the child <div> s and adjust for their width/height and offset from the container <div> .

Here's how to do it:

container width: 500  height: 500  rotation: 0       top: 0    left: 0
child     width: 90   height: 90   rotation: 120deg  top: 330  left: 0


  1. To ensure the rotation is correct, the container must get the negative rotation of the child:

     child rotation: 120deg --> container rotation: -120deg 
  2. To have the child centered in the view, we need to subtract its width from the container width, divide by 2 (to keep it centered), and then subtract any offsets. The same thing happens for height:

     Width Height 500 (container width) 500 (container height) - 60 (child width) - 40 (child height) ---- ---- 440 460 / (divide by 2 / (divide by 2 2 to keep it centered) 2 to keep it centered) ---- ---- 220 230 - 0 (subtract the left: offset) -330 (subtract the top: offset) ---- ---- 220 translateX -100 translateY 


Because the "stage" is centered on top of the container, the new transform will also be centered within the stage. (It's not much harder to make this happen for a rectangle compared to a square.) Thus our transform to be applied to the container <div> is:

transform: rotate(-120eg) translateX(220px) translateY(-100px)

Rinse and repeat for the other child elements and transition through them accordingly.


This JSFiddle is just an example of how to accomplish this. It is not a very well-designed implantation. A more optimal solution would have a transition-delay or animation-delay (instead of setInterval) and then listen for the 'transitionEnd' or 'animationEnd' events.

I would definitely recommend NOT trying to calculate the new positions and rotations programmatically on the fly (although you can, and then you just have to get those values from the .style property in JS). I would recommend having the translations/rotations you need pre-defined upfront (as seen in my example, though they should be in CSS) and simply apply them in correct order to the container. This will ensure the transitions are fast and smooth.

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