Transform-origin issue - css

I'm trying to rotate object using: transform: rotate(90deg); and applying transform-origin: right bottom;. But there I get different transformation behavior depending on where I place transform-origin: either to parent element or to :hover state.
Why does those differences happens?
div {
width: 100px;
height: 100px;
transition: all 1s ease-in;
}
.main:hover {
transform: rotate(90deg);
transform-origin: right bottom;
}
.main2 {
transform-origin: right bottom;
}
.main2:hover {
transform: rotate(90deg);
}
/*********BG colors*******/
.main, .main2 {
background: green;
}
.wrapper {
background: red;
float: left;
margin-right: 20px;
}
<div class="wrapper">
<div class="main">Transform-origin on :hover state</div>
</div>
<div class="wrapper transform">
<div class="main2">Transform-origin on parent element</div>
</div>

With .main2, you're initializing transform-origin to right bottom, but with .main, you're setting the origin on :hover. Because of this, CSS is trying to tween between the default origin (the center of the element) and right bottom - creating this odd effect.

Related

CSS: Changing width/height after rotation in one direction only

My end goal is a draggable, resizable, Scalable, and rotatable element, just like the example on: https://daybrush.com/moveable/ only by using css width,height, and transform: rotate, translate.
Say I have a div with following css:
.rect {
background-color: red;
width: 200px;
height: 100px;
top:100px;
left:100px;
position: absolute;
transform: rotate(0deg);
}
<div class="rect"></div>
If I want to resize the div horizontally to the left, I just change the width by x pixels. If I want to change it to the right I just change the width by x pixels, and translate(-xpx, 0).
But what if I change the angle? From trying a lot of stuff, I found some of the x and y values for translate to the respective angle, however I feel like there is a more straight forward way than just guessing. E.g: For 90deg, if I want to resize to the left by x px I do translate(-x0.5px, x0.5px).
More: what if I want to change both the width & height at the same time?
P.S.: I would rather avoid using libraries, transform: scale or svg
P.P.S:Example to further demonstrate the problem, just changing the width:
.rect {
background-color: red;
width: 200px;
height: 100px;
top:100px;
left:100px;
position: absolute;
transform: rotate(45deg);
animation: expand 5s infinite
}
#keyframes expand {
from {width: 200px;}
to {width: 2000px;}
}
<div class="rect"></div>
Fixed, stretching the left side of the original rectagle (now up since rotated 90deg):
.rect {
background-color: red;
width: 200px;
height: 100px;
top:100px;
left:100px;
position: absolute;
transform: rotate(90deg);
animation: expand 3s infinite
}
#keyframes expand {
from {
width: 200px;
}
to {
width: 800px;
transform: rotate(90deg) translate(-300px, 300px);
}
}
<div class="rect"></div>
You can apply several transformations to the same object and they will be composed in the order that you specify. Move then rotate, is different than rotate then move.
.rect {
background-color: red;
width: 200px;
height: 100px;
top:100px;
left:100px;
position: absolute;
}
.t1 {
background-color: #40d04080;
/* green shaded rectangle: rotate after translation */
transform: translate(2cm, 0) rotate(30deg);
}
.t2 {
background-color: #f0404080;
transform: rotate(30deg);
}
.t3 {
background-color: #4040f080;
/* blue shaded rectangle: translate after rotation */
transform: rotate(30deg) translate(2cm, 0) ;
}
<div class="rect t1"></div>
<div class="rect t2"></div>
<div class="rect t3"></div>

CSS animations and z-index

