The Wayback Machine - https://web.archive.org/web/20151209071810/http://staff.washington.edu/fmf/2011/07/15/css3-transform-attribute-order/
  • CSS 15.07.2011

    CSS3 transforms are powerful and are relatively straightforward to use. However, care must be taken when you apply more than one transform. This post isn’t a tutorial on using transforms, but concentrates on how transforms behave when combined. More information is available in the 2D Transforms Working Draft.

    These examples consist of a solid red square with a nested blue square, and a larger black border encompassing the two boxes. The HTML is:

    <div class="wrap">
        <div class="red">
            <div class="blue"></div>
        </div>
    </div>

    and the common CSS to get the initial size, position, and color is equivalent to:

    .wrap {
        position: relative;
        width: 10em;
        height: 10em;
        border: 0.25em black solid;
    }
    .red {
        position: relative;
        left: 1em;
        top: 1em;
        width: 8em;
        height: 8em;
        opacity: 0.75;
        background: red;
    }
    .blue {
        position: relative;
        width: 100%;
        height: 100%;
        opacity: 0.75;
        background: blue;
    }

    With no transformations the layout would look like:

    You don’t see the red square since it’s completely hidden by the blue square, although since both squares have 75% opacity, some of the red is blended into the blue.

    This page should work on the most recent versions of the major browsers. It uses the transform property, but also replicates the properties with vendor prefixes. While those prefixed properties are in the CSS file, they are not listed below for brevity.

    Nested Transforms

    Just as with nested objects with absolute positioning, nested objects of CSS transforms inherit properties from parents. For example, if you have one object with absolute positioning which is moved over by 100px, all child objects will be positioned relative to the parent, or 100px relative to its parent’s parent. If one of those child objects is also moved over by 100px in the same direction, it will be moved relative to the parent, or 200px relative to the parent’s parent.

    Since CSS transforms can alter more than just position, the order is significant. In this example the two transforms are scaling to 50% and moving to the right by 50%.

    Nested transforms
    red: scale(0.5,0.5)
    blue: translateX(50%)
    red: translateX(50%)
    blue: scale(0.5,0.5)

    As you can see, the blue square ended up in a different position because the transform order changed. For the example on the left, the red square (and, since it’s a child object, the blue square) is scaled from a width and height of 8em to 4em. The blue square then gets moved over by 50%, or 2em.

    For the example on the right, the red square is moved over by 50% (4em) and then the blue square is scaled to 50%. Since the translation is done before the scaling, the squares are moved by 50% of 8em, rather than 50% of 4em. In addition, since the red square is scaled around its center, its left edge is effectively moved over by another 2em. Adding up the position shifts (red square starts 1em to the right of the left black edge, is moved 50% or 4em, and blue square scaling moves it another 2em) makes the blue square 7em from the left black edge. Since the blue square is 4em wide, it extends past the inside edge of the right black line by 1em.

    The change in size makes sense since the squares were declared with a relative size, but scaling also affects absolute sizes. In this example, the translation is 48px instead of 50%:

    Nested transforms using pixels
    red: scale(0.5,0.5)
    blue: translateX(48px)
    red: translateX(48px)
    blue: scale(0.5,0.5)

    The position of the squares may differ slightly from the first example (depending on your default font size) but you should see that the blue square is still in a different position in the example on the left (translated 24 pixels) than the one on the right (translated 48 pixels). This may seem incorrect at first, but the transforms manipulate an object’s frame of reference; by reducing by 50%, 48px to the object is represented by 24px on the screen.

    Reversing order, preserving effect

    While reversing the transform order changes the position of the blue square, we could change the transforms to make the blue square appear in the same location as before.

    Reversing order, preserving effect
    red:  translateX(25%)
    blue: scale(0.5,0.5)
    red:  scale(0.5,0.5)
    blue: translateX(100%)

    While the red square looks different in each case, the blue square should be in the same location as the first examples above. In each case, the translateX value is different, to take the scaling into effect.

    The left example only moves the square 25%, which is half of the original 50%. This is because the scaling is done second, so we want the translation to be half the original value.

    The right example does the scaling first, so we want the translation to be twice the original value.

    Combined transforms

    Just as transform order is relevant for nested objects, it’s important when doing multiple transforms on one object. In this example, the transforms above are performed on just the blue square.

    Combined transforms
    blue: scale(0.5,0.5)
          translateX(50%)
    blue: translateX(50%)
          scale(0.5,0.5)

    The blue squares are in the same position as the first example, showing that transform order is significant, and has the same effect as applying transforms on nested objects. Note the red square is the same on the left and right because it has no transforms performed on it.

    Combining with rotate

    The above examples show that the frame of reference gets changed when you transform the scale and position. Doing a rotate transform also changes the frame of reference. In this example, we always scale the red square (to make things fit better) and then swap translations on the red and blue squares. This helps to visualize how the transforms interact.

    Combining with rotate
    red:  scale(0.5,0.5)
          rotate(-45deg)
    blue: translate(50%,-50%)
    red:  scale(0.5,0.5)
          translate(50%,-50%)
    blue: rotate(-45deg)

    In the left example, we rotate the red square counterclockwise by 45° then translate the blue square 50% to the right and 50% up. Because the red square’s frame of reference also gets rotated, the result is two diamonds on top of each other. It may help to tilt your head to the left to see that the blue square is indeed translated up and to the right. Another check that the translation is correct is to check that the center of the blue square aligns with the top right corner of the red square.

    The right example has the red box translated; since it was centered, moving it 50% up and 50% to the right makes the lower left corner sit at the middle of the black square. We then rotate the blue square 45°, and as would be expected, the squares have the same center.

    Combining with skew

    While rotating a frame of reference is pretty pretty easy to visualize, it’s a little tricker to do that when skewing objects. In this set of examples, we skew 45° in the Y direction, which means the left edge moves up and the right edge moves down such that the top and bottom are at a 45° slow going down from left to right (opposite of the -45° rotate we did above).

    Combining with skew
    red:  scale(0.5,0.5)
          translate(50%,-50%)
    blue: skewY(45deg)
    red:  scale(0.5,0.5)
          skewY(45deg)
    blue: translate(50%,-50%)

    The result on the left is pretty easy to figure out; the red box is scaled and then translated, then the blue square is skewed about its center.

    The position of the blue square in the example on the right may not seem intuitively correct; we moved it 50% in each direction but it appears to have only moved along the X axis. However, because of the skew, it actually moved 50% in a 45° angle down and to the right, then 50% up. Another way to think of it is the center of the blue square is correctly positioned at the red square’s upper right corner.

    Changing the origin

    All the examples above had the transitions applied relative to the center of the object. You can, however, change the transform’s origin, which sometimes makes for simpler transforms.

    Changing the origin
    red: scale(0.5,0.5)
    blue: rotate(-45deg)
    red: scale(0.5,0.5)
    blue: rotate(-45deg)
    blue origin: left top

    The left example shows the default, center origin. The right example is rotated around the top left corner. To replicate that without changing the origin, the transform would also need to include a translation which would involve irrational numbers, translate(20.710678%,-50%) (the first number represents √2/2 – 0.5) before the rotate, translate(50%,-20.710678%) after. By changing transform-origin, no translation (and the required math) is necessary.

    Posted by fmf @ 5:01pm

  • Leave a Reply