旋转的沙漏ui动画特效

纯css3绘制卡通沙漏,当沙漏滴完,自动旋转沙漏动画特效。
在这里插入图片描述

<body>
<div class="title">
    Give it a minute...
</div>
<div class="container">
    <div class="hourglass">
        <div class="top"></div>
        <div class="bottom">
            <div class="bottom_sand"></div>
        </div>
        <div class="mid"></div>
        <div class="drops">
            <div class="drop"></div>
            <div class="drop"></div>
            <div class="drop"></div>
            <div class="drop"></div>
            <div class="drop"></div>
            <div class="drop"></div>
            <div class="drop"></div>
            <div class="drop"></div>
            <div class="drop"></div>
            <div class="drop"></div>
        </div>
        <div class="woods">
            <div class="wood"></div>
            <div class="wood"></div>
        </div>
        <div class="glares">
            <div class="glare"></div>
            <div class="glare"></div>
            <div class="glare"></div>
            <div class="glare"></div>
        </div>
    </div>
</div>
</body>
* {
  padding: 0;
  margin: 0 auto;
  box-sizing: border-box;
}

body {
  font-family: 'Yellowtail', cursive;
  background-color: #eee;
  color: #777;
  min-height: 100vh;
  display: -webkit-box;
  display: flex;
  -webkit-box-pack: center;
          justify-content: center;
  -webkit-box-align: center;
          align-items: center;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
          flex-direction: column;
}

.title {
  font-size: 24px;
  width: 420px;
  text-align: left;
}

.container {
  position: relative;
  width: 420px;
  height: 420px;
  border: 2px solid #fff;
  border-radius: 4px;
}