I'm working on a JavaScript game similar to "2048." It has a tabular display of "tiles" made up of several layers of divs. I'd like to make the selected tile "bounce" using a CSS animation. (The bouncing needs to be able to happen in each of the four cardinal directions: bounce up, down, left, or right, depending on the state of the game.)
My problem is that when I attach an animation, I see the content correctly bounce "in front of" tiles up and left of the selected one, but it incorrectly bounces "behind" the tiles to its right and below it. Here's a JSFiddle illustrating the problem:
https://jsfiddle.net/atqwc8er/2/
I'd like the bouncing blue tile "2" to show up in front of tile "3", the same way it already shows up in front of tile "1". Can anyone help me make it do that?
.tile {
position: absolute;
width: 50px;
height: 50px;
background-color: red;
}
.tile-inner {
text-align: center;
z-index: 10;
}
.tile-1-0 {
transform: translate(0px, 0px);
}
.tile-1-1 {
transform: translate(55px, 0px);
}
.tile-1-2 {
transform: translate(110px, 0px);
}
.selected .tile-inner {
background-color: blue;
animation: back-and-forth 0.5s infinite alternate;
z-index: 99; /* not working */
}
#keyframes back-and-forth {
from {
transform: translateX(-25px);
}
to {
transform: translateX(25px);
}
}
<div class="tile-container">
<div class="tile tile-1-0">
<div class="tile-inner">1</div>
</div>
<div class="tile tile-1-1 selected">
<div class="tile-inner">2</div>
</div>
<div class="tile tile-1-2">
<div class="tile-inner">3</div>
</div>
</div>
increase the z-index of .selected instead
.tile {
position: absolute;
width: 50px;
height: 50px;
background-color: red;
}
.tile-inner {
text-align: center;
z-index: 10;
}
.tile-1-0 {
transform: translate(0px, 0px);
}
.tile-1-1 {
transform: translate(55px, 0px);
}
.tile-1-2 {
transform: translate(110px, 0px);
}
.selected .tile-inner {
background-color: blue;
animation: back-and-forth 0.5s infinite alternate;
}
.selected {
z-index: 99;
}
#keyframes back-and-forth {
from {
transform: translateX(-25px);
}
to {
transform: translateX(25px);
}
}
<div class="tile-container">
<div class="tile tile-1-0">
<div class="tile-inner">1</div>
</div>
<div class="tile tile-1-1 selected">
<div class="tile-inner">2</div>
</div>
<div class="tile tile-1-2">
<div class="tile-inner">3</div>
</div>
</div>
Related question to get more details and understand why it cannot work with .title-inner: Why can't an element with a z-index value cover its child?
The z-index attribute should be added to the element at the same level, so pls add the z-index attribute to .title-1-1 instead of .title-inner

CSS Animation - drawing line from left to right on mouseenter, then disappearing left to right on mouseleave

I'm trying to animate a line that underlines from left to right on 'mouseenter' and then to disappear from left to right on 'mouseleave' instead of the current behaviour where it disappears right to left.
Example of what I'm trying to achieve (but with animations not transitions):
https://jsfiddle.net/1gyksyoa/
I have tried to reverse the 'draw' animation but this doesn't achieve what I'm trying to accomplish.
#keyframes draw-reverse {
100% {
width: 0;
background-color: red;
}
0% {
width: 47px;
background-color: red;
}
}
I have put together this to give a better understanding of the problem;
https://jsfiddle.net/Lq560be9/
Currently, I have the line animating from left to right as desired on 'mouseenter', but on 'mouseleave' it disappears from right to left, whereas I am trying to get the line to also disappear from left to right.
But the problem isn't animation's ability it's the properties that you're animating. Instead of animating the width of an object you should animate its "X" position using translate. (this is much more performant too)
Simply put you need to MOVE the bar from left to center to right instead of trying to scale it.
(there's lots of code here to show the different states the only one you really need to follow is .ex4)
document.querySelector('#animate').addEventListener('mouseenter', function(){
this.classList.toggle('over');
})
document.querySelector('#animate').addEventListener('mouseleave',function(){
this.classList.toggle('out');
})
.example {
margin: 30px auto;
padding: 10px;
background: #dadada;
max-width: 50%;
position: relative;
}
.example:after {
content:'';
position: absolute;
width: 50%;
height: 5px;
background-color: #333;
left:0;
bottom:0;
}
.ex1:after {
transform: translateX(-100%);
}
.ex3:after {
transform: translateX(200%);
}
.ex4 {
overflow: hidden;
}
.ex4:after {
transform: translateX(-100%);
}
.ex4.over:after {
animation: animate-in 1s ease-in-out 1 normal forwards;
}
.ex4.out:after {
animation: animate-out 1s ease-in-out 1 normal forwards;
}
#keyframes animate-in {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0);
}
}
#keyframes animate-out {
0% {
transform: translateX(0);
}
100% {
transform: translateX(200%);
}
}
<div class="example ex1">Object State 1</div>
<div class="example ex2">Object State 2</div>
<div class="example ex3">Object State 3</div>
<div id="animate" class="example ex4">Full example (hover)</div>
As a follow on from above, an alternative solution without using the translate property.
The new animation for mouseleave is;
#keyframes draw-reverse {
0% {
width: 47px;
}
25% {
width: calc(100% - 16px);
}
26% {
width: auto;
right: 8px;
left: 8px;
}
100% {
width: auto;
right: 8px;
left: calc(100% - 8px);
}
}
Full solution can be seen here - https://jsfiddle.net/1wq25tg7/

