如何防止 CSS 关键帧动画在页面加载时出现 运行?

How to prevent a CSS keyframe animation from running on page load?

我有一个 div 动画内容:

#container {
  position: relative;
  width: 100px;
  height: 100px;
  border-style: inset;
}
#content {
  visibility: hidden;
  -webkit-animation: animDown 1s ease;
  position: absolute;
  top: 100px;
  width: 100%;
  height: 100%;
  background-color: lightgreen;
}
#container:hover #content {
  -webkit-animation: animUp 1s ease;
  animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;
}
@-webkit-keyframes animUp {
  0% {
    -webkit-transform: translateY(0);
    visibility: hidden;
    opacity: 0;
  }
  100% {
    -webkit-transform: translateY(-100%);
    visibility: visible;
    opacity: 1;
  }
}
@-webkit-keyframes animDown {
  0% {
    -webkit-transform: translateY(-100%);
    visibility: visible;
    opacity: 1;
  }
  100% {
    -webkit-transform: translateY(0);
    visibility: hidden;
    opacity: 0;
  }
}
<div id="container">
  <div id="content"></div>
</div>

悬停时,内容会滑入容器 div。 当我刷新页面并加载页面时,#content 的 animDown 动画将 运行,我更喜欢它而不是 运行 只有在悬停事件之后。

有没有办法做到这一点 CSS,或者我必须在 JS 中弄清楚一些东西?

http://jsfiddle.net/d0yhve8y/

解决方案 1 - 在第一次悬停时添加向下动画

可能最好的选择是在用户第一次将鼠标悬停在 container 上之前不打开按下动画。

这涉及侦听 mouseover 事件,然后在该点添加带有动画的 class,并删除事件侦听器。这样做的主要(潜在)缺点是它依赖于 Javascript.

;(function(){
    var c = document.getElementById('container');
    function addAnim() {
        c.classList.add('animated')
        // remove the listener, no longer needed
        c.removeEventListener('mouseover', addAnim);
    };

    // listen to mouseover for the container
    c.addEventListener('mouseover', addAnim);
})();
#container {
    position:relative;
    width:100px;
    height:100px;
    border-style:inset;
}
#content {
    position:absolute;
    top:100px;
    width:100%;
    height:100%;
    background-color:lightgreen;
    opacity:0;
}

/* This gets added on first mouseover */
#container.animated #content {
    -webkit-animation:animDown 1s ease;
}

#container:hover #content {
    -webkit-animation:animUp 1s ease;
    animation-fill-mode:forwards;
    -webkit-animation-fill-mode:forwards;
}

@-webkit-keyframes animUp {
    0% {
        -webkit-transform:translateY(0);
        opacity:0;
    }
    100% {
        -webkit-transform:translateY(-100%);
        opacity:1;
    }
}
@-webkit-keyframes animDown {
    0% {
        -webkit-transform:translateY(-100%);
        opacity:1;
    }
    100% {
        -webkit-transform:translateY(0);
        opacity:0;
    }
}
<div id="container">
    <div id="content"></div>
</div>

解决方案 2 - 隐藏播放动画

解决此问题的另一种方法是最初隐藏元素,确保在隐藏时播放动画,然后使其可见。这样做的缺点是时间可能会稍微偏离,并且显示得太早,而且悬停不能立即使用。

这需要一些 Javascript 等待动画的长度,然后才使 #content 可见。这意味着您还需要将初始 opacity 设置为 0,这样它就不会出现在加载中,并且还需要从关键帧中删除 visibility - 这些无论如何都没有做任何事情:

// wait for the animation length, plus a bit, then make the element visible
window.setTimeout(function() {
    document.getElementById('content').style.visibility = 'visible';
}, 1100);
#container {
    position:relative;
    width:100px;
    height:100px;
    border-style:inset;
}

#content {
    visibility:hidden;
    -webkit-animation:animDown 1s ease;
    position:absolute;
    top:100px;
    width:100%;
    height:100%;
    background-color:lightgreen;
    opacity:0;
}

#container:hover #content {
    -webkit-animation:animUp 1s ease;
    animation-fill-mode:forwards;
    -webkit-animation-fill-mode:forwards;
}

@-webkit-keyframes animUp {
    0% {
        -webkit-transform:translateY(0);
        opacity:0;
    }
    100% {
        -webkit-transform:translateY(-100%);
        opacity:1;
    }
}

