十分钟CSS创建简单的时间轴效果

1,759 阅读3分钟

timeline.gif

前言

因为最近做一个创建Svg动画的Demo,需要一个时间轴展示编辑效果,所以用CSS简单实现了一下😆,其实主要就使用到了animation-play-state属性。

代码预览

Stackblitz: stackblitz.com/edit/vitejs…

技术栈

CSS animation:
animation-name -- 动画名字
animation-duration -- 持续时间
animation-timing-function -- 速度曲线(贝塞尔): linear...
animation-delay -- 延迟时间
animation-iteration-count -- 播放次数 :infinite...
animation-direction -- 动画方向 : normal/reverse/alternate...
animation-fill-mode -- 动画不播放的时候,要应用到元素的样式 :forwards(移动div从左到右,然后就停留 在右)/backwards
animation-play-state -- 动画是否暂停 : paused/running

实现步骤

  • 🍕上下布局面板
  • 🍔播放、暂停、停止图标按钮及动画
  • 🍟时间条 body 背景
  • 🍳帧线及其移动

布局

image.png

上下布局,两部分:top、body。flex布局,direction设置column,top元素设置height,body元素flex-1。
top部分设置阴影,box-shadow: 0 2px 15px 4px #00000090;

flex布局是比较基础的,可参考阮一峰老师的blog学习。www.ruanyifeng.com/blog/2015/0…
box-shadow: /* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色 */ 任意数量的阴影 用逗号隔开就好了

 <div
    class="
      flex flex-col
      w-800px
      h-280px
      rounded-3xl
      bg-[var(--animate-bg-color)]
      overflow-hidden
    "
  >
      <!-- top -->
      <div class="top w-full h-14 flex flex-row items-center justify-between px-4" ></div>

      <!-- body -->
      <div class="w-full flex-1 px-5 min-h-100px"></div>
  </div>

按钮及动画

btn-animate.gif

按钮是从 @iconify-json/carbon 拿的。

image.png

<button class="rounded-full">
    <div class=" w-8 h-8 cursor-pointer bg-green-400 animate-btn" i="carbon-play-outline" />
</button>
<div class=" w-8 h-8 cursor-pointer bg-green-400 animate-btn" i="carbon-pause-outline" />
<div class=" w-8 h-8 cursor-pointer bg-green-400 animate-btn" i="carbon-stop-outline" />

动画主要是配合 transition 设置 outline 。outline 跟 border 差不多,不过区别是 outline 不占据空间,绘制于元素内容周围。outline-offset 用于设置 outline 与一个元素边缘或边框之间的间隙。

CSS transition:
transition-property -- 指定CSS属性的name,transition效果
transition-duration -- 持续时间
transition-timing-function -- 指定CSS属性的name,transition效果
transition-delay -- 延迟时间

button{
  outline: 2px dotted transparent;
  outline-offset: -2px;
  transition: outline 0.12s ease, outline-offset 0.12s ease;
}
button:focus{
  outline: 2.5px dashed var(--primary-color) !important;
  stroke-dashoffset: 12px;
  outline-offset: 2px;
  transition: outline 0.12s ease, outline-offset 0.12s ease;
  transform: scaleX(1.1) scaleY(1.1);
}
button:active {
  outline: 2px dotted transparent;
  transform: scaleX(1) scaleY(1);
}

时间线及body 背景

image.png

时间线

以 5% 为一个单位,从0到100%,共有21个数, v-for="item,index in 21"中 item 是从 1 到21 ,index是从 0 到 20 。

<div class="tick" v-for="i in 21" :key="i">
    <b>{{(i*5) - 5}}%</b>
</div>

样式

“父相子绝”定位,子元素21个flex && justify-between,nth-child(even) 偶数不显示文字。

.timelineTicks{
    @apply absolute w-full top-0 left-0 flex flex-row justify-between;
    .tick{
      height: 10px;
      width: 2px;
      font-size: 0.6em;
      position: relative;
      background: linear-gradient(360deg, currentColor, #00000000);

      b{
        position: absolute;
        width: 20px;
        text-align: center;
        left: 50%;
        bottom: -14px;
        margin-left: -10px;
        top: 15px;
      }

      &:nth-child(even){
        b{
          display: none;
        }
      }
    }
  }
  1. 背景的话,点状背景,本来打算直接使用 svg pattern 宽高铺满,但是考虑到后面列要和时间条对齐,于是就只有一列svg,然后图省事简单的循环遍历了21个😅。
<div v-for="i in 21" :key="i">
    <svg width="5" height="100%">
      <defs>
        <pattern id="rect" patternUnits="userSpaceOnUse" width="5" height="40">
          <rect y="5" width="5" height="5" fill="currentColor" rx="5"></rect>
        </pattern>
      </defs>
      <rect id="canvas" width="5" height="100%" fill="url(#rect)" />
    </svg>
</div>

帧线及播放

animate.gif

帧线

这里的线,也是图了个方便,使用 :before 伪元素绘制了一个五边形加一条线构造。伪元素的话使用了 clip-path 切割的,可参考这个 tool 快速生成。

动画

动画的话就是 。帧线 “父相子绝”定位,设置其left,0到100%移动。这样就可以一直左右移动,设置其 animation-play-state,让其暂停或播放。

/* 设置给帧线 */
animation:scrubAnimation 5s linear infinite running
/* 定义的动画 */
@keyframes scrubAnimation {
    0%{
      left: 0%;
    }
    100%{
      left: 100%;
    }
}

按钮控制动画

将控制事件绑定到按钮上,这里使用的是 vue3 的 css 的 v-bind函数 ,也可以将其样式写到动态 style 上,例如 :style="{animation-play-state: scrubberAnimationState }"。分别给三个按钮,绑定触发更改 animation 和 animation-play-state: running/paused 的值就好了。

const scrubberPositionLeft = ref('0');
const scrubberAnimationState = ref('');
const scrubberAnimation = ref('');
const startPlay = () => {
  store.isPlay = true;
  scrubberAnimation.value = 'scrubAnimation 5s linear infinite running';
  scrubberAnimationState.value = 'running';
};
const pausePlay = () => {
  store.isPlay = false;
  scrubberAnimationState.value = 'paused';
};
const stopPlay = () => {
  if (store.isPlay) {
    // if 正在播放 $reSet
    store.isPlay = false;
    scrubberAnimation.value = '';
  }
};

附言

以上,一个简单的时间线的动画就完成了,代码很简单,待改进的还有很多,后面再修改,这里在此记录。

Stackblitz地址: stackblitz.com/edit/vitejs…
github地址:github.com/pinky-pig/w…