.hourglass {
  position: relative;
  width: 100;
  height: 100%;
  -webkit-animation: rotateGlass 60s ease-in-out infinite;
          animation: rotateGlass 60s ease-in-out infinite;
}
@-webkit-keyframes rotateGlass {
  0%, 90% {
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
    -webkit-filter: drop-shadow(2px 4px 8px rgba(0, 0, 0, 0.15));
            filter: drop-shadow(2px 4px 8px rgba(0, 0, 0, 0.15));
  }
  100% {
    -webkit-transform: rotate(180deg);
            transform: rotate(180deg);
    -webkit-filter: drop-shadow(-2px -4px 8px rgba(0, 0, 0, 0.15));
            filter: drop-shadow(-2px -4px 8px rgba(0, 0, 0, 0.15));
  }
}
@keyframes rotateGlass {
  0%, 90% {
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
    -webkit-filter: drop-shadow(2px 4px 8px rgba(0, 0, 0, 0.15));
            filter: drop-shadow(2px 4px 8px rgba(0, 0, 0, 0.15));
  }
  100% {
    -webkit-transform: rotate(180deg);
            transform: rotate(180deg);
    -webkit-filter: drop-shadow(-2px -4px 8px rgba(0, 0, 0, 0.15));
            filter: drop-shadow(-2px -4px 8px rgba(0, 0, 0, 0.15));
  }
}
.wood {
  position: absolute;
  left: 50%;
  -webkit-transform: translateX(-50%);
          transform: translateX(-50%);
  background-color: saddlebrown;
  width: 220px;
  height: 30px;
  border-radius: 5px;
  background-image: linear-gradient(120deg, rgba(255, 255, 255, 0.25), rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.25));
  background-size: 200%, 100%;
  -webkit-animation: woodGlr 60s infinite linear;
          animation: woodGlr 60s infinite linear;
}
.wood:nth-child(1) {
  top: 20px;
}
.wood:nth-child(2) {
  bottom: 20px;
}
@-webkit-keyframes woodGlr {
  0%, 90% {
    background-position: 0%;
  }
  100% {
    background-position: 100%;
  }
}
@keyframes woodGlr {
  0%, 90% {
    background-position: 0%;
  }
  100% {
    background-position: 100%;
  }
}
.top {
  position: absolute;
  bottom: 50%;
  left: 50%;
  width: 200px;
  height: 110px;
  border: 2px solid saddlebrown;
  border-radius: 20px 20px 200px 200px;
  -webkit-transform: translateX(-50%) scale(1, 1.5);
          transform: translateX(-50%) scale(1, 1.5);
  -webkit-transform-origin: bottom;
          transform-origin: bottom;
  overflow: hidden;
}
.top::before {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: sandybrown;
  -webkit-animation: sandHeightTop 60s linear infinite;
          animation: sandHeightTop 60s linear infinite;
  -webkit-filter: blur(3px);
          filter: blur(3px);
}
@-webkit-keyframes sandHeightTop {
  0% {
    height: 100%;
  }
  90%, 100% {
    height: 0%;
  }
}
@keyframes sandHeightTop {
  0% {
    height: 100%;
  }
  90%, 100% {
    height: 0%;
  }
}
.bottom {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200px;
  height: 110px;
  border: 2px solid saddlebrown;
  border-radius: 200px 200px 20px 20px;
  -webkit-transform: translateX(-50%) scale(1, 1.5);
          transform: translateX(-50%) scale(1, 1.5);
  -webkit-transform-origin: top;
          transform-origin: top;
  overflow: hidden;
}
.bottom_sand {
  position: absolute;
  top: 100%;
  left: 0%;
  width: 100%;
  height: 100%;
  background-color: sandybrown;
  -webkit-transform: scale(3, 1);
          transform: scale(3, 1);
  -webkit-animation: sandTop 60s linear infinite;
          animation: sandTop 60s linear infinite;
}
.bottom_sand::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 200px;
  height: 200px;
  border-radius: 5px;
  background-color: sandybrown;
  -webkit-transform: translateX(-50%) rotate(45deg);
          transform: translateX(-50%) rotate(45deg);
  -webkit-animation: sandAngle 60s linear infinite;
          animation: sandAngle 60s linear infinite;
  -webkit-filter: blur(2px);
          filter: blur(2px);
}
@-webkit-keyframes sandTop {
  0% {
    top: 135%;
  }
  90%, 100% {
    top: 20%;
  }
}
@keyframes sandTop {
  0% {
    top: 135%;
  }
  90%, 100% {
    top: 20%;
  }
}
@-webkit-keyframes sandAngle {
  0% {
    border-radius: 5px;
  }
  90%, 100% {
    border-radius: 50px;
  }
}
@keyframes sandAngle {
  0% {
    border-radius: 5px;
  }
  90%, 100% {
    border-radius: 50px;
  }
}
.mid {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18px;
  height: 6px;
  border: solid saddlebrown;
  border-width: 0 3px;
  -webkit-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
  -webkit-transform-origin: center;
          transform-origin: center;
  background-color: sandybrown;
}
.mid::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
  background-color: sandybrown;
  width: 12px;
  height: 14px;
  border-radius: 50%;
  -webkit-filter: blur(3px);
          filter: blur(3px);
}

