Element animation with respect to its center using just css - html

I have spend hours trying to find a solution that just requires css to work. I am trying to animate a div when it is hoverd, that its height and width be reduced uniformly.
<div class="a"></div>
Above is a simple div element with the following css:
.a {
width: 350px;
height: 350px;
background: #000;
margin: 100px 0 0 400px;
transition: all 2s linear;
position:fixed;
}
I want to reduce the height and width of the element on hover from the center using just css.

Use scale transformation:
.a {
width: 350px;
height: 350px;
background: #000;
margin: 10px;
transition: all 2s linear;
position: fixed;
}
.a:hover {
transform:scale(0.5);
}
<div class="a"></div>

The function you are looking for is scale()
As per your requirement, ideally what you would want to do is to fire the scale() function on hover of the target element as:
.a {
width: 350px;
height: 350px;
background: #000;
margin: 10px;
transition: all 2s linear;
position: fixed;
}
.a:hover {
transform:scale(0.7);
}
This css function will zoom the div element with respect to its center. See this for a complete reference.

Use the :hover pseudoclass.
.a:hover {
width: 300px;
height: 300px;
}
The transition on .a will apply as expected.

Related

Can't quite get image to scale, and use overflow:hidden to work

Here is a link to a demo
I'm not sure what I'm missing, I've done this before a few times but It's been a day of fighting this particular CSS. I want the image to enlarge, but stay within the dimensions, so a zoom effect versus any enlargement. I've attempted to move the overflow:hidden into other parent or children, but it doesn't have an effect. I've played around with the display settings as well.
Any advice? The JSfiddle link is above, and the code below. Thanks for taking a look!
#purple-square {
width: 355px;
height: 255px;
background-image: url("../img/website_cards/purple_card.png");
border-radius: 10px;
}
#migraine-dentistry {
width: 355px;
height: 255px;
background-image: url("../img/website_cards/migraine_dentistry_card.png");
border-radius: 10px;
}
/* need position: relative in shell otherwisee the elements disappear */
#shell {
margin: auto;
width: 355px;
height: 255px;
position: relative;
transform-origin: center;
transition: 0.3s ease-in-out;
}
#shell:hover {
transform: scale(1.2);
}
#container {
width: 100%;
overflow: hidden;
display: inline-block;
transition: 0.3s;
margin: 0 auto;
}
#container div {
position: absolute;
left: 0;
transition: 0.3s ease-in-out;
}
#container:hover {
transition: ease-in-out 0.3s;
}
#container div.bottom:hover {
opacity: 0;
}
and here is the HTML setup:
<body>
<div id="shell">
<div id="container">
<div id='purple-square' class="top"></div>
<div id='migraine-dentistry' class="bottom"></div>
</div>
</div>
</body>
Full working code snipped below my steps
remove unnecessary elements Removed purple square, because it's never seen in wanted animation.
Removed the part the full #container div.bottom:hover part.
Removed every style that begins with #shell in the css and later trigger the animation on #container:hover.
main issue Add an #migraine-dentistry after the #container:hover animation, so if someone hovers the container it effects the #migraine-dentistry element. (#container:hover #mi.. {trans..})
In this (#container:hov..) element remove everything and
insert transform: scale(1.2);
because we just want to scale if user is hovering.
Remove whole #container div {..} style element, because we will directly add these styles to the #migraine-dentistry element.
In #container define px values for
> width: 355px; and height: 255px;
just because we not use the #shell element anymore. Also
> set position: relative; and z-index: 2;
that the #migrain.. element is inside his parent. And
> set border-radius: 15px;
for styling. Finally
>remove the display and transition values
because they are simply not needed.
last In #migraine-de.. styles
>set width: 100%; and height: 100%;
to fit div to parent.
> remove border-radius tag
because it's set by the #container
> add transition: 0.3s ease-in-out;
to transition like you wanted.
#container {
border-radius: 15px;
width: 355px;
height: 255px;
overflow: hidden;
z-index: 2;
position: relative;
}
#container:hover #migraine-dentistry {
transform: scale(1.2);
}
#migraine-dentistry {
width: 100%;
height: 100%;
transition: 0.3s ease-in-out;
background-image: url('https://images.unsplash.com/flagged/photo-1563248101-a975e9a18cc6?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1950&q=80');
}
<body>
<div id="shell">
<div id="container">
<div id='migraine-dentistry' class="bottom"></div>
</div>
</div>
</body>
I know these long nights where you just can't get it done.

Transition left div width over right div and vise versa

