【问题标题】:Event timeline with animation带有动画的事件时间线
【发布时间】:2021-12-31 21:34:28
【问题描述】:

我正在尝试构建的是带有一些动画的垂直时间轴组件。我正在尝试的动画是它从第一个圆圈开始,无论哪个项目的状态为真,垂直线将从上到下绘制,同时无论哪个步骤完成,当线穿过它时,将从圆形变为完成复选标记。

如何实现上面的动画,到目前为止我已经尝试过但不知道如何实现上面的。

我想达到什么目的this

sandbox

感谢任何帮助。

【问题讨论】:

    标签: javascript css reactjs


    【解决方案1】:

    我已经修改了您的沙盒以使其正常工作: https://codesandbox.io/s/animable-timeline-reactjs-tiofz

    1. 对于动画,我使用了以下 CSS:

      div {
        height: 200px;
        width: 10px;
      }
      
      .green-progress {
        background: linear-gradient(0, #00a36c, #00a36c) no-repeat, #ccc;
        background-size: 100% 0;
        animation: progressAnim 3s linear infinite forwards;
      }
      
      @keyframes progressAnim {
        0% {
          background-size: 100% 0;
        }
        100% {
          background-size: 100% 100%;
        }
      }
      <div class="green-progress"></div>

      为了使实际时间线动画化,我们将从第一个条目中删除垂直条,并且只有选中的圆圈。从第二个条目开始,我们将有一个垂直条和选中的圆圈。为了使它们保持一致,它们已向上移动。为了显示进度,该条将填充,然后将检查圆圈。
    2. App 转换为有状态组件,以便我们可以维护动画状态。 在构造函数中,为每个条目添加了idstartAnimchecked 状态。在这里,我们将设置startAnim 标志以在相应的TimelineConnector 上启动动画。 checked 用于控制对圆圈的勾选。

    3. TimelineConnector 中,如果this.props.startAnim 为真,则将类设置为绿色进度。还添加了onAnimationEnd 处理程序作为{() => this.props.onAnimDone(this.props.id)}。这告诉 App 组件动画是在这个组件上用id 完成的。

    4. TimelineDot 中使用props.event.checked 设置检查状态。

    5. 在 App 中添加了一个生命周期钩子 componentDidMount,当所有组件都添加到实际 DOM 时,它将被调用。在钩子中,您选中第一个圆圈并在第一个 TimelineConnector 上启动动画。

    6. 当 TimelineConnector 完成动画后,它会在 App 中调用 startNextAnim。在该方法中,您首先完成最后一个条目的复选标记。如果条目有status:true,则开始下一个动画。


    我们可以为每个动画添加延迟并立即运行它们。但是父控制每个组件以及每个组件在动画完成时发出通知使得更新代码更加灵活。您可以根据每个条目的状态为每个条目设置不同的动画。

    我们可以使用 react-spring 动画库,但事情会变得复杂。 CSS 动画是最简单的解决方案。

    【讨论】:

    【解决方案2】:

    你可以试试这个

    /* timeline css */
    @keyframes fill-color {
      0% {
        height: 0;
      }
      100% {
        height: 100%;
      }
    }
    
    @keyframes fill-color1 {
      0% {
        height: 0;
      }
      100% {
        height: 50%;
      }
    }
    @keyframes scaleup {
      0% {
        transform: scale(0);
      }
      100% {
        transform: scale(1);
      }
    }
    
    @keyframes fade {
      0% {
        color: rgba(black, 0.4);
      }
      100% {
        color: rgba(black, 1);
      }
    }
    body {
      margin: 0;
      padding: 0;
    }
    .timeline {
      padding: 0;
      list-style: none;
      margin: 32px;
      overflow: hidden;
      position: relative;
    }
    .details {
      margin-left: 48px;
      border-bottom: 1px solid #f2f2f2;
      min-height: 85px;
      display: flex;
      justify-content: center;
      flex-direction: column;
    }
    .list,
    .list-content {
      position: relative;
      width: 100%;
    }
    .list-content::before,
    .list-content::after {
      content: "";
      display: block;
      position: absolute;
      left: 0;
      transition: 0.2s all linear;
      width: 0.714rem;
      height: 0.714rem;
      border-radius: 50%;
      background-color: gray;
      top: 50%;
      z-index: 3;
      margin-left: 0.35rem;
      margin-top: rem(-8px);
    }
    
    .list-content::after {
      z-index: 2;
    }
    .list {
      position: relative;
      width: 100%;
    }
    
    .list.active .list-content:before {
      transform: scale(0);
      width: 17px;
      height: 17px;
      border: 2px solid white;
      background-color: red;
      background-image: url("https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/White_check.svg/2048px-White_check.svg.png");
      background-position: center;
      background-repeat: no-repeat;
      background-size: 9px 7px;
      margin-left: 0;
      margin-top: -8px;
      animation: scaleup 0.4s forwards;
    }
    .list:before,
    .list:after {
      content: "";
      display: block;
      position: absolute;
      left: 0;
      transition: 0.2s all linear;
      width: 0.214rem;
      margin-left: 0.6rem;
    }
    .list:before {
      background: #f2f2f2;
      height: 100%;
    }
    .list:after {
      background: red;
      height: 0;
      z-index: 1;
    }
    .list:before {
      top: -50%;
    }
    .list.active:after {
      top: 0;
      animation: fill-color 0.4s forwards;
    }
    .list:last-child:after {
      display: none;
    }
    .list:last-child.active:after {
      display: block;
      bottom: 50%;
      animation: fill-color1 0.4s forwards;
    }
    .list:last-child .details {
      border-bottom: none;
    }
    .list:first-child:before {
      display: none;
    }
    .list:first-child.active:after {
      animation: fill-color1 0.4s forwards;
      top: 50%;
    }
    .list:first-child.active:after {
      animation-delay: 1s;
    }
    .list:first-child.active .list-content:before {
      animation-delay: 0.5s;
    }
    
    .list:nth-child(2).active:after {
      animation-delay: 2s;
    }
    .list:nth-child(2).active .list-content:before {
      animation-delay: 2s;
    }
    
    .list:nth-child(3).active:after {
      animation-delay: 3s;
    }
    .list:nth-child(3).active .list-content:before {
      animation-delay: 3s;
    }
    
    .list:nth-child(4).active:after {
      animation-delay: 4s;
    }
    .list:nth-child(4).active .list-content:before {
      animation-delay: 4.15s;
    }
    <body>
        <ul class="timeline">
            <li class="list active">
                <div class="list-content">
                    <div class="details">
                     <h4 class="status-title">Step 1</h4>
                    </div>
                </div>
            </li>
            <li class="list active">
                <div class="list-content">
                    <div class="details">
                     <h4 class="status-title">Step 2</h4>
                    </div>
                </div>
            </li>
            <li class="list active">
                <div class="list-content">
                    <div class="details">
                     <h4 class="status-title">Step 3</h4>
                    </div>
                </div>
            </li>
            <li class="list active">
                <div class="list-content">
                    <div class="details">
                     <h4 class="status-title">Step 4</h4>
                    </div>
                </div>
            </li>
        </ul>
    </body>

    您也可以查看Sandbox

    在这里,我在所有 li 元素中添加了 active 类,但如果您想激活两个步骤,那么只应用前两个 li(即该类是有条件的)

    【讨论】:

      【解决方案3】:

      可以为背景设置动画。请检查下面的示例

      .bg {
        min-height: 300px;
        width: 10px;
        background: linear-gradient(0, red, red) no-repeat;
        background-size: 100% 0;
        animation: gradient 15s ease infinite;
      }
      
      @keyframes gradient {
          0% {
              background-size: 100% 0;
          }
          100% {
              background-size: 100% 100%;
          }
      }
      &lt;div class="bg"&gt;&lt;/div&gt;

      您的 TimelineConnector 可以使用包含该 CSS 类的内容进行更新。

      【讨论】:

      • 谢谢,在这里我尝试过使线条动画化,但问题是我不知道如何像 gif 中所示那样逐一制作动画,因为根据数据只完成了三个,所以如何应该画一条线,直到完成我无法继续前进的步骤
      • 当然,您可以为动画添加一个附加参数,作为从您的 React 脚本定义的 CSS 变量,其中包含项目数 * 超时,类似于:animation: gradient 15s ease infinite var(--timeout );
      • 要在元素范围内定义 CSS 变量,您可以使用这样的方法: element.style.setProperty('--timeout', '0.5s');
      • 对不起,可能是新手,如果你能帮忙,你能像 Gif 一样分享你能够解决的代码框
      • 它可以通过项目和一组不同的超时来循环。示例:codepen.io/alekskorovin/pen/ExvJOrz
      猜你喜欢
      • 2013-11-09
      • 2020-06-27
      • 1970-01-01
      • 2017-01-19
      • 1970-01-01
      • 2014-07-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-18
      相关资源
      最近更新 更多