.drop {
  position: absolute;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
  background-color: sandybrown;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  -webkit-filter: blur(3px);
          filter: blur(3px);
  --dropx: -50%;
  -webkit-animation: sandDrop 0.6s ease-in infinite, dropVis 60s linear infinite;
          animation: sandDrop 0.6s ease-in infinite, dropVis 60s linear infinite;
}
.drop:nth-child(1) {
  -webkit-animation-delay: 0s, 60s;
          animation-delay: 0s, 60s;
  --dropx: -100%;
}
.drop:nth-child(2) {
  -webkit-animation-delay: 0.06s, 60s;
          animation-delay: 0.06s, 60s;
  --dropx: 30%;
}
.drop:nth-child(3) {
  -webkit-animation-delay: 0.12s, 60s;
          animation-delay: 0.12s, 60s;
  --dropx: -40%;
}
.drop:nth-child(4) {
  -webkit-animation-delay: 0.18s, 60s;
          animation-delay: 0.18s, 60s;
  --dropx: 90%;
}
.drop:nth-child(5) {
  -webkit-animation-delay: 0.24s, 60s;
          animation-delay: 0.24s, 60s;
  --dropx: 20%;
}
.drop:nth-child(6) {
  -webkit-animation-delay: 0.3s, 60s;
          animation-delay: 0.3s, 60s;
  --dropx: -50%;
}
.drop:nth-child(7) {
  -webkit-animation-delay: 0.36s, 60s;
          animation-delay: 0.36s, 60s;
  --dropx: 80%;
}
.drop:nth-child(8) {
  -webkit-animation-delay: 0.42s, 60s;
          animation-delay: 0.42s, 60s;
  --dropx: 10%;
}
.drop:nth-child(9) {
  -webkit-animation-delay: 0.48s, 60s;
          animation-delay: 0.48s, 60s;
  --dropx: -60%;
}
.drop:nth-child(10) {
  -webkit-animation-delay: 0.54s, 60s;
          animation-delay: 0.54s, 60s;
  --dropx: 70%;
}
@-webkit-keyframes sandDrop {
  from {
    top: 50%;
    -webkit-transform: translate(-50%, -50%);
            transform: translate(-50%, -50%);
  }
  to {
    top: calc(50% + 160px);
    -webkit-transform: translate(var(--dropx), -50%) scale(0.25);
            transform: translate(var(--dropx), -50%) scale(0.25);
  }
}
@keyframes sandDrop {
  from {
    top: 50%;
    -webkit-transform: translate(-50%, -50%);
            transform: translate(-50%, -50%);
  }
  to {
    top: calc(50% + 160px);
    -webkit-transform: translate(var(--dropx), -50%) scale(0.25);
            transform: translate(var(--dropx), -50%) scale(0.25);
  }
}
@-webkit-keyframes dropVis {
  0% {
    opacity: 0;
  }
  1%, 100% {
    opacity: 1;
  }
}
@keyframes dropVis {
  0% {
    opacity: 0;
  }
  1%, 100% {
    opacity: 1;
  }
}
.glare {
  position: absolute;
  -webkit-filter: blur(10px);
          filter: blur(10px);
  width: 120px;
  height: 80px;
  border-radius: 50%;
  border-style: solid;
  border-width: 0 0 15px 0;
}
.glare:nth-child(1) {
  top: 19%;
  left: 29%;
  -webkit-transform: rotate(70deg);
          transform: rotate(70deg);
  -webkit-animation: glrClr1 60s infinite linear;
          animation: glrClr1 60s infinite linear;
}
.glare:nth-child(2) {
  bottom: 19%;
  left: 29%;
  -webkit-transform: rotate(110deg);
          transform: rotate(110deg);
  -webkit-animation: glrClr1 60s infinite linear;
          animation: glrClr1 60s infinite linear;
}
.glare:nth-child(3) {
  top: 19%;
  right: 29%;
  -webkit-transform: rotate(-70deg);
          transform: rotate(-70deg);
  -webkit-animation: glrClr2 60s infinite linear;
          animation: glrClr2 60s infinite linear;
}
.glare:nth-child(4) {
  bottom: 19%;
  right: 29%;
  -webkit-transform: rotate(-110deg);
          transform: rotate(-110deg);
  -webkit-animation: glrClr2 60s infinite linear;
          animation: glrClr2 60s infinite linear;
}
@-webkit-keyframes glrClr1 {
  0%, 90% {
    border-color: rgba(255, 255, 255, 0.85);
  }
  100% {
    border-color: rgba(0, 0, 0, 0.15);
  }
}
@keyframes glrClr1 {
  0%, 90% {
    border-color: rgba(255, 255, 255, 0.85);
  }
  100% {
    border-color: rgba(0, 0, 0, 0.15);
  }
}
@-webkit-keyframes glrClr2 {
  0%, 90% {
    border-color: rgba(0, 0, 0, 0.15);
  }
  100% {
    border-color: rgba(255, 255, 255, 0.85);
  }
}
@keyframes glrClr2 {
  0%, 90% {
    border-color: rgba(0, 0, 0, 0.15);
  }
  100% {
    border-color: rgba(255, 255, 255, 0.85);
  }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的沙漏动画自定义控件的实现过程: 1. 创建一个自定义控件类SandClockView,继承自View。 2. 在构造函数中初始化画笔Paint和属性动画AnimatorSet,用于控制沙漏动画的进度。 3. 重写onMeasure方法,确保控件的宽高比为2:3,并且高度不超过屏幕高度的一半。 4. 重写onDraw方法,在画布上绘制两个三角形和一条线段,形成沙漏的形状。 5. 创建方法startAnimation和stopAnimation,分别用于开始和停止沙漏动画。在startAnimation方法中,创建两个ValueAnimator对象,分别控制沙漏上下部分的动画进度,然后将它们添加到AnimatorSet中,设置动画时间和循环次数,并启动动画。 6. 在onDetachedFromWindow方法中停止动画。 以下是完整代码实现: ``` public class SandClockView extends View { private Paint mPaint; private AnimatorSet mAnimatorSet; private float mTopLeftX; private float mTopLeftY; private float mBottomLeftX; private float mBottomLeftY; private float mRightX; private float mTopY; private float mBottomY; private int mWidth; private int mHeight; public SandClockView(Context context) { super(context); init(); } public SandClockView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SandClockView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLACK); mPaint.setStrokeWidth(5); mAnimatorSet = new AnimatorSet(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); // 保证宽高比为2:3 if (width * 3 > height * 2) { width = height * 2 / 3; } else { height = width * 3 / 2; } // 保证高度不超过屏幕高度的一半 int screenHeight = getResources().getDisplayMetrics().heightPixels; if (height > screenHeight / 2) { height = screenHeight / 2; width = height * 2 / 3; } mWidth = width; mHeight = height; setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { float triangleWidth = mWidth / 3f; float triangleHeight = mHeight / 2f; mTopLeftX = (mWidth - triangleWidth) / 2f; mTopLeftY = (mHeight - triangleHeight) / 2f; mBottomLeftX = mTopLeftX; mBottomLeftY = mTopLeftY + triangleHeight; mRightX = mTopLeftX + triangleWidth; mTopY = mTopLeftY; mBottomY = mBottomLeftY; Path path = new Path(); path.moveTo(mTopLeftX, mTopLeftY); path.lineTo(mRightX, mTopY); path.lineTo(mBottomLeftX, mBottomLeftY); path.lineTo(mTopLeftX, mTopLeftY); path.lineTo(mRightX, mBottomY); path.lineTo(mBottomLeftX, mBottomLeftY); canvas.drawPath(path, mPaint); } public void startAnimation() { ValueAnimator topAnimator = ValueAnimator.ofFloat(mTopY, mBottomY); topAnimator.setInterpolator(new LinearInterpolator()); topAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mTopY = (float) animation.getAnimatedValue(); invalidate(); } }); ValueAnimator bottomAnimator = ValueAnimator.ofFloat(mBottomY, mTopY); bottomAnimator.setInterpolator(new LinearInterpolator()); bottomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mBottomY = (float) animation.getAnimatedValue(); invalidate(); } }); mAnimatorSet.play(topAnimator).with(bottomAnimator); mAnimatorSet.setDuration(1000); mAnimatorSet.setStartDelay(500); mAnimatorSet.setRepeatCount(ValueAnimator.INFINITE); mAnimatorSet.start(); } public void stopAnimation() { mAnimatorSet.cancel(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); stopAnimation(); } } ``` 使用时,可以在布局文件中添加一个SandClockView控件,并在代码中调用startAnimation方法开始动画,调用stopAnimation方法停止动画。例如: ``` SandClockView sandClockView = findViewById(R.id.sand_clock_view); sandClockView.startAnimation(); ``` 注意:以上代码只是一个简单的实现,实际应用中可能需要根据具体需求进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值