【问题标题】:CSS animation - revert on mouseoutCSS 动画 - 鼠标移出时恢复
【发布时间】:2018-11-27 14:39:49
【问题描述】:

我确定这一定是以前被问过的,并且我已经找到了相关的问题,但我似乎无法解决这个问题。

我有一个元素,它接收一个类,然后扩展。稍后,当该类被删除时,它应该恢复(动画)回原来的宽度。

let el = document.querySelector('#side-bar');
el.addEventListener('click', evt => el.classList.toggle('contracted'));
#side-bar {
  height: 100%;
  width: 75px;
  background: red;
  position: fixed;
  left: 0;
  top: 0;
}

#side-bar.contracted {
  animation: .5s side-bar-contract forwards;
}

#side-bar:not(.contracted) {
  animation: .5s side-bar-expand forwards;
}

@keyframes side-bar-expand {
  to {
    width: 350px;
  }
}

@keyframes side-bar-contract {
  to {
    width: 75px;
  }
}
<div id='side-bar' class='contracted'></div>

展开动画效果很好。但是没有发生反转动画;它只是恢复到原来的属性,没有动画。

Fiddle

我做错了什么?

[编辑]

好吧,我显然应该提到为什么我不使用transition 这样做。这是一系列更广泛的依赖动画的一部分,这些动画一个接一个地按顺序运行。我的理解是,这种按时间顺序排列的重要情况对于animation 而不是transition 更好。

