Eugene Krevenets
Eugene Krevenets

Reputation: 1801

How to mix css3 transform properties without overriding (in realtime)?

Is there any way to combine (mix) css transform properties without overriding? For example there I want to rotate and scale. http://jsfiddle.net/hyzhak/bmyN3/

html

<div class="item rotate-90 tiny-size">
    Hello World!
</div>

css

.rotate-90 {
    -webkit-transform: rotate(90deg);
}
.tiny-size {
    -webkit-transform: scale(0.25, 0.25);
}

PS

I just have a lot of elements and a lot of simple classes to transform view of the elements. And I just want to tune view of them by adding and removing some classes. Combining of all classes doesn't work because it will be hundreds of combinations.

As well I want to do it in realtime.

The number of transformations can be approximately 5 and each of them can hold about 10 states - so just describe all combinations of them with hands give you approximately

10*10*10*10*10 = 100000 cases

It is a bad solution.

Upvotes: 27

Views: 17692

Answers (6)

Taylor Plante
Taylor Plante

Reputation: 81

One thing you could do is get the computed transform style in JS and concatenate your secondary transform value:

HTML

<div class="rotate-scale"></div>

CSS

.rotate-scale {
   transform: rotate(90deg);
}

JS

var el = document.querySelector('.rotate-scale'),
    // rotation will get the transform value declared in css
    rotation = window.getComputedStyle(el).getPropertyValue('transform');

el.style.transform = rotation + 'scale(0.25, 0.25)';

Upvotes: 1

Hugo Scott-Slade
Hugo Scott-Slade

Reputation: 897

The way I get around this issue is to add a wrapper class around the one I want. For example, I have the wrapper which has its position set via javascript, and then the internal which is is affected by css animations - both on the transform property.

I know adding DOM nodes isn't ideal but it circumvents the script-heavy solution and lets the browser optimise the animations and transitions as it would normally.

Upvotes: 8

Zach Saucier
Zach Saucier

Reputation: 25954

Currently there is no way to do this, it only accepts one command for transform at a time and cannot add or subtract like you'd like. Thus you'll have to add or remove a class depending on what the first and second type is and write a custom transform for that element

Using a CSS preprocessor language like SASS or LESS could make combining transitions easier, the following allows for any number of transitions to be combined into one, but requires being told which ones to add (this is SCSS)

.item {
  width:500px;
  height:500px;
  font-size:150px;
  background:blue;
}

$firstTransform: rotate(90deg);
$secondTransform: scale(0.25, 0.25);

@mixin transform($transform...) {
  -webkit-transform: $transform;
     -moz-transform: $transform;
      -ms-transform: $transform;
       -o-transform: $transform;
          transform: $transform;
}
.rotate-90 {
  @include transform($firstTransform);
}
.tiny-size {
  @include transform($secondTransform);
}
.both {
  @include  transform($firstTransform $secondTransform);
}

Demo

Really the only way to do it currently would be to use javascript, essentially adding a class with a transform, saving a variable of that class' transforms, removing the class and doing the same with another class, then combine the two matrices of the class' transforms. EDIT: See brejep's javascript answer for more on this approach

Upvotes: 4

BrettJephson
BrettJephson

Reputation: 416

Here's an attempt at solving the problem with Javascript: http://jsfiddle.net/eGDP7/

I'm using a data attribute on the html to list the classes with transforms that we want to apply:

<div class="compoundtransform" data-transformations="scaletrans2, rotatetrans1">Test subject</div>

I iterate through the classes and find out what css rules they represent:

function getCSSRulesForClass( selector ) {
    var sheets = document.styleSheets;
    var cssRules = [];
    for (var i = 0; i<sheets.length; ++i) {
        var sheet = sheets[i];
        if( !sheet.cssRules ) { continue; }
        for (var j = 0; j < sheet.cssRules.length; ++j ) {
            var rule = sheet.cssRules[j];
            if (rule.selectorText && rule.selectorText.split(',').indexOf(selector) !== -1) {
                var styles = rule.style;
                for( var k = 0; k<styles.length; ++k ) {
                    var style = styles[k];
                    cssRules.push( { 
                        property: style,
                        value: styles[style]
                    } );
                }
            }
        }
    }
    return cssRules;
}

I find any transforms and apply the transforms to a matrix:

function convertTransformToMatrix( source, transformValue ) {
    var values = transformValue.split(") "); // split into array if multiple values
    var matrix = cloneObject( source );
    for ( var i = 0; i<values.length; ++i ) {
        var value = values[i];
        if( isRotate( value ) ) {
            var rotationValue = cssRotationToRadians( value );
            var cosValue = Math.cos( rotationValue );
            var sinValue = Math.sin( rotationValue );
            var a = matrix.a;
            var b = matrix.b;
            var c = matrix.c;
            var d = matrix.d;
            var tx = matrix.tx;
            var ty = matrix.ty;
            matrix.a = a*cosValue - b*sinValue;
            matrix.b = a*sinValue + b*cosValue;
            matrix.c = c*cosValue - d*sinValue;
            matrix.d = c*sinValue + d*cosValue;
            matrix.tx = tx*cosValue - ty*sinValue;
            matrix.ty = tx*sinValue + ty*cosValue;
        } else if ( isScale( value ) ) {
            var scale = cssScaleToObject( value );
            matrix.a *= scale.x;
            matrix.b *= scale.y;
            matrix.c *= scale.x;
            matrix.d *= scale.y;
            matrix.tx *= scale.x;
            matrix.ty *= scale.y;
        } else if ( isTranslate( value ) ) {
            var translate = cssTranslateToObject( value );
            matrix.tx += translate.x;
            matrix.ty += translate.y;
        }
    }
    return matrix;
}

And I, finally, apply that matrix to the node as a transform.

Currently:

  • The code is a bit messy
  • The CSS parsing is limited to scale(x,y), translate(x,y), and rotate with degree or radian values
  • And it would only work with the webkit vendor prefix

I might tidy it up and turn it into a utility if useful to anyone.

Upvotes: 8

BrettJephson
BrettJephson

Reputation: 416

Instead of rotate and scale you could use the matrix function.

transform: matrix(a, b, c, d, tx, ty);

In the example given, I believe the following would produce the desired result:

transform: matrix(0,-0.25,0.25,0,0,0);

For an explanation of matrix calculations:

And some useful tools exist for calculating these matrices as CSS, such as:

Upvotes: 2

m59
m59

Reputation: 43755

Hmm...as far as I know, you would have to create new class and combine them this way (separated by a space):

-webkit-transform: rotate(90deg) scale(0.25, 0.25);

Upvotes: 10

Related Questions