@-webkit-keyframes animDown {
    0% {
        -webkit-transform:translateY(-100%);
        opacity:1;
    }
    100% {
        -webkit-transform:translateY(0);
        opacity:0;
    }
}
<div id="container">
    <div id="content"></div>
</div>

解决方案 3 - 使用转换

在您的场景中,您只能通过将 keyframe 替换为 transition 来实现此 CSS,因此它以 opacity:0 开头,并且只是悬停opacitytransform:

有变化

#container {
    position:relative;
    width:100px;
    height:100px;
    border-style:inset;
}

#content {
    position:absolute;
    top:100px;
    width:100%;
    height:100%;
    background-color:lightgreen;

    /* initial state - hidden */
    opacity:0;
    /* set properties to animate - applies to hover and revert */
    transition:opacity 1s, transform 1s;
}

#container:hover #content {
    /* Just set properties to change - no need to change visibility */
    opacity:1;
    -webkit-transform:translateY(-100%);
    transform:translateY(-100%);
}
<div id="container">
    <div id="content"></div>
</div>

我总是将预加载 class 设置为动画时间值为 0 的主体,并且效果很好。我有一些反向过渡,所以我也必须删除它们的加载动画。我通过临时将动画时间设置为 0 解决了这个问题。您可以更改过渡以匹配您的过渡。

HTML

... <body class="preload">...

CSS 正在将动画设置为 0s

body.preload *{
animation-duration: 0s !important;
-webkit-animation-duration: 0s !important;
transition:background-color 0s, opacity 0s, color 0s, width 0s, height 0s, padding 0s, margin 0s !important;}

JS 将在一些延迟后删除 class,以便动画可以在正常时间发生 :)

setTimeout(function(){
    document.body.className="";
},500);

Rotation animation that (appears) not to run until needed.
下面的 CSS 允许向上和向下箭头显示菜单项。 动画在页面加载时似乎 运行 没有,但确实如此。

@keyframes rotateDown {
   from { transform: rotate(180deg); }
   to   { transform: rotate(0deg); }
}

@keyframes rotateUp {
   from { transform: rotate(180deg); }
   to   { transform: rotate(0deg); }
}

div.menu input[type='checkbox'] + label.menu::before {
   display            :inline-block;
   content            : "▼";
   color              : #b78369;
   opacity            : 0.5;
   font-size          : 1.2em;
}

div.menu input[type='checkbox']:checked + label.menu::before {
   display            : inline-block;
   content            : "▲";
   color              : #b78369;
   opacity            : 0.5;
   font-size          : 1.2em;
}

div.menu input[type='checkbox'] + label.menu {
   display            : inline-block;
   animation-name     : rotateDown;
   animation-duration : 1ms;
}

div.menu input[type='checkbox']:checked + label.menu {
   display            : inline-block;
   animation-name     : rotateUp;
   animation-duration : 1ms;
}

div.menu input[type='checkbox'] + label.menu:hover {
   animation-duration : 500ms;
}

div.menu input[type='checkbox']:checked + label.menu:hover {
   animation-duration : 500ms;
}

从上到下:

  1. 创建旋转。为此,有两个……一个用于向下箭头,一个用于向上箭头。需要两个箭头,因为在旋转之后,它们 return 回到自然状态。因此,向下箭头开始向上并向下旋转,而向上箭头开始向下并向上旋转。
  2. 创建小箭头。这是 ::before
  3. 的直接实现
  4. 我们把动画放在标签上。没有什么特别的,除了动画持续时间是 1ms。
  5. 鼠标驱动动画速度。当鼠标悬停在元素上时,动画持续时间设置为足够长的时间以使其看起来平滑。

Working on my site

Is there a way to do this pure CSS ?

是的,绝对是:见分叉http://jsfiddle.net/5r32Lsme/2/ 真的不需要JS了

and I'd prefer it to run only after a hover event.

所以你需要告诉 CSS 当它不是悬停事件时会发生什么 - 在你的例子中:

#container:not(:hover) #content {
  visibility: hidden;
  transition: visibility 0.01s 1s;
}

但是有两点需要注意:

1) 上面的过渡延迟应该匹配你的动画持续时间

2) 不能在动画中使用隐藏动画onLoad的属性。 如果您确实需要动画中的 visibility,请先隐藏动画,例如

#container:not(:hover) #content {
  top: -8000px;
  transition: top 0.01s 1s;
}    

