jQuery .position() strangeness while using CSS3 rotate attribute
Asked Answered
I

2

15

I'm getting absolutely positioned rotated elements position with jQuery .position() method, then setting position-related attributes (top, left) with jQuery .css(pos), where pos is the data returned by .position(). I think it'll leave the element in it's place, but elements position is changing.

How can I use set rotated elements position, so that it'll be placed as expected? Maybe there is a coefficient depended on angle that changes position?

I'm testing in Google Chrome v.9, Windows XP.

HTML

<div id="container">
  <div id="element"> 
    <img src="http://t0.gstatic.com/images?q=tbn:ANd9GcS0Fawya9MVMez80ZusMVtk_4-ScKCIy6J_fg84oZ37GzKaJXU74Ma0vENc"/>
  </div>
</div> 

CSS

#container {
    position: relative;   
    border: 1px solid #999;
    padding: 5px;
    height: 300px;
    width:300px;
}    
#element {
    position: absolute;
    top:50px;
    left: 60px;
    width: auto;
    border: 1px solid #999;
    padding: 5px;
    -webkit-transform: rotate(45deg);
    -moz-transform: rotate(45deg);
}

JS

$(document).ready(function(){
    var $el = $('#element'),
    // getting position    
        pos = $el.position();
    alert(pos.left + '/' + pos.top);
    // alerts 37/11

    // setting css position attributes equal to pos
    $el.css(pos);
    // re-getting position
    pos = $el.position();
    alert(pos.left + '/' + pos.top);
    // alerts 14/-28
});    

View it http://jsfiddle.net/Antaranian/2gVL4/

Isolde answered 22/2, 2011 at 13:42 Comment(5)
One obvious solution would be to straighten up the image temporarily whenever you read/write its position properties...Truncation
Thanks for the answer @Šime, I thought about it too, but it'll have a big effect on productivity of the whole app, and user will feel is visually, so I'm looking for the more intelligent way to solve it.Isolde
It works in Opera and Firefox both alerting 50/60.Albania
@Albania It works in Opera because the image is not rotated in that browser. Add -o-transform: rotate(45deg); to the #element CSS rules and it won't work in Opera, too.Truncation
Sorry, I forgot to mention that I'm about Google Chrome Browser.Isolde
S
7
// Needed to read the "real" position
$.fn.adjustedPosition = function() {
    var p = $(this).position();
    return {
        left: p.left - this.data('dx'),
        top: p.top - this.data('dy')
    }
};

$(function() { 

    var img = $('img'),
        pos;

    // Calculate the delta
    img.each(function() {
        var po = $(this).position(), // original position
            pr = $(this).addClass('rot').position(); // rotated position

        $(this).data({
            dx: pr.left - po.left, // delta X
            dy: pr.top - po.top // delta Y
        });
    });

    // Read the position
    pos = img.adjustedPosition();    
    alert(pos.left + '/' + pos.top);     

    // Write the position
    img.css(pos);

    // Read the position again
    pos = img.adjustedPosition();    
    alert(pos.left + '/' + pos.top);

});

Live demo: http://jsfiddle.net/2gVL4/4/

So what is going on here:

  1. The CSS code that rotates the image is stored inside a special CSS class. I do this because I want to read the original position of the image (before rotating). Once I read that original position, I apply the .rot class, and then read the position again to calculate the difference (delta), which is stored inside the element's data().

  2. Now, I can read the position via the custom method adjustedPosition (which is defined above). This method will read the position of the element and then subtract the delta values stored inside the data() of the element.

  3. To write the position, just use the css(pos) method like normally.

Szombathely answered 22/2, 2011 at 17:56 Comment(1)
Maybe there's a browser problem here (using firefox) but after updating the fiddle to use transform: rotate() it doesn't get the new positions after rotating.Numismatology
Y
1

Had similar problem. There is simple solution (not elegant, but working):

  • set current angle to 0
  • read X/Y position
  • revert angle back to its value

    var temp = $(this).position();
    temp.angle = getRotationDegrees( $(this) );            // remember current angle
    rotateObject($(this), 0);                              // set angle to 0
    temp.left = Math.round($(this).position().left);       // proper value
    temp.top = Math.round($(this).position().top);         // proper value
    
    // revert back the angle
    rotateObject($(this), temp.angle); 
    

Used functions:

    function rotateObject(obj, angle) {
        obj.css({ '-webkit-transform': 'rotate(' + angle + 'deg)'});
        obj.css({ '-moz-transform': 'rotate(' + angle + 'deg)'});
        obj.css({ '-ms-transform': 'rotate(' + angle + 'deg)'});
        obj.css({ 'msTransform': 'rotate(' + angle + 'deg)'});
        obj.css({ '-o-transform': 'rotate(' + angle + 'deg)'});
        obj.css({ '-sand-transform': 'rotate(' + angle + 'deg)'});
        obj.css({ 'transform': 'rotate(' + angle + 'deg)'});
    }

    function getRotationDegrees(obj) {
        var matrix = obj.css("-webkit-transform") ||
        obj.css("-moz-transform")    ||
        obj.css("-ms-transform")     ||
        obj.css("-o-transform")      ||
        obj.css("transform");
        if(matrix !== 'none') {
            var tr;
            if (tr = matrix.match('matrix\\((.*)\\)')) {
                tr = tr[1].split(',');
                if(typeof tr[0] != 'undefined' && typeof tr[1] != 'undefined') {
                    var angle = Math.round(Math.atan2(tr[1], tr[0]) * (180/Math.PI));
                }else{
                    var angle = 0; 
                }
            }else if(tr = matrix.match('rotate\\((.*)deg\\)')){
                var angle = parseInt(tr[1]); 
            }
        } else { var angle = 0; }
        return (angle < 0) ? angle + 360 : angle;
    }
Yesima answered 15/7, 2016 at 10:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.