Please refer to this jsfiddle: https://jsfiddle.net/b53te5qb/1/
I am attempting to make each of these div widths transition nicely over the other.
Right now it is an instant effect, but I would like for it to transition smoothly. When I attempt the transition it starts to get buggy.
Here is the HTML:
<div class="outer">
<div class="color left"></div>
<div class="color right"></div>
</div>
And here is the CSS so far:
.outer {
position: relative;
height: 100px;
width: 200px;
}
.color {
height: 50px;
width: 50%;
float: left;
transition: width 0.3s linear;
-webkit-transition: width 0.3s linear;
}
.color:hover {
width: 100%;
position: absolute;
}
.left {
background-color: #ff0;
}
.right {
background-color: #0ff;
}
I am open to restructuring this however I would need to in order to complete the task. I just provided this as a base example.
If you're just doing this with solid colors, I would transition transform: scaleX(). Using transition with transform will give you better performance.
.outer {
position: relative;
height: 100px;
width: 200px;
}
.color {
height: 50px;
width: 50%;
float: left;
transition: transform 0.3s linear;
-webkit-transition: transform 0.3s linear;
transform-origin: 0 0;
}
.color:hover {
transform: scaleX(2);
}
.left {
background-color: #ff0;
}
.right {
background-color: #0ff;
transform-origin: 100% 0;
}
<div class="outer">
<div class="color left"></div>
<div class="color right"></div>
</div>
Here you go: https://jsfiddle.net/prowseed/b53te5qb/10/
Two techniques, one with flexbox and one with position absolute, pick any :)
.outer {
position: relative;
height: 100px;
width: 666px;
display:flex;
}
.color {
flex: 1;
height: 100%;
transition: .3s;
}
.color:hover {
flex-basis:100%;
}
.outer2 {
margin-top:100px;
position: relative;
height: 100px;
width: 666px;
}
.outer2:hover .color {
width:0;
}
.outer2 .color {
position:absolute;
top:0;
left:0;
bottom:0;
width:50%;
}
.outer2 .color + .color {
left:auto;
right:0;
}
.outer2 .color:hover {
width:100%;
z-index:2;
}
You'll need to position them absolutely in order to avoid them from moving.
https://jsfiddle.net/b53te5qb/6/
I would highly recommend not transitioning the width, much better would be to transition transform: translateX(), since it will be hardware accelerated and much smoother: https://jsfiddle.net/b53te5qb/8/.
It still needs polishing, but the idea is there. (note the overflow: hidden to avoid showing the excess.) Another improvement would be to have two elements on top (50%/50% width) that trigger the hover via javascript, since when the elements move it's difficult to keep the hover on them, or to remove the hover without leaving the .outer component.
Hope it helps.

Move div with dynamic height out of its parent container

I'm trying to move a div with a dynamically changing height out of it's parent div and back in.
The problem is the dynamically height, otherwise I could easily set the negative height as the bottom value.
For now I just set a large negative number of pixels as the bottom value, but it isn't very nice and does not solve the problem properly. (logically this happens for small numbers: fiddle)
Hopefully the example below clarifies what I try to do.
I was thinking about using transforms instead, but i did not find a solution as well.
Of course I could do this with JavaScript, but as everyone I prefer a pure CSS solution :)
#outer {
position: relative;
width: 400px;
height: 400px;
background: black;
overflow: hidden;
}
#inner {
position: absolute;
bottom: -500px;
/*
It's working but ugly and not perfect.
The value I need would be the height of the inner div, but it is dynamic
*/
width: 100%;
background: red;
transition: 0.4s;
}
#outer:hover #inner {
transition: 0.4s;
bottom: 0;
}
<div id="outer">
<div id="inner">
Some expanding text here
</div>
</div>
You could use CSS transform:translateY(100%) property, so the height is calculated based on the element itself. Then reset the value to 0 on hover.
Inspect the element, you'll be able to see exact the height and position of it.
Also take a look of support tables for transform, and prefix it if necessary.
Updated JsFiddle
.outer {
position: relative;
display: inline-block;
width: 100px;
height: 100px;
background: grey;
overflow: hidden;
}
.inner {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
background: aqua;
transition: 0.4s;
transform: translateY(100%);
}
.outer:hover .inner {
bottom: 0;
transform: translateY(0);
}
<div class="outer">
<div class="inner">Some expanding text here..</div>
</div>
If I understand your issue, you can set a max-height for its normal and :hover state and transition it. However, you must set it to a max-height that you know will always be tall enough (which may lead to random speeds depending on how much content there is).
So something like: JS Fiddle
.outer {
position: relative;
display: inline-block;
width: 400px;
height: 400px;
background: black;
overflow: hidden;
}
.inner {
position: absolute;
bottom: 0px;
width: 100%;
background: red;
transition: 0.4s;
max-height: 0;
overflow: hidden;
}
.outer:hover .inner {
transition: 0.4s;
bottom: 0;
max-height: 40px;
}
Otherwise, I would recommend a JS solution.

