11.18「宝可梦朱·紫」正式发售,我才记起还没在 Switch 上玩过「宝可梦剑·盾」,赶紧趁着双十一,在某宝上下单了一张卡带,为双十一做出了一点微博的贡献。
(资料图)
到手才发现,买的是二手卡带。但没关系,价格减半,快乐加倍,一个周末都忙着在旷野地带奔跑,几乎要练成十里坡剑神。
恰巧最近在看 CSS3 相关的内容,对游戏里的 Loading 过场动画产生了兴趣,想着能不能用 CSS3 来实现下面这个效果。
实现效果:
要实现这个效果,第一步先要做一个静态的精灵球。精灵球图案不复杂,拆分下来就是几个图层背景的叠加:
第一层是线性渐变背景,上半部分 46% 的面积是红色(色值:#cb0905),中间 8% 的部分是黑色(色值:#000000),下半部分 50% 的面积是白色(色值:#ffffff)第二层是径向渐变背景,中间是占 15% 半径的白色圆,接着是一个占 7% 半径的黑色圆环,剩下的部分设为透明转化成代码:
.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 设置成了一个巨大值。
有了一个静态的精灵球,让它动起来还不容易?回过头再看下动画效果:
精灵球整体绕着圆心在做 360°旋转精灵球的上下两部分,分别顺时针作绘制扇形的处理,先从头到尾将扇形从 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); }}
第二步动画就比较棘手了:
静态的精灵球使用的是线性渐变实现,而不是使用扇形实现而且 CSS 没提供绘制扇形的 APICSS 的帧动画在linear-gradient
属性上不支持插帧,表现就是跳变既然不行,那只能换个思路,毕竟计算机视觉是一门欺骗的艺术,重新拆解一下:
扇形可通过叠加两层元素实现:
- 下面一层是真实层,显示我们想要的颜色(比如红色)- 上面一层是遮盖层,用背景色相同的颜色
当遮盖层相对于右下角旋转时,看起来的效果就像是在绘制圆的左上部分的扇形。
同理,相对于左下角/右上角/左上角旋转时,看起来的效果就像是在绘制圆的右上/左下/右下部分的扇形。
将左上、左下、右上、右下组合起来,再通过动画配置,就能变相实现扇形绘制的效果。
/* 精灵球 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); }}
最终效果:
X 关闭