旁注:

建议将原生CSS属性放在前缀属性之后,所以应该是

-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;

现在有原生 transform

-webkit-transform: translateY(0);
transform: translateY(0);

这不是纯粹的 CSS 但也许有人会像我一样偶然发现这个线程:

在 React 中,我通过在 ComponentDidMount() 中设置一个临时 class 来解决这个问题,如下所示:

componentDidMount = () => {
    document.getElementById("myContainer").className =
        "myContainer pageload";
};

然后在 css:

.myContainer.pageload {
    animation: none;
}

.myContainer.pageload * {
    animation: none;
}

如果您不熟悉上面的“*”(n.b。space)意味着它也适用于该元素的所有后代。 space 表示所有后代,星号是指所有类型元素的通配符。

如果您在 2019 年之后查看此内容,更好的解决方案是:

let div = document.querySelector('div')
document.addEventListener('DOMContentLoaded', () => {
    // Adding timeout to simulate the loading of the page
    setTimeout(() => {
        div.classList.remove('prevent-animation')
    }, 2000)
    
    document.querySelector('button').addEventListener('click', () => {
        if(div.classList.contains('after')) {
            div.classList.remove('after')
        } else {
            div.classList.add('after')
        }
    })
})
div {
    background-color: purple;
    height: 150px;
    width: 150px;
}

.animated-class {
    animation: animationName 2000ms;
}

.animated-class.prevent-animation {
    animation-duration: 0ms;
}

.animated-class.after {
    animation: animation2 2000ms;
    background-color: orange;
}

@keyframes animationName {
    0% {
        background-color: red;
    }
    50% {
        background-color: blue;
    }
    100% {
        background-color: purple;
    }
}

@keyframes animation2 {
    0% {
        background-color: salmon;
    }
    50% {
        background-color: green;
    }
    100% {
      background-color: orange;
    }
}
<div class="animated-class prevent-animation"></div>
<button id="btn">Toggle between animations</button>

基于 Tominator 的回答,在 React 中,您可以像这样为每个组件应用它:

import React, { Component } from 'react'

export default class MyThing extends Component {
  constructor(props) {
    super(props);

    this.state = {
      preloadClassName: 'preload'
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    return nextState.preloadClassName !== this.state.preloadClassName;
  }

  componentDidUpdate() {
    this.setState({ preloadClassName: null });
  }

  render() {
    const { preloadClassName } = this.state;

    return (
      <div className={`animation-class ${preloadClassName}`}>
        <p>Hello World!</p>
      </div>
    )
  }
}

和 css class:

.preload * {
  -webkit-animation-duration: 0s !important;
  animation-duration: 0s !important;
  transition: background-color 0s, opacity 0s, color 0s, width 0s, height 0s, padding 0s, margin 0s !important;
}

不依赖javascript总是更好的解决方案。

这里提到的带CSS的就可以了。不在鼠标悬停时隐藏的想法在某些情况下很好,但我注意到如果我希望动画在鼠标移出元素时发生,由于 :not(:hover) 规则,它不会发生.

我想出的解决方案最适合我,通过向父元素添加动画,它只在末尾添加不透明度,持续时间相同。显示比解释更容易:

我抓取了@sebilasse 和@9000 制作的 fiddle 并在那里添加了以下代码:

https://jsfiddle.net/marcosrego/vqo3sr8z/2/

#container{
    animation: animShow 1s forwards;    
}

@keyframes animShow {
    0% {
      opacity: 0;
    }
    99% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
}

不得不解决类似的挑战,在隐藏元素的关键帧上 CSS-only trick morewry posted already back in 2013 is to create an animation that initially is in a paused play-state

#content {
  animation:animDown 1s ease, hasHovered 1ms paused;
  animation-fill-mode: forwards; /* for both animations! */
}
#container:hover #content {
  animation:animUp 1s ease, hasHovered 1ms;
}

/* hide #content element until #container has been hovered over */
@keyframes hasHovered {
  0% { visibility: hidden; }     /* property has to be removed */
  100% { visibility: visible; }  /* from the other animations! */
}

悬停时,由于 animation-fill-mode

,即使在鼠标离开后,也会应用非常简短的动画变换并保持在 100% 关键帧状态

有关如何设置具有多个动画的 animation 子属性,请参阅 https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations#setting_multiple_animation_property_values