Animate an element that moves to occupy empty space with CSS only

http://jsfiddle.net/kscjq0y0/
I want to animate the movement of the yellow div when the red one disappears.
I know it can be done with jQuery animate but I want a CSS3 solution (even if it's not fully supported by all modern browsers).
I've tried the CSS transition property but doesn't seem to work for this kind of movement.
It's there a way to do this?
Make it shrink
div {
height: 100px;
width: 300px;
background-color: red;
margin: 20px;
padding: 10px;
}
#bottom {
background-color: yellow !important;
transition: all 0.5s ease;
}
#top {
transition: all 2s;
}
body:hover #top {
height: 0px;
padding: 0px;
margin-top: 0px;
margin-bottom: 0px;
}
<div id="top"></div>
<div id="bottom"></div>
You can do this, by modifying the CSS attribute that you want to animate. Currently the positioning is based on block layout with the other div, and this is not animating. But if you update the CSS position yourself, then that transition will animate. See the below example.
window.setTimeout(function () {
$("#top").fadeOut("slow");
$("#bottom").css({ top: '0px' });
}, 1000);
div {
height: 100px;
width: 300px;
background-color: red;
margin: 20px;
padding: 10px;
}
#bottom {
position: absolute;
top: 140px;
background-color: yellow !important;
transition: all 0.5s ease;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top"></div>
<div id="bottom"></div>

How to use transform:translateX to move a child element horizontally 100% across the parent

