CSS3 实现宝可梦剑盾精灵球 Loading 效果(带源码)

缘起

11.18「宝可梦朱·紫」正式发售,我才记起还没在 Switch 上玩过「宝可梦剑·盾」,赶紧趁着双十一,在某宝上下单了一张卡带,为双十一做出了一点微博的贡献。

到手才发现,买的是二手卡带。但没关系,价格减半,快乐加倍,一个周末都忙着在旷野地带奔跑,几乎要练成十里坡剑神。

恰巧最近在看 CSS3 相关的内容,对游戏里的 Loading 过场动画产生了兴趣,想着能不能用 CSS3 来实现下面这个效果。

动画长这样

实现效果:

最终效果

做静态的一个精灵球

要实现这个效果,第一步先要做一个静态的精灵球。精灵球图案不复杂,拆分下来就是几个图层背景的叠加:

  1. 第一层是线性渐变背景,上半部分 46% 的面积是红色(色值:#cb0905),中间 8% 的部分是黑色(色值:#000000),下半部分 50% 的面积是白色(色值:#ffffff)
  2. 第二层是径向渐变背景,中间是占 15% 半径的白色圆,接着是一个占 7% 半径的黑色圆环,剩下的部分设为透明

转化成代码:

<!-- 精灵球元素 -->
<div class="pkm_ball_bg"></div>
.pkm_ball_bg {
  /* 设置大小 */ 
  width: 400px;
  height: 400px;
  /* 设置背景 */
  background: 
    radial-gradient(white 15%, black 15%, black 22%, transparent 22%),
    linear-gradient(#cb0905 46%, #000000 46%, #000000 54%, #ffffff 54%);
  /* 设置为圆形 */
  border-radius: 9999px;
}

这样就得到一个方形的精灵球。

宝可梦球

可以看到虽然使用了渐变背景,但图案上并没有渐变效果,这里用了一个小技巧:在同一个位置同时设置两个颜色,达到颜色跳变的效果。

如:#cb0905 46%, #000000 46%,表示在 46%的位置从红色变化到黑色,由于渐变距离为 0 ,表现出来就是颜色跳变的效果。

另外这里需要注意一点:

  • 在 HTML 里,元素重叠时,后书写的元素会覆盖在前面书写的元素上。
  • 但使用 background 属性叠加多层背景时,图层的放置顺序则是相反的,从顶到底覆盖,类似栈结构,先书写的背景层在上层,后书写的背景层在下层。

形状的调整是通过设置圆角来实现:border-radius: 9999px;,简单起见,参考 tailwind css 设置成了一个巨大值。

如何让它动起来

有了一个静态的精灵球,让它动起来还不容易?回过头再看下动画效果:

  1. 精灵球整体绕着圆心在做 360°旋转
  2. 精灵球的上下两部分,分别顺时针作绘制扇形的处理,先从头到尾将扇形从 0 绘制到 180°,再从尾到头将扇形绘制从 180° 绘制到 0°。

整体绕圆心做旋转,这一步好实现:

.pkm_ball_bg {
  /* 设置整体绕着中心旋转 */
  transform-origin: center;
  animation: rotate-clockwise-360 2s linear infinite;
}

/* 顺时针 360° 旋转的动画 */
@keyframes rotate-clockwise-360 {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

第二步动画就比较棘手了:

  1. 静态的精灵球使用的是线性渐变实现,而不是使用扇形实现
  2. 而且 CSS 没提供绘制扇形的 API
  3. CSS 的帧动画在 linear-gradient 属性上不支持插帧,表现就是跳变

既然不行,那只能换个思路,毕竟计算机视觉是一门欺骗的艺术,重新拆解一下:

扇形可通过叠加两层元素实现:

- 下面一层是真实层,显示我们想要的颜色(比如红色)
- 上面一层是遮盖层,用背景色相同的颜色

当遮盖层相对于右下角旋转时,看起来的效果就像是在绘制圆的左上部分的扇形。

同理,相对于左下角/右上角/左上角旋转时,看起来的效果就像是在绘制圆的右上/左下/右下部分的扇形。

将左上、左下、右上、右下组合起来,再通过动画配置,就能变相实现扇形绘制的效果。

遮盖实现

重新绘制一个精灵球

<div class="pkm_ball_loading">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>
/* 精灵球 loading 根元素样式 */
.pkm_ball_loading {
  width: 400px;
  height: 400px;
  /* 直接设置背景,而不设置 overflow: hidden 避免裁切边缘有残留颜色 */
  background: linear-gradient(#cb0905 50%, #ffffff 50%);
  /* 裁剪为圆形 */
  border-radius: 9999px;
  position: relative;
  /* 网格布局,一行 2 个元素,元素宽度为布局的一半宽度 */
  display: grid;
  grid-template-columns: auto auto;
  /* 行间距,模拟中间的黑色横条 */
  grid-row-gap: 6%;
  grid-column-gap: 0%;
}

/* 精灵球的开关和中间黑色条单独成一层,作为最上层盖住 */
.pkm_ball_loading::after {
  content: "";
  width: 100%;
  height: 100%;
  position: absolute;
  background: 
    radial-gradient(white 15%, black 15%, black 22%, transparent 22%),
    linear-gradient(transparent 46%, #000000 46%, #000000 54%, transparent 54%);
}

/* 布局中的每个元素的公共属性 */
.pkm_ball_loading > div {
  width: 100%;
  height: 100%;
  /* 避免子元素旋转后超出父布局 */
  overflow: hidden;
  /* 设置布局,不然伪类不生效 */
  display: flex;
  /* 设置定位,作为内部子元素的定位基点 */
  position: relative;
}

.pkm_ball_loading > div::before {
  content: "";
  /* 宽高设为 2 倍,确保旋转的时候完全遮盖 */
  width: 200%;
  height: 200%;
  /* 遮盖层的颜色 */
  background: #000000;
}

/* 网格布局第一个元素,即左上 */
.pkm_ball_loading > div:nth-child(1)::before {
  /* 定位设为放在右下,和旋转点一致 */
  position: absolute;
  bottom: 0;
  right: 0;
  /* 设置选择点为右下 */
  transform-origin: bottom right;
  /* 逆时针旋转 45° */
  transform: rotate(-45deg);
}

/* 网格布局第一个元素,即右上 */
.pkm_ball_loading > div:nth-child(2)::before {
  /* 定位设为放在左下,和旋转点一致 */
  position: absolute;
  bottom: 0;
  left: 0;
  /* 设置选择点为右下 */
  transform-origin: bottom left;
  /* 顺时针旋转 45° */
  transform: rotate(45deg);
}

/* 网格布局第一个元素,即左下 */
.pkm_ball_loading > div:nth-child(3)::before {
  /* 定位设为放在右上,和旋转点一致 */
  position: absolute;
  top: 0;
  right: 0;
  /* 设置选择点为右下 */
  transform-origin: top right;
  /* 顺时针旋转 45° */
  transform: rotate(45deg);
}

/* 网格布局第一个元素,即右下 */
.pkm_ball_loading > div:nth-child(4)::before {
  /* 定位设为放在左上,和旋转点一致 */
  position: absolute;
  top: 0;
  left: 0;
  /* 设置选择点为右下 */
  transform-origin: top left;
  /* 逆时针旋转 45° */
  transform: rotate(-45deg);
}

/* loading 整体 360°旋转 */
.pkm_ball_loading {
  animation: rotate-clockwise-360 2s linear infinite;
}

/* 第一第四个遮盖物,先逆时针旋转90°,再顺时针旋转90° */
.pkm_ball_loading > div:nth-child(1)::before,
.pkm_ball_loading > div:nth-child(4)::before {
  animation: rotate-anticlockwise-90 2s linear infinite;
}

/* 第一第四个遮盖物,先顺时针旋转90°,再逆时针旋转90° */
.pkm_ball_loading > div:nth-child(2)::before,
.pkm_ball_loading > div:nth-child(3)::before {
  animation: rotate-clockwise-90 2s linear infinite;
}

/* 顺时针 360° 动画 */
@keyframes rotate-clockwise-360 {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* 顺时针 90° 再逆时针 90° 动画 */
@keyframes rotate-clockwise-90 {
  0% {
    transform: rotate(0deg);
  }
  50% {
    transform: rotate(90deg);
  }
  100% {
    transform: rotate(0deg);
  }
}

/* 逆时针 90° 再顺时针 90° 动画 */
@keyframes rotate-anticlockwise-90 {
  0% {
    transform: rotate(0deg);
  }
  50% {
    transform: rotate(-90deg);
  }
  100% {
    transform: rotate(0deg);
  }
}

最终效果:

最终效果

特别鸣谢

  1. 免费在线视频转 GIF 软件:https://www.tutieshi.com/video/
本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
CSS3 实现宝可梦剑盾精灵球 Loading 效果(带源码)
11.18「宝可梦朱·紫」正式发售,我才记起还没在 Switch 上玩过「宝可梦剑·盾」,赶紧趁着双十一,在某宝上下单了一张卡带,为双十一做出了一点微博的贡献。
<<上一篇
下一篇>>