Reputation: 8103
I need to animate height, and set overflow: hidden
for the first keyframe, and overflow: visible
(and keep it) for the last one.
I'm trying this, but at the end, overflow
is still hidden
.
How can I solve this issue?
The 2 includes are merely SCSS polifill mixins.
@include keyframes(open) {
0% {
height: 0;
overflow: hidden;
}
100% {
height: $main_menu_height;
overflow: visible;
}
}
#main-menu-box {
overflow: hidden;
height: 0;
&.opened{
@include animation('open 200ms ease-out 0s 1 normal forwards');
}
}
Upvotes: 3
Views: 29067
Reputation: 18766
In most modern browsers, clip-path
(prefixed with -webkit-
in Safari) is an animatable property which can sometimes be used as an alternative to overflow
.
Given the original example, the closest way to use clip-path
to emulate flipping overflow
on the last frame would look something like this:
@include keyframes(open) {
0% {
height: 0;
clip-path: inset(0);
}
99.99999% {
clip-path: inset(0);
}
100% {
height: $main_menu_height;
clip-path: inset(-100vh -100vw);
}
}
#main-menu-box {
clip-path: inset(0);
height: 0;
&.opened {
@include animation('open 200ms ease-out 0s 1 normal forwards');
}
}
Since this animation is a simple linear animation it could even be replaced with a regular CSS transition:
#main-menu-box {
clip-path: inset(0);
height: 0;
transition: clip-path 0s ease-out, height 200ms ease-out;
&.opened {
height: $main_menu_height;
clip-path: inset(-100vh -100vw);
transition-delay: 200ms, 0s;
}
}
However, there are two notable differences between clip-path
and overflow
which make it not suitable in all cases.
First, unlike an element with overflow: visible
, an element with any clip-path
has a stacking context, so the way that overflowing content is rendered will be different—though in the case of a menu with overflowing content, you probably wanted this anyway!
Second, unlike overflow
which only clips children, clip-path
clips the entire element. This means if you have borders, box shadows, etc., those are going to get clipped, too. Depending upon the design of the container, this can sometimes be worked around by applying the clip to a child wrapper element instead.
Upvotes: 2
Reputation: 8103
The solution is to use AnimationEvent listeners. Here's my raw implementation:
CSS
• 2 animations (open, close)
• 2 classes (opened, closed)
• 2 states (overflow hidden/visible)
opened and closed are always toggled at animationstart, while hidden/visible states are differently worked out on animationend.
Note: you'll see a #main-menu element: it's an UL with transitioned translations on y-axis, because the whole thing is a menu slide-down/up effect.
@include keyframes(open) {
0% {
height:0;
}
100% {
height:$main_menu_height;
}
}
@include keyframes(close) {
0% {
height:$main_menu_height;
}
100% {
height:0;
}
}
#main-menu-box{
overflow-y:hidden;
height:0; // js
&.closed{
@include animation('close 200ms ease-out 0s');
}
&.opened{
@include animation('open 200ms ease-out 0s 1');
//#main-menu{
// @include translate(0, 0);
//}
}
&.overflow-hidden{
overflow-y:hidden;
}
&.overflow-visible{
overflow-y:visible;
}
}
JS
• hamburger is a simple on/off button
• for now I had to use both jquery and vanilla selectors..
function poly_event_listener(element, type, callback) {
var pfx = ['webkit', 'moz', 'MS', 'o', ''];
for(var i=0; i<pfx.length; i++) {
if (pfx[i] === '') type = type.toLowerCase();
element.addEventListener(pfx[i]+type, callback, false);
}
}
var hamburger = $('header .hamburger');
var main_menu_box = $('#main-menu-box');
var main_menu_box_std = document.querySelector('#main-menu-box');
var init_menu = true;
hamburger.click(function(){
if(init_menu){
main_menu_box.addClass('opened');
init_menu = false;
return;
}
main_menu_box.toggleClass('opened closed');
});
poly_event_listener(main_menu_box_std,'AnimationStart',function(e){
main_menu_box.addClass('overflow-hidden');
main_menu_box.removeClass('overflow-visible');
});
poly_event_listener(main_menu_box_std,'AnimationEnd',function(e){
// in all the other cases I want hidden:true, visible:false
// if class == closed, since animationend comes after animationstart, the state will already be hidden:true, visible:false
// so non need to check for 'closed' here
if(main_menu_box.hasClass('opened')){
main_menu_box.addClass('overflow-visible');
main_menu_box.removeClass('overflow-hidden');
}
});
This works for me.
Upvotes: 1