Rotate more than 360 deg using css 3 matrix - css

I am using CSS transitions like this:
div
{
-webkit-transform: rotate(0deg);
-webkit-transition: -webkit-transform 2s;
}
div:hover
{
-webkit-transform: rotate(720deg);
}
This effectively makes the div rotate 2 times for 2 seconds. Now I want to use the matrix so that I can rotate and scale the image; however the matrix does not use degrees but cos(a) and sin(a) and as we all know cos(0) = cos(360) = cos(720) etc. So using the matrix I am unable to rotate the image more than 359deg.
So I decided to be clever and with JavaScript I took the matrix from a rotated element (720deg) and it looks like this:
-webkit-transform: matrix(1, -0.0000000000000004898587196589413, 0.0000000000000004898587196589413, 1, 0, 0);
However using this matrix I am not able to rotate the element - I will later calculate the size and apply that too.
So the question is - how do I rotate an element more than 359deg using css 3 matrix transform?

Matrices in CSS 3 define
a mathematical mapping from one coordinate system into another
(W3C reference),
which implies that 720deg is exactly equivalent to 360deg as you pointed out. So you will not be able to do this directly with a matrix.
However, this syntax should work for your need :
div:hover {
transform: scale(1.5,1.5) rotate(720deg);
transition: transform 3s;
}

Related

How to show fixed elements inside a web-animation-api transformed parent

I have a parent div transformed with the web-animations-api.
This makes the containing block, of any child fixed elements the parent rather than the viewport (as expected).
The parent transforms to translate3d(0, 0, 0) ... so just removing the animation/transform on complete would be perfect.
I cant find a simple* way to do this through the web-animations-api, is there one?
Previously this was done via CSS, or inlined styles & hence easy to remove on complete.
I have tried ..
amination.cancel(), inside animation.finished, but rapidly applying another animation to the
same el, breaks ... looks like fill mode flips from 'forward' to 'none'
adding an additional transform on animation.finished. This seems to work but messy.
el.animate([
{ transform: 'unset' },
{ transform: 'unset' },],
{
duration: 0,
fill: 'forwards'
}
)

Translate CSS transition-timing-function to swiping

