普通视图

发现新文章,点击刷新页面。
昨天 — 2025年6月8日首页

纯css实现一个沙漏动画

作者 JYeontu
2025年6月8日 00:37

说在前面

沙漏大家都见过吧,使用js的话相信大家都能很轻易地实现一个沙漏动画吧,今天我们纯css来实现一个试试。

在线体验

码上掘金

codePen

codepen.io/yongtaozhen…

代码实现

html

<div class="container">
  <div class="hourglass">
    <div class="frame"></div>
    <div class="glass"></div>
    <div class="sand-top-stroke"></div>
    <div class="sand-top"></div>
    <div class="sand-bottom-stroke"></div>
    <div class="sand-bottom"></div>
    <div class="sand-flow"></div>
    <div class="sand-drop"></div>
    <div class="glass-reflection"></div>
  </div>
</div>
  • container:包裹整个沙漏,调整整个沙漏的定位
  • hourglass:沙漏的主容器
  • frame:沙漏的外框,一个木架子

  • sand-top-strokesand-bottom-stroke:存放沙子的玻璃容器

  • sand-topsand-bottom:上下部分的沙子

  • sand-flow:连接上下两部分沙漏的管道

  • sand-drop:滴落的沙子

  • glass-reflection:添加一个玻璃反光效果

css

通用变量

:root {
  --rotateTime: 10s;
}

定义沙漏动画时间,在动画中需要用到。

沙漏翻转

.hourglass {
  position: relative;
  width: 120px;
  height: 200px;
  margin: 0 auto;
  animation: rotate var(--rotateTime) linear infinite;
  transform-origin: center 100px;
}
@keyframes rotate {
  0% {
    transform: rotate(0deg);
  }
  45% {
    transform: rotate(0deg);
  }
  50% {
    transform: rotate(180deg);
  }
  99% {
    transform: rotate(180deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

计时结束(沙漏沙子漏完)后需要将整个沙漏框架翻转,在rotateTime时间内沙漏需要翻转2次,也就是说沙漏漏完一次的时间是rotateTime

沙堆减少和增加

.sand-top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 90px;
  background-color: #f5d76e;
  clip-path: polygon(0 0, 100% 0, 51% 100%, 49% 100%);
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
  animation: sand-top var(--rotateTime) linear infinite;
}
.sand-bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 0;
  background-color: #f5d76e;
  clip-path: polygon(49% 0, 51% 0, 100% 100%, 0 100%);
  animation: sand-bottom var(--rotateTime) linear infinite;
  border-bottom-left-radius: 10px;
  border-bottom-right-radius: 10px;
}
/* 上半部分沙子减少动画 */
@keyframes sand-top {
  0% {
    height: 90px;
    width: 100%;
  }
  49% {
    height: 0;
    width: 0;
    left: 50%;
    top: 90px;
  }
  50% {
    height: 0px;
    width: 100%;
    top: 0;
    left: 0;
  }
  99% {
    width: 100%;
    height: 90px;
  }
  100% {
    width: 100%;
    height: 90px;
  }
}

/* 下半部分沙子增加动画 */
@keyframes sand-bottom {
  0% {
    height: 0;
    width: 100%;
    bottom: 0;
    left: 0;
  }
  50% {
    height: 90px;
    width: 100%;
    bottom: 0;
    left: 0;
  }
  51% {
    height: 90px;
    width: 100%;
    bottom: 0;
    left: 0;
  }
  99% {
    height: 0px;
    width: 0;
    bottom: 90px;
    left: 50%;
  }
  100% {
    height: 0px;
    width: 0;
    bottom: 90px;
    left: 50%;
  }
}

上半部分沙堆需要先减少后增加,下半部分沙堆需要先增加后减少;通过改变高度、宽度和位置,模拟沙子流动的过程。

管道沙子滴落

@keyframes sand-drop {
  0% {
    opacity: 1;
    transform: translate(-50%, 0);
  }
  4.9%,
  9.9%,
  14.9%,
  ……,
  44.9%,
  49.9% {
    opacity: 0;
    transform: translate(-50%, 15px);
  }
  5%,
  10%,
  15%,
  ……,
  40%,
  45% {
    opacity: 0;
    transform: translate(-50%, 0);
  }
  5.1%,
  10.1%,
  ……,
  90.1%,
  95.1% {
    opacity: 1;
  }
  50%,
  55%,
  ……,
  100% {
    opacity: 0;
    transform: translate(-50%, -15px);
  }
  54.9%,
  59.9%,
  64.9%,
  ……,
  94.9%,
  99.9% {
    opacity: 0;
    transform: translate(-50%, -30px);
  }
}

通过控制透明度和位移,模拟沙子滴落的连贯动作,每5%的动画时间完成一个滴落动作,以10s为例的话也就是每0.5s完成一个滴落动作;因为沙子漏完之后整个沙漏需要翻转180°,所以沙子滴落的动画应该分为前后两段,前半段是从上往下滴( transform: translate(-50%, 15px) ),后半段是从下往上滴( transform: translate(-50%, -15px) )。

源码

gitee

gitee.com/zheng_yongt…

github

github.com/yongtaozhen…


  • 🌟 觉得有帮助的可以点个 star~
  • 🖊 有什么问题或错误可以指出,欢迎 pr~
  • 📬 有什么想要实现的功能或想法可以联系我~

公众号

关注公众号『 前端也能这么有趣 』,获取更多有趣内容。

发送 加群 还可以加入群聊,一起来学习(摸鱼)吧~

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

❌
❌