All,
I'd like to be able to use translateX to animate a child element 100% of the way across it's parent (i.e., from the left edge to the right edge).
The challenge is that percentages in translateX refer to the element itself, not the parent.
So, for example, if my html looks like this:
<div id="parent">
<div id="child">
</div>
And my CSS like this (vendor-prefixes omitted):
#parent {
position: relative;
width: 300px;
height: 100px;
background-color: black;
}
#child {
position: absolute;
width: 20px;
height: 100px;
background-color:red;
transform: translateX(100%);
}
This doesn't work - the child only moves 20px (100% of itself), not all the way across the parent. (You can see this on jsfiddle):
I can do this:
#child {
position: absolute;
width: 20px;
height: 100px;
background-color:red;
-webkit-transform: translateX(300px) translateX(-100%);
transform: translateX(300px) translateX(-100%);
}
This works (seen here again on jsfiddle), because it first moves the child 300px (the full width of the parent), minus 20px (the width of the child). However, this depends on the parent having a fixed, known pixel dimension.
However, in my responsive design - I don't know the width of the parent, and it will change.
I know that I can use left:0 and right:0, but the animation performance of left/right is much worse than translateX (Thanks Paul Irish!).
Is there a way to do this?
Thanks in advance.
I didn't post my idea originally, because it involves creating an additional HTML layer, and expected better solutions to come.
Since that hasn't happened, I explain my comment. What I meant was this:
#parent {
position: relative;
width: 300px;
height: 100px;
background-color: black;
}
#wrapper {
position: absolute;
width: 100%;
height: 100px;
border: solid 1px green;
transition: all 1s;
}
#wrapper:hover {
-webkit-transform: translateX(100%);
transform: translateX(100%);
}
#child {
position: absolute;
width: 20px;
height: 100px;
background-color:red;
}
#wrapper:hover #child {
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
}
Since the wrapper is 100% width of the parent, translating it 100% works as expected.
fiddle
Note that the wrapper is being translated 100% as you stated. However, seems that what you really want is to move the element 100% - width. To achieve this, you have to translate the child also 100% (now this applies to the child width) in the opposite direction.
Correction: the child should share the transition property of the wrapper:
#parent {
position: relative;
width: 300px;
height: 100px;
background-color: black;
}
#wrapper {
position: absolute;
width: 100%;
height: 100px;
border: solid 1px green;
transition: all 5s;
}
#wrapper:hover {
transform: translateX(100%);
}
#child {
position: absolute;
width: 50px;
height: 100px;
background-color:red;
transition: inherit;
}
#wrapper:hover #child {
transform: translateX(-100%);
}
<div id="parent">
<div id="wrapper">
<div id="child"></div>
</div>
</div>
There's a pretty cool solution to this problem using Flexbox. The key is to take advantage of the flex-grow property.
Say you have some HTML that looks like this:
<div class="flex-container">
<div class="flex-spacer"></div>
<div class="slider"></div>
</div>
First, give .flex-container the basic display: flex property, and set its flex-direction to row. Set the positioning of the child elements to relative, so they will sit next to each other inside .flex-container.
By default, the flex-grow property is set to 0, which is exactly what we want at the beginning. This means that .flex-spacer and .slider will only have their normal dimensions to begin with. We simply keep .flex-spacer empty, and it will have a width of 0.
Now for the animation. We only need two CSS rules to make it work: add a transition to .flex-spacer and set flex-grow to 1 on .flex-spacer during some event. The second rule gives all of the unused width inside .flex-container to the width of .flex-spacer, and the first rule animates the change in width. The .slider element gets pushed along to the edge of .flex-container.
The CSS looks something like this - I added a background to .flex-spacer to make its presence a little more obvious, and set flex-grow to 1 when the user hovers over .flex-container:
body * {
box-sizing: border-box;
}
.flex-container {
cursor: pointer;
display: flex;
flex-flow: row nowrap;
width: 100%;
border: 2px solid #444;
border-radius: 3px;
}
.flex-spacer,
.slider {
flex-grow: 0;
position: relative;
}
.slider {
padding: 25px;
background-color: #0DD;
}
.flex-spacer {
background-color: #DDD;
transition: all .4s ease-out;
}
.flex-container:hover .flex-spacer {
flex-grow: 1;
}
<div class="flex-container">
<div class="flex-spacer"></div>
<div class="slider"></div>
</div>
Flexbox makes this pretty configurable, too. For example, say we want .slider to move from right to left, instead. All we have to do is switch the flex-direction property in .flex-container to row-reverse, and we're done!
Feel free to play with it in this pen.
Keep in mind that things can get a little trickier if we want animations for different types of events. For example, I came across this issue when trying to animate a label when a user types in an input element. A little more HTML and CSS is needed to make it work (I used some JS, as well), but the concept is the same.
Here's another pen, this time in the context of a form with input.
With the recent addition of Size Container Queries it is now possible to do this by setting the container-type property to inline-size in the parent and then translating the child element by 100cqw - 100% where 100cqw is the full width of the parent and 100% is the width of the child.
#parent {
position: relative;
width: 300px;
height: 100px;
background-color: black;
container-type: inline-size;
}
#child {
position: absolute;
width: 20px;
height: 100px;
background-color:red;
transform: translateX(calc(100cqw - 100%));
}
<div id="parent">
<div id="child">
</div>
I implemented this using wrapper and flex-grow:1.
Here are two animations at the same time with the same duration: 1) the container (green) moves with the car at 100% of the parent's width; 2) the car moves back -100% of its width (to stay on the track at the finish line). The duration can be taken separately and distributed to the container (.track-inner) and the car (.car)
const goBtn = document.querySelector('.go');
const inner = document.querySelector('.track-inner');
const car = document.querySelector('.car');
const durationFromServerMS = '3000ms';
goBtn.addEventListener('click', ()=>{
inner.classList.add('drive');
inner.style.animationDuration = durationFromServerMS;
car.classList.add('backShift');
car.style.animationDuration = durationFromServerMS;
})
const backBtn = document.querySelector('.back');
backBtn.addEventListener('click', ()=>{
inner.classList.remove('drive');
car.classList.remove('backShift');
})
html, body {
padding: 2rem;
}
.track{
width: 50%;
display:flex;
position: relative;
background-color: gray;
width: auto;
margin-bottom: 1rem;
border: 5px dashed blue;
overflow:hidden;
}
.track-inner{
width: 100%;
border: 5px dotted green;
}
.car{
width: 3rem;
height: 1.5rem;
border: 1px solid black;
background-color: salmon;
}
.finish-line{
position:absolute;
top:0;
right: 0.5rem;
width: 3rem;
height: 1.5rem;
border-left: 6px dotted yellow;
}
button{
padding: 0.5rem 1rem;
background-color: lightblue;
outline: none;
border:none;
margin: 0.2rem
}
button:hover{
pointer:cursor;
background-color: salmon;
}
.backShift {
animation-name: car-back;
/* animation-duration: 5s; */
animation-timing-function: ease-in;
animation-fill-mode: forwards;
}
.drive {
animation-name: driving;
/* animation-duration: 5s; */
animation-timing-function: ease-in;
animation-fill-mode: forwards;
}
#keyframes driving {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(100%);
}
}
#keyframes car-back {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(-100%);
}
}
p{
padding:0;
}
<div class="track">
<div class="track-inner">
<div class="car ">car</div>
</div>
<div class="finish-line">finish</div>
</div>
<div>
<button class='go'>Go</button>
<button class='back'>Back</button>
</div>