I have a parent div wrapped around a scaled child div. The child div starts off with transform:scale(0,0); & expands to transform:scale(1,1); when a button is clicked.
.content-wrapper {
display: inline-block;
background-color: #ddf;
padding: 10px;
clear: both;
}
.content {
padding: 10px;
background-color: #fff;
display: flex-block;
overflow: hidden;
transform:scale(0,0);
transform-origin:top;
transition:transform 1s ease-out;
}
.content.open {
transform:scale(1,1);
}
However the parent div content-wrapper stays at the same size of the child div content - even when the child is "closed".
The desired behaviour is when the child div is closed the parent div shrinks to only wrap around the button.
JSFiddle of Example
Is it possible to wrap the parent div around the child div when it's "closed" in this example?
This will be a little challenging because the background color is attached to the content container. I would remove the background color from the main container, then make it a separate div positioned absolute
<div class="content">
...
<div class="content-bg"> //contains your background color
then manipulate that based on your click handler.
I've updated the JSFiddle http://jsfiddle.net/ztxa5kwu/90/
CSS for the new div:
.content-bg{
position: absolute;
background-color: #ddf;
left: 0;
right: 0;
bottom: 0;
top: 0;
z-index: -1;
transition: all .5s ease;
transform-origin: bottom right;
}
Notice the transform-origin: bottom right; to scale the background towards your button. In the JSFiddle, I made the button take on a border the same color as the background, but you could easily edit the size of the new <div class="content-bg"></div> to fit around your button.
Hope that helps, and gets you in the right direction.
Try this:
.content {
background-color: #fff;
overflow: hidden;
transform:scale(0,0);
transform-origin:top;
transition:transform 1s ease-out;
display: block;
padding: 0;
height: 0; width: 0;
}
.content.open {
padding: 10px;
height: auto; width: auto;
transform: scale(1,1);
}
Edit: Play with this:
.content {
padding: 0;
background-color: #fff;
display: block;
overflow: hidden;
transform-origin:top;
transition: transform 1s ease-out, max-width 0.5s ease-out 0.4s, max-height 1s ease-out;
transform: scale(0,0); max-width: 0; max-height: 0;
}
.content.open {
padding: 10px;
transition: transform 1s ease-out, max-width 1s ease-out, max-height 8s ease-out;
transform: scale(1, 1); max-width: 1920px; max-height: 1080px;
}
I found this comment on an older question:
This method only partially achieves the desired effect but doesn't
actually remove the space. The transformed box acts like a
relatively positioned element - the space is taken up no matter how it
is scaled. Check out this jsFiddle which takes your first one and
just adds some bogus text at the bottom. Note how the text below it
doesn't move up when the box height is scaled to zero. – animuson♦ Jul
29 '13 at 20:37
So with that in mind I used the max-height/ max-width hack to get something close to what I was after: http://jsfiddle.net/BaronGrivet/ztxa5kwu/176/
.content {
padding: 10px;
background-color: #fff;
display: flex-block;
overflow: hidden;
transform:scale(0,0);
transform-origin:top;
transition:all 1s ease-out;
max-width: 0;
max-height: 0;
}
.content.open {
transform:scale(1,1);
max-width: 500px;
max-height: 500px;
}
Related
SOLUTION:
The solution I found is using two divs on top of each other(to make the function clearer I made one of the divs red), while it has to animate two divs for the effect, it should still be cleaner and faster than using javascript:
.outerDiv {
width: 100%;
height: 50px;
position: relative;
}
.hover1 {
height: 50px;
width: 50px;
background: black;
position: relative;
top: 0px;
transition: width 1s
}
.hover2 {
height: 50px;
width: 50px;
background: red;
position: relative;
top: -50px;
transition: width 1s 1s
}
.outerDiv:hover .hover2 {
width: 100%;
transition: width 0s 0.9s;
}
.outerDiv:hover .hover1 {
width: 100%;
transition: width 1s;
}
<div class="outerDiv">
<div class="hover1"></div>
<div class="hover2"></div>
</div>
This can be achieved by specifying different transition time for the normal state and hover state.
<style>
#hoverDiv {
height: 50px;
width: 50px;
background: black;
transition: width 5s; /*Specify required time.
This affects how long it takes to transition
back to the normal state*/
}
#hoverDiv:hover {
width: 100%;
transition: width 2s; /*This affects how long it takes to transition
into the hover state*/
}
</style>
<div id="hoverDiv"></div>
Also, if you want to add a delay before width decreases back to normal, try
#hoverDiv {
height: 50px;
width: 50px;
background: black;
transition: width 5s;
transition-delay: 5s; /*Waits for 5 seconds and then decreases
back to normal size in 5 seconds*/
}
I'm working on this mobile menu where you can expand and collapse different categories. When doing so, a sliding animation should be performed on the expanded submenu (when expanding) or on the top-level menu (when collapsing).
The structure of the HTML is the following:
<div class="slideOpenMainMenu">
<div class="sideMenuGeneral">
...Top-level menu...
</div>
<div class="sideMenuPanelMainChildren">
...Expanded submenu...
</div>
</div>
By adding and removing classes, I show the appropriate div while hiding the other from view. As you'll notice, I go out of my way to not use anything like display:none; since then I won't be able to animate the containers. Instead I use a combination of width, height, visibility and flex properties to hide and show the containers.
/* Menu parent container */
.slideOpenMainMenu {
position: fixed;
width: 100%;
box-sizing: border-box;
background: linear-gradient(90deg, #12416e 0%, #0d3050 100%);
display: flex;
justify-content: center;
}
/* Top-level menu - Initial state */
.sideMenuGeneral {
width: 100%;
max-width: 620px;
display: flex;
flex-flow: column;
padding: 20px 16px 0 16px;
box-sizing: border-box;
overflow: hidden;
}
/* item submenu - Initial state */
.sideMenuPanelMainChildren {
width: 0;
height: 0;
flex: 0 1 0;
max-height: 100vh;
overflow: hidden;
visibility: hidden;
-webkit-overflow-scrolling: touch;
}
/* Top-level menu - Expanded state */
.slideOpenMainMenu.item-expanded .sideMenuGeneral {
width: 0%;
padding: 0 !important;
visibility: hidden;
}
.slideOpenMainMenu.item-expanded .sideMenuPanelMainChildren {
flex: 1 1 auto;
flex-flow: column;
width: 100%;
height: auto;
overflow: scroll;
visibility: visible;
}
For the animation, I use transform:translateY and opacity properties to create the sliding effect I want.
/* Initial state */
.slideOpenMainMenu .sideMenuPanelMainChildren {
opacity: 0;
transform: translateX(30%);
transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s ease;
}
.slideOpenMainMenu .sideMenuGeneral {
opacity: 1;
transform: translateX(0%);
transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s ease;
}
/* Expanded state */
.slideOpenMainMenu.item-expanded .sideMenuPanelMainChildren {
opacity: 1;
transform: translateX(0%);
}
.slideOpenMainMenu.item-expanded .sideMenuGeneral {
opacity: 0;
height: 0px;
transform: translateX(-50%);
}
As you can see on this fiddle, the animation works well in Chrome and Firefox. Not so well on Webkit and Edge. From what I can tell, there seems to be some kind of conflict between the change in width and the transitions, because when I disable changes in width, you can see the animation play out. What could cause the change in behavior between platforms? Is there a way to correctly sequence the changes?
So while writing this question, I arrived at the answer (as it often happens). The issue seems to be rooted in that the sequence of CSS property changes is different on different browsers. by adding a tiny delay (0.01s) to the transform transition I got it to work, like so:
transition: opacity 0.3s ease, transform 0.3s ease 0.01s, visibility 0s ease;
Still, I find this a little odd and also interesting. If anyone has info on how these things are sequenced in the browser that'd be really great to learn.
How can I realize a smooth transition for my mobile menu?
The transform property is working, but I want it to happen slowly..
My Sass looks like this
.mobile-nav
display: none
position: relative
width: 100%
background: $white
padding: 30px 0 20px
transform: translateY(-100%)
#media (max-width: 775px)
.mobile-nav.is-o
display: block
transform: translateY(0%)
The main obstacle you're facing is that the display property is not animatable.
Like a light switch, display: none is off and display: block is on. There's no middle ground, no fade effects, no CSS transitions.
However, there are multiple other properties you can use for transitions. Among them:
height
opacity
z-index
background-color
Here's an example:
.mobile-nav-toggle {
font-size: 2em;
cursor: pointer;
}
.mobile-nav {
display: inline-block;
overflow: hidden;
width: 0;
height: 0;
opacity: 0;
transition: width 1s, height 1s, opacity 0s 1s, background-color 0s 2s;
}
.mobile-nav-toggle:hover + .mobile-nav {
width: 150px;
height: 150px;
opacity: 1;
background-color: lightgreen;
transition: 1s;
}
<div class="mobile-nav-toggle">☰</div>
<div class="mobile-nav">
<ul>
<li><a>Item</a></li>
<li><a>Item</a></li>
<li><a>Item</a></li>
</ul>
</div>
References:
Full list of animatable properties in CSS
Transitions on the display: property
I personally use opacity combined with visibility to achieve fade effect like I would like for whole element. Remember to manipulate z-index, so you "hidden" object won't be clickable when dissapeared.
I have a css transition that moves an element on hover and an animation that rotates the element on hover too. There's a delay on the animation equal to the transition duration so that after it's transitioned to it's correct position, the animation starts. And it works nice, however, when we mouse off, the animation stops but it doesn't transition back down.
Is it possible to get it to transition back after we mouse off and the animation ends?
You can see an example here: http://codepen.io/jhealey5/pen/zvXBxM
Simplified code here:
div {
width: 200px;
height: 200px;
margin: 40px auto;
background-color: #b00;
position: relative;
&:hover {
span {
transform: translateY(-60px);
animation-name: rotate;
animation-duration: 1s;
animation-delay: .5s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
}
}
span {
position: absolute;
width: 20px;
height: 20px;
background-color: #fff;
bottom: 10px;
left: 0;
right: 0;
margin: auto;
transition: .5s;
}
#keyframes rotate {
from {
transform: translateY(-60px) rotate(0);
}
to {
transform: translateY(-60px) rotate(-90deg);
}
}
I have forked your project and adapted it so it works. You can find it here.
What I have changed is the following:
I give the white square a start position of top: 150px and let it, on hover of div, get a top: 0. The span gets a transition: top .5s and with that it goes to top: 0; on hover and back to top: 150px; when the mouse leaves.
I have removed the translateY(-60px); from the animation, because that would move it even more up when the animation would start.
Here's your new CSS:
div {
width: 200px;
height: 200px;
margin: 40px auto;
background-color: #b00;
position: relative;
&:hover {
span {
top: 0px;
animation: rotate 1s infinite .5s alternate;
animation-direction: alternate;
}
}
}
span {
position: absolute;
width: 20px;
height: 20px;
background-color: #fff;
bottom: 10px;
left: 0;
right: 0;
top: 150px;
margin: auto;
transition: top .5s;
}
#keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(-90deg);
}
}
Edit: The problem is that an animation is time-based and not action-based, which means that as soon as you trigger an animation, a timer starts running and it will run through all the keyframes until the set time has passed. Hover-in and hover-out have no effect, except that the timer can be stopped prematurely, but the animation will not continue (or reversed, which you wanted) after that. transition is action-based, which means it gets triggered every time an action (for example :hover) is happening. On :hover, this means it takes .5s to go to top:0 and when the hover ends, it takes .5s to got to top:150px.
I hope the above addition makes sense :)
As you can see, I also cleaned up a bit in your animation-name: etc., since it can be combined into one line.
As Harry pointed out, the problem is that you are animating/transitioning the same property, in this case transform. It looks like the current versions of Chrome/FF will allow the animation to take control of the property, thereby breaking the transition. It seems like the only way to work around this is to transition/animation a different property. Since you need to continue rotating the element, you could translate/position the element by changing the bottom property instead. I know that doesn't produce the exact same results, but nonetheless, it does move the element (just not relative to the parent element).
Updated Example
div:hover span {
bottom: 80px;
}
As an alternative, you could also wrap the span element, and then translate that element instead.
In the example below, the .wrapper element is transitioned to translateY(-60px) on hover, and then the child span element is rotated and maintains the animation.
Example Here
div {
width: 200px;
height: 200px;
margin: 40px auto;
background-color: #b00;
position: relative;
}
div:hover .wrapper {
transform: translateY(-60px);
}
div:hover .wrapper span {
animation-name: rotate;
animation-duration: 1s;
animation-delay: .5s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
.wrapper {
display: inline-block;
transition: .5s;
position: absolute;
bottom: 10px;
left: 0;
right: 0;
text-align: center;
}
.wrapper span {
display: inline-block;
width: 20px;
height: 20px;
background-color: #fff;
}
#keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(-90deg);
}
}
<div>
<span class="wrapper">
<span></span>
</span>
</div>
I have a :before pseudo element displayed on :hover of a particular element.
I'm using font awesome and want to vertically center the content of the :before, but vertical align, margins etc haven't been of much help.
Any ideas?
.tile:before {
font-family: FontAwesome;
font-size: 150px;
color: white;
text-align: center;
border-radius: 15px;
opacity: 0;
z-index: 9999;
width: 100%;
height: 100%;
content: "\f16b";
position: absolute;
background-color: rgba(219,127,8, 0.7);
-webkit-transition: all 0.3s ease-in-out;
-moz-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
-ms-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.tile:hover:before {
opacity: 1;
}
Here are a few potential suggestions for .tile:before :
1 - use pixel value instead of 100% for height:
height: 100px;
2 - Make sure this is being displayed as an element that can ACCEPT margin, padding, etc.
display: block;
-or-
display: inline-block;
3 - I know you said you tried margins, but did you try padding-top?
padding-top: 20px;
4 - Try setting the overflow to hidden or visible. This often forces elements to behave "better."
overflow:hidden;
I would try all of these TOGETHER and see what happens.
Last, I might try setting a "top:" value since you have "position:absolute;" already. Maybe try this in conjunction with "position:relative;" too.
top: 10px;
Really need all the code (HTML) to tell what would work.
Using :before as the cover background to display on top of the tile element, and an :after with:
.tile:after {
top: 50%;
left: 50%;
/* Both half of font-size */
margin-left: -75px;
margin-top: -75px;
height: 150px;
line-height: 1;
}
Seemed to do the trick. Thanks all.