How can I place image just left outside visible part of page before animation start?

I want to place my image left outside of page how to do it?
+-------------------+
| visible page part |
image -> | |
+-------------------+
Finally I want to move image inside page.
+-------------------+
| visible page part |
| image inside page |
+-------------------+
You can use either negative margin or translateX transform to achieve this. The below snippet has an example for both approaches.
One thing to note is that the two methods work a bit differently even though their end output is similar. While translateX(-100%) moves element to the left (on the X-axis) by as many pixels as the width of the image, margin-left: -100% moves the image by as many pixels as the width of the container of the image. So, if the emphasis is on just left outside the visible part then using translateX(-100%) is more suitable.
/* using negative margins */
.margin {
margin-left: -100%;
animation: marginmove 1s 3s forwards;
}
#keyframes marginmove {
from {
margin-left: -100%;
}
to {
margin-left: 0%;
}
}
/* using translate transforms */
.translate {
transform: translateX(-100%);
animation: translatemove 2s 3s forwards;
}
#keyframes translatemove {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0%);
}
}
/* Just for demo */
body {
max-width: 300px;
margin: 0 auto;
padding: 0;
border: 1px solid;
}
html,
body {
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div>Test content</div>
<img src="http://lorempixel.com/100/100/nature/1" class="margin" />
<div>Test content</div>
<img src="http://lorempixel.com/100/100/nature/2" class="translate" />
Note: As mentioned in comments, if there is a chance that the page's width can become lesser than the viewport's width then it would be imperative to add overflow: hidden to the root/parent element (as applicable) to prevent the image from showing up outside the page's left border.
You can adapt the above answer to work even when the image is part of a centered column which has equal margins on either sides. Below is a sample snippet to help you:
/* using negative margins */
.margin {
margin-left: -100%;
margin-right: 0%;
animation: marginmove 1s 3s forwards;
}
#keyframes marginmove {
from {
margin-left: -100%;
}
to {
margin-left: 0%;
}
}
/* using translate transforms */
.translate {
transform: translateX(-100%);
animation: translatemove 1s 3s forwards;
}
#keyframes translatemove {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0%);
}
}
/* Just for demo */
.container {
width: 300px;
margin: 0 auto;
border: 1px solid;
overflow: hidden;
}
.container > div{
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='container'>
<div>Centered column</div>
<img src="http://lorempixel.com/300/100/nature/1" class="margin" />
</div>
<div class='container'>
<div>Centered column</div>
<img src="http://lorempixel.com/300/100/nature/2" class="translate" />
</div>

Position AFTER transform in CSS?

Consider the following attempt to rotate a paragraph 90 degrees and position it so that the corner that was initially its top-left corner (and which therefore becomes its top-right corner after the rotation) ends up located at the top-right corner of the parent block.
HTML:
<!DOCTYPE html>
<html>
<body>
<div id="outer">
<p id="text">Foo bar</p>
</div>
</body>
</html>
CSS:
#outer {
border: solid 1px red;
width:600px;
height: 600px;
position: relative;
}
#text {
transform: rotate(90deg);
position: absolute;
top: 0;
right: 0;
}
In Firefox 19.0.2 on OS X 10.6.8, it fails. This appears to be because, despite the order in which the CSS properties were given, the transformation is applied after the positioning. In other words, the browser:
places #text such that its top-right corner is located at the top-right corner of the parent block, but only then
rotates it, with the result that what is now its top-right corner is not located at the top-right corner of the parent block.
As a result, the transform-origin property isn't much use here. If, for instance, one used transform-origin: top right; then #text would need to be moved downwards by the width it had before it was rotated.
My question: is there a way to tell the browser to apply the CSS positioning properties after the rotation; and if not, then is there instead a way to move #text downwards (e.g. using top:) by the width it had before it was rotated?
NB. Ideally the solution should not require setting a fixed width: for #text, and must not require JavaScript.
You can apply more than one transform to an element, and the order does matter. This is the simplest solution: http://jsfiddle.net/aNscn/41/
#outer {
border: solid 1px red;
width:600px;
height: 600px;
position: relative;
}
#text {
background: lightBlue;
position: absolute;
top: 0;
left: 0;
right: 0;
transform: translate(100%) rotate(90deg);
transform-origin: left top;
-webkit-transform: translate(100%) rotate(90deg);
-webkit-transform-origin: left top;
}
The transform origin is the point around which a transformation is applied. For example, the transform origin of the rotate() function is the center of rotation - https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin
Rotating -90deg.
.rotate {
position:absolute;
-webkit-transform-origin: left top;
/* Safari */
-webkit-transform: rotate(-90deg) translateX(-100%);
/* Firefox */
-moz-transform: rotate(-90deg) translateX(-100%);
/* IE */
-ms-transform: rotate(-90deg) translateX(-100%);
/* Opera */
-o-transform: rotate(-90deg) translateX(-100%);
}
Solved: here
This is the code I've added:
left: 100%;
width: 100%;
-webkit-transform-origin: left top;
I've also added some prefixed transform properties so it will be cross browser
-webkit-transform:rotate(90deg);
-moz-transform:rotate(90deg);
-ms-transform:rotate(90deg);
-o-transform:rotate(90deg);
transform:rotate(90deg);
How I did it:
I've found this question and, as the name of the website says, "fiddled" with the code to obtain this behavior. I guess the solution is left: 100%; instead of right: 0;.
(the width: 100%; is there because for some reason it wasn't 100% and the text would overflow to the next line)
You may want to try using CSS3 #keyframes animation. It will allow you to rotate and reposition in any order you like. Here is a tutorial that may help: [CSS-Tricks][1]
.container {
position: relative;
width: 200px;
height: 200px;
border: 1px solid red;
}
p {
border: 1px solid blue;
position: absolute;
top: auto;
right: 0;
display: inline-block;
margin: 0;
animation: 1s rotate 1s both;
}
#keyframes rotate {
0% {
transform-origin: top left;
transform: rotate(0deg);
right:0;
}
50% {
right:0;
}
100% {
transform-origin: top left;
transform: rotate(90deg);
right: -64px;
}
}
<div class="container">
<p>some text</p>
</div>
You might want to play around with the translate option which you can apply as the second transform function after rotate and place your element at the exact position that you want to.
There is no other way I guess to tell the browser to use the position properties after the transform function is used using plain css.
See this demo - http://codepen.io/anon/pen/klImq
Place "!important" at the end of the transform line.

Resources