【问题讨论】:

    标签: css animation css-animations


    【解决方案1】:

    更新:(删除开头的动画)

    let init = 0,
        el = document.querySelector('#side-bar');
    
    el.addEventListener('click', function() {
        if (init < 1) {
            init++;
            el.classList.remove("init");
            el.classList.add('contracted');
        }
        el.classList.toggle('contracted');
    
    });
    #side-bar {
    	height: 100%;
    	width: 75px;
    	background: #d4653c;
    	position: fixed;
    	left: 0;
    	top: 0;
    	padding: .8rem;
    }
    
    #side-bar:not(.init) {
    	animation: .5s side-bar-expand forwards;
    }
    
    #side-bar.contracted {
    	animation: .5s side-bar-contract forwards;
    }
    
    @keyframes side-bar-expand {
    	to {
    		width: 350px;
    	}
    }
    
    @keyframes side-bar-contract {
    	from {
    		width: 350px;
    	}
    }
    &lt;div id='side-bar' class='init'&gt;Click me&lt;/div&gt;

    只需将side-bar-contract 中的to 更改为from

    @keyframes side-bar-expand { to { width: 350px; } }
    @keyframes side-bar-contract { from { width: 350px; } }
    

    let el = document.querySelector('#side-bar');
    el.addEventListener('click', evt => el.classList.toggle('contracted'));
    #side-bar {
    	height: 100%;
    	width: 75px;
    	background: #d4653c;
    	position: fixed;
    	left: 0;
    	top: 0;
    	padding: .8rem;
    }
    
    #side-bar:not(.contracted) {
    	animation: .5s side-bar-expand forwards;
    }
    
    #side-bar.contracted {
    	animation: .5s side-bar-contract forwards;
    }
    
    @keyframes side-bar-expand {
    	to {
    		width: 350px;
    	}
    }
    
    @keyframes side-bar-contract {
    	from {
    		width: 350px;
    	}
    }
    &lt;div id='side-bar' class='contracted'&gt;Click me&lt;/div&gt;

    【讨论】:

    • 我同意。我还将添加一个初始的额外类,例如具有animation: none 的“初始”,并在单击时删除;只是为了在页面最初加载时不播放动画。
    • 好声音,@maurojflores - 这就是我目前面临的问题,你的建议将阻止它。谢谢大家。
    【解决方案2】:

    为什么不只使用过渡动画:

    let el = document.querySelector('#side-bar');
    el.addEventListener('click', evt => el.classList.toggle('contracted'));
    #side-bar {
      height: 100%;
      width: 350px;                         /* have width at 350px when not contracted */
      background: #d4653c;
      position: fixed;
      left: 0;
      top: 0;
      padding: .8rem;
      transition: width .5s;   /* animate the width */
    }
    
    #side-bar.contracted {
      width: 75px;
    }
    &lt;div id='side-bar' class='contracted'&gt;Click me&lt;/div&gt;

    如果您需要使用关键帧,那么您需要从 350 像素开始第二个 - 您从 75 到 75 开始,这就是它没有动画的原因:

    let el = document.querySelector('#side-bar');
    el.addEventListener('click', evt => el.classList.toggle('contracted'));
    #side-bar {
      height: 100%;
      width: 75px;
      background: #d4653c;
      position: fixed;
      left: 0;
      top: 0;
      padding: .8rem;
    }
    
    #side-bar:not(.contracted) {
      animation: .5s side-bar-expand forwards;
    }
    
    #side-bar.contracted {
      animation: .5s side-bar-contract forwards;
    }
    
    @keyframes side-bar-expand {
      to {
        width: 350px;
      }
    }
    
    @keyframes side-bar-contract {
      0% {
        width: 350px;
      }
      100% {
        width: 75px;
      }
    }
    &lt;div id='side-bar' class='contracted'&gt;Click me&lt;/div&gt;

    【讨论】:

    • 我应该提到我正在使用动画而不是过渡,因为其他子元素应该在之后淡入,即这里有一个年表。我的理解是转换不能真正处理像这样的多元素、按时间顺序排列的非平凡案例(可能是错误的。)
    • 啊,好的,请参阅编辑 - 您需要从 350px 开始第二个动画(它目前从 75px 开始,因为这是您给边栏的宽度(即使其他动画以 350px 结束,我认为它采用初始值而不是其他动画结束的值)
    • 有趣 - 我从来没有想过只有一个 from 的动画。非常感谢。
    • 实际上来自 gildas 的答案更好,因为你没有得到初始的关闭动画
    • 非常诚实,请勾选其他答案。不过感谢您的帮助。
    【解决方案3】:

    首先,我建议您使用悬停样式和 css transition 来执行此操作,而不是像为单个属性设置动画这样简单的动画。

    .class {
      width: 400px;
      transition: width 1500ms ease-in-out;
    }
    
    .class:hover {
      width: 100px;
    }
    

    CSS 过渡实际上会在过渡的中途停止并为您恢复到初始大小。

    其次,我建议您不要在 CSS 中为 width 属性设置动画或过渡。 Here's a great article about what properties you should avoid animating.

    如果您需要延迟发生在其他元素上的过渡,您可以使用transition-delay 属性。此属性也可以应用于悬停效果...包括对父元素的悬停效果。因此,您可能会在给定时间使用多个悬停效果来实现您想要的效果。

    【讨论】:

    • 谢谢。我应该提到为什么我使用动画而不是过渡。请参阅编辑。
    • 啊。看到你的评论。我为你更新了我的答案。从技术上讲,您的其他转换可以使用transition-delay 运行。这样,无论您需要延迟多长时间,它们都不会运行。
    • 谢谢,我会研究你的答案。回复:转换延迟,那么问题是您何时想要反转转换。假设我将鼠标悬停在元素 A 上,元素 B 然后在 A 完成后淡入。我认为在还原时没有办法扭转这个顺序,对吧?因此我选择了动画。
    • 是的。当您将鼠标移动到另一个区域时,您还可以根据需要更改悬停时的转换延迟。过渡会自动为您反转。但是,根据您描述的交互,在这种情况下,悬停实际上可能不是最好的 UX。用户会因为悬停而没有看到任何显示而感到困惑。所以坚持使用你可以控制代码的动画可能是一个更好的答案。
    • 感谢您的意见。我的真实情况是基于点击的,而不是悬停(这只是我评论中为什么我选择动画而不是过渡的一个人为的例子。)谢谢大家和 +1。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-15
    • 2014-11-29
    • 2020-08-07
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    相关资源
    最近更新 更多