I am fading in a div (using CSS transitions) with a custom timing function (http://cubic-bezier.com/#1,0,1,1). The timing function is basically a more extreme version of 'ease-in'.
div {
opacity: 0;
transition: opacity 1s;
.in {
opacity: 1;
transition-timing-function: cubic-bezier(1, 0, 1, 1);
}
In addition to that, I want to be able to fade in the div by swiping across the screen. I am using the following Jquery to set the opacity according to how far the user has swiped:
documentWidth = $(document).width();
$(document).on('touchmove', function(e) {
// How much of the animation is completed (in %)
completion = e.changedTouches[0].pageX / documentWidth;
$('div').css('opacity', completion);
})
Nooooow, this is linear! Is there a clever Math-person out there who can figure out how to re-state that last line to represent my timing function?
So, for example, if completion is at 25%, the opacity should be around 2%. At 50%, it should be around 11% and at 75% it should be around 31%.
Start by finding a curve that approximates your cubic-bezier curve. With the given points and some online tools it's possible to draw a curve with this equation:
y = 464085.7 + (0.0174619 - 464085.7)/(1 + (x/22.88957)^4.174069)
in your case the x represents your completion variable and y the resulting opacity.
Then your code becomes
let completion = e.changedTouches[0].pageX / documentWidth;
let exponential = Math.pow((completion / 22.88957), 4.174069);
let opacity = 464085.7 + (0.0174619 - 464085.7)/(1 + exponential);
$('div').css('opacity', opacity);
(of course you may find a better equation that best fits your needs)

Resetting RotateY degrees without transformation

I use css 3D-transform rotateY to flip a div with css transition. I want the image to flip for a certain number of times : when the transition ends, I trigger it again until a certain counter value is reached.
What I would like to do : when the rotateY reaches 360 deg, I want reset it to 0 to restart the same rotation.
What happens: if I reset to 0, the div rotates backward first. I tried to disable the transform property before "rewinding', without any luck.
Is there an easy way to restore the 0 deg value without any transformation / rotation?
I did a codepen to illustrate: http://codepen.io/3MO/pen/QKogxE
It is not very graphical, but if you click on start, then reset, it will be obvious.
Here is my reset function:
$('#reset').click(function () {
deg = 0;
countRotations = 0;
$('#card').attr('transition', 'transform 0');
flipStarted = false;
flip($('#card'), 0);
});
You need to set the transition property to 0s, then change the rotation to 0 (this way it'll rotate instantly), then change the transition back to the "default" value.
Thanks to #Harry here's a working demo:
http://codepen.io/hari_shanx/pen/PGLKzN

Breaking up transformations

I have 48 elements and they all have the same animation which is a transformation in the Y axis but they each have a different rotation
transform:rotate(0deg) translateY(-105px);
...
#keyframes mov {
0%,65%,100% {transform:rotate(0deg) translateY(-105px);}
15%, 50% {transform:rotate(0deg) translateY(105px);}
}
and it would be horribly inefficient if I were to have that for each element - is there a way I can have transform:rotate(0deg) in one place and translateY(?px); in another place?
Current solution: element in an element, first is rotate and the second is translateY

Unskewed child not aligning

Note: This question is about a problem with output, and not about creation of any shape.
I recently created a shape :
.prog {
position: relative;
top: 20px;
width: 150px;
height: 120px;
background: green;
display: inline-block;
transform: skewY(20deg);
transform-origin: 0% 100%;
transition: 0.8s ease;
}
.prog:before {
content: "";
position: absolute;
left: 150px;
width: 150px;
height: 120px;
background: red;
transform: skewY(-40deg);
transform-origin: 0% 100%;
transition: 0.8s ease;
}
<div class="prog "></div>
In the above snippet, the green shape is the .prog element, and is skewed. The red shape is :before pseudoelement of the first element.
I skewed .prog (skewY) to 20deg. Now, I needed :before to be -20deg. For this, I first had to unskew it. Then skew it further 20deg.
So final skewY value will be -40deg. I applied this, and appropriate transform-origins.
But the problem is that the top points of both the shapes aren't aligning. They should, but they aren't. Here's an image showing the problem :
The black lines are just for reference.
Now even more!
I skewed -20 -20 instead of -40 :
transform: skewY(-20deg) skewY(-20deg); <-- This works!
transform: skewY(-40deg); <---------------- This doesn't!
The behaviour of the "unskewed" child is normal, it is the way skew works. In order to understand this, I am going to simplify the question to :
why isn't skewX(40deg) the same as skewX(20deg) skewX(20deg)?
div {
width: 100px; height: 100px;
position:absolute;
top:20px; left:20px;
transform-origin: 0 0;
}
.d1 {
transform: skewX(40deg);
background: red;
opacity:0.7;
}
.d2 {
transform: skewX(20deg) skewX(20deg);
background: blue;
opacity:0.7;
}
/** FOR THE DEMO **/
body {background: url('http://i.stack.imgur.com/GySvQ.png');background-size: 10px;}
.m {text-align:right;padding-top:105px;}
.m1{width:83px;color:red;border-right:1px solid red;}
.m2 {width:72px;color:blue;border-right:1px solid blue;}
p{margin:0 0 5px 150px;color:red;}
.b{color:blue;}
<div class="d1"></div>
<div class="d2"></div>
<div class="m m1">x = 83</div>
<div class="m m2"><br/>x = 72</div>
<p class="r">skewX(40deg)</p>
<p class="b">skewX(20deg) skewX(20deg)</p>
Note: for the sake of explanation I will be using a 100*100 square div and the transform origin is set on the top left corner of this div. Like in the above code snippet.
To understand the difference between the two transformations, we need to explore the way the CSS skew() function works. The specs say :
A 2D skew transformation along the X axis with the parameter alpha is
equivalent to the matrix:
So this means we can calculate the coordinates of each point of a 2D X skewed element like this :
| 1 tan(α) | . | x |
| 0 1 | | y |
α is the X skewed angle
x/y the coordinates of the point before transformation
For skewX(40deg)
α = tan(40deg) ~= 0.83
| 1 0.83 | . | 0 | | 83 |
| 0 1 | | 100 | = | 100 |
The new coordinates are x = 83 as seen in the code snippet example.
For skewX(20deg) skewX(20deg)
α = tan(20deg) ~= 0.36
first skew :
| 1 0.36 | . | 0 | | 36 |
| 0 1 | | 100 | = | 100 |
Second skew :
| 1 0.36 | . | 36 | | 72 |
| 0 1 | | 100 | = | 100 |
The new coordinates are x = 72 as seen in the code snippet.
Conclusion
Both transformations don't give the same result. So skewY(20deg) skewY(-40deg) isn't the same transformation as skewY(-20deg) and the two top corners of the red and green elements can't align as :
tan(20deg) != tan(40deg)/2
References :
The CSS3 matrix() Transform for the Mathematically Challenged
CSS transform skew: math unveiled
CSS Transforms Module Level 1
skew introduces a vertical offset equal to the tangent of the angle. So, skew(20deg) introduces an offset of tan(20deg).
For your example to work, it should be that
tan(-20deg) = tan(20deg) + tan( -2 * 20deg)
or
tan (2 * x) = 2 * tan (x)
but this is not true, tangent and sum are not asociative
the required skew to reverse it is
result = - atan ( 2 * tan (x))
that, for x = 20, gives a result of
36,052388732387908475278040193987
(aproximately)
By way of a revision of my initial answer which produced an output rather than an explanation, I would postulate* that the effect is seen because you are surmising a negative skew can be used to offset a position on the positive skew curve, when in fact- at minus values you are operating on the negative skew curve.
This would first require that the measure of skew was singular and occurring on the same curve (see normal curve below), with positive and negative values allowing to shift along the curve.
However, the curve for negative and positive skews are inversly tailed.
Zero skew is the only value which operates the same on both. As such, if you have an element, apply a skew of 20 degrees to it, then apply a skew of minus 20 you will actually have a skew (positive or negative) of zero, so using a negative offset appears to work..
However, if you then apply additional negative skew, you will have a negatively skewed element, the curve for which is different and not equal to the inverse equivalent position on the positive skew curve.
20deg = Original element, 20deg on positive skew curve
20deg - 20deg = 0, same for positive and negative skew curve
-40deg = taking the elements current 20deg skew, minus 40 deg = 20deg on negative skew curve - NOT an equivalent 'opposite' point on the positive skew curve
When using psuedos, the skew works because you arent offsetting a positively skewed value by a newgatively skewed amount.
* Im no mathematician, so afraid I can only claim this as conjecture
I will just address to the main issue, as to why it happens, instead of trying to provide alternatives
Since your math appears to be exact, we have to search for what is not exact, which is the platform itself...
MDN's article on transform states that it is not a stable technology:
This is an experimental technology
Because this technology's specification has not stabilized, check the
(emphasis mine)
Consider the following:
When you skew the main div, it requires a huge amount of browser render calculations to show in a 2D environment what it would look like considering both 3 axis... The pseudo element suffer with those calculations, as you can see if you further it a little left from the edge where you've put it:
See that I just moved to the left, and that caused it to go down a little, according to the Y skewing applied to the main div. Now, add to that another huge amount of calculations when you re-skew the pseudo element... The browser will just fail to provide an accurate render of what the 3D space would look like in a 2D environment...

Resources