我们在做什么?
您可能已经看到过一些这样的加载器,它们主要出现在 Flash 网站上。 它基本上是一个饼图,它越来越大,最终变成一个完整的圆圈。
起初,我认为这将非常容易,只需创建一个圆圈,让它旋转,在遮罩后面隐藏一部分,就完成了。 然而,结果证明这比想象的要困难得多。 事实上,即使使用像 Sass 和 Compass 这样的预处理器,CSS 也无法完成这样的任务。 当涉及到制作形状时,我们总是会遇到困难,更不用说样式化或动画化这些形状了。 很多时候,我们可以找到解决方法,并让它运行起来,但代价是可维护性或语义性。
为什么要这样做?
我认为最常见的用例是计时器。 但这些概念也可以用于仅用纯 CSS 制作饼图。 如果您是
即使有很多很棒的工具可以管理饼图(主要使用 JavaScript),我们可能可以轻松地想出如何仅用 CSS 制作饼图,甚至使用这样的技巧来制作这些饼图的动画。
Atomic Noggin Enterprise 网站上有一篇关于使用 clip 属性制作 CSS 饼图的教程。
好吧,这是一个语义不好的解决方法! 但是可维护性还不错,所以我们开始吧。
HTML 代码
我们需要 3 个不同的元素
- 一个旋转器: 这是在整个过程中旋转的半圆
- 一个遮罩: 这是在动画的前 50% 期间隐藏旋转器的元素
- 一个填充器: 这是在动画的后 50% 期间完成圆圈的元素
我们需要将所有这些元素放在同一个父元素中,以便允许绝对定位
<div class="wrapper">
<div class="pie spinner"></div>
<div class="pie filler"></div>
<div class="mask"></div>
</div>
由于旋转器和填充器是同一个圆圈的两个半部分,我们使用一个共享的类(.pie
)来为它们设置样式。
为了保持本文代码的简洁和易懂,我们不会添加任何供应商前缀。
父元素为计时器设置大小和绝对定位上下文
.wrapper {
width: 250px;
height: 250px;
position: relative;
background: white;
}
确保宽度和高度相等,以便制作圆圈,并确保整个过程正常工作。
“旋转器”和“填充器”共享此 CSS 代码
.pie {
width: 50%;
height: 100%;
position: absolute;
background: #08C;
border: 10px solid rgba(0,0,0,0.4);
}
它们的宽度等于父元素宽度的 50%,因为它们都是同一个圆圈的一部分,它们的高度与父元素高度相同。 我们还为它们添加了一些颜色和边框,以便正确地识别它们。
“旋转器”
.spinner {
border-radius: 125px 0 0 125px;
z-index: 200;
border-right: none;
animation: rota 10s linear infinite;
}
我们必须让它看起来像一个半圆,在左上角和左下角使用圆角。 此外,我们为它提供了一个较高的正z-index
,以便将它放在填充器上面,但在遮罩后面。
然后我们添加animation
,时长为 10 秒。 我们稍后将详细讨论动画。
“填充器”
.filler {
border-radius: 0 125px 125px 0;
z-index: 100;
border-left: none;
animation: fill 10s steps(1, end) infinite;
left: 50%;
opacity: 0;
}
对于旋转器,我们设置了border-radius
和 z-index
,删除了border-left
,并将animation
时长设置为 10 秒。 对于此元素,animation-timing-function
并非设置为linear
,而是设置为steps(1, end)
。 这意味着animation
不会从 0% 到 100% 逐步进行,而是一次性完成。
由于填充器在动画的前半部分不可见,我们将它的不透明度设置为 0,并将它的位置设置为父元素宽度的 50%。
“遮罩”
.mask {
width: 50%;
height: 100%;
position: absolute;
z-index: 300;
opacity: 1;
background: inherit;
animation: mask 10s steps(1, end) infinite;
}
遮罩从动画开始就存在,所以它的不透明度设置为 1,它的背景从父元素的背景颜色继承(使其不可见)。 为了覆盖旋转器,它与旋转器具有相同的尺寸,它的z-index
设置为 300。
关键帧
@keyframes rota {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes fill {
0% { opacity: 0; }
50%, 100% { opacity: 1; }
}
@keyframes mask {
0% { opacity: 1; }
50%, 100% { opacity: 0; }
}
第一个动画(rota
)是为旋转器准备的。 它在 10 秒内从 0 度到 360 度逐步旋转。
第二个动画(fill
)是为填充器准备的。 它在 5 秒后立即从 0 不透明度到 1 不透明度。
最后一个动画(mask
)是为遮罩准备的。 它在 5 秒后立即从 1 不透明度到 0 不透明度。
所以动画看起来像这样
- T0 – 旋转器在左侧,被遮罩隐藏。 填充器被隐藏。
- T1 – 旋转器开始顺时针旋转,并逐渐从遮罩后面出现。
- T2 – 旋转器旋转了 360/10*2 = 72 度,并继续旋转。
- T3 – 旋转器旋转了 360/10*3 = 108 度,并继续旋转。
- T4 – 旋转器旋转了 360/10*4 = 144 度,并继续旋转。
- T5 – 旋转器旋转了 360/10*5 = 180 度,并继续旋转。 在此时,填充器立即变为 100% 不透明度,而遮罩消失。
- T6 – 旋转器旋转了 360/10*6 = 216 度,并继续旋转。
- T7 – 旋转器旋转了 360/10*7 = 252 度,并继续旋转。
- T8 – 旋转器旋转了 360/10*8 = 288 度,并继续旋转。
- T9 – 旋转器旋转了 360/10*9 = 324 度,并继续旋转。
- T10 – 旋转器旋转了 360 度,回到起点。 然后我们重新开始动画。 遮罩变为 100% 不透明度,而填充器消失。
奖励
以下是一些额外的技巧,它们可能会非常酷,具体取决于您的需求。
悬停暂停
.wrapper:hover .filler,
.wrapper:hover .spinner,
.wrapper:hover .mask {
animation-play-state: paused;
}
使用此代码片段,您可以通过将鼠标悬停在父元素上,来使整个动画暂停。
内部内容
借助z-index
,我们可以轻松地在旋转器内部添加一些内容,并让它以相同的方式旋转。 尝试将以下代码片段添加到您的代码中
.spinner:after {
content: "";
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
top: 10px;
right: 10px;
background: #fff;
border: 1px solid rgba(0,0,0,0.4);
box-shadow: inset 0 0 3px rgba(0,0,0,0.2);
}
预处理器或 CSS 变量
目前,维护起来并不容易。但如果我们使用变量(预处理器或即将推出的原生 CSS 变量),我们可以更容易地维护。例如,您可以添加一个变量来管理持续时间,而无需在 3 个动画声明中进行更改。
如果您想在不使用预处理器的情况下简化可维护性,我猜您可以创建一个只处理动画持续时间的类,并将该类添加到 3 个子元素中。它看起来像这样
.animation-duration {
animation-duration: 10s;
}
缺点
遗憾的是,这种技术无法做到一些事情。
- 不支持渐变(看起来很糟糕)
- 不支持阴影(看起来很脏)
- 不完全响应。如果更改父元素的尺寸,除了边框半径之外,所有内容都会正常。您仍然需要手动更改边框半径值,因为除非我们处理正方形,否则我们无法设置 50% 的边框半径。
- 不是语义化的(一个动画有 4 个元素)。即将推出的多个伪元素可能会解决这个问题,或者使用 Web Components。
浏览器支持
由于我们使用的是 CSS 动画和关键帧,因此浏览器支持率很低,但会随着时间的推移而提高。目前,唯一支持 CSS 动画的浏览器是
- Internet Explorer 10
- Firefox 12+
- Chrome
- Safari 5+
- Opera 12+
- iOS Safari 3+
- Android 2+(在 v4 之前存在 bug)
演示
虽然这作为 CSS 能力的演示非常酷,但我又一次不得不问,为什么不使用 SVG?
做一个演示!
Codepen:SVG 饼图计时器
基于由 Lars Gunther 发布的 HSL 旋转器。
实际上,渐变 **确实** 可以工作,但在某些情况下。
边框不能透明(如演示中所示),并且渐变必须是径向渐变(当然)。
此外,
.filler
上的渐变必须左对齐(left center
),.spinner
上的渐变必须右对齐(right center
)。此外,如果您稍微调整动画,其他内容(甚至可能是阴影)也应该可以工作(不透明度必须立即更改,这意味着
或者类似的东西……)
我很快就会做一个演示 :)
现在我有时间制作 演示 了。
它演示了渐变和其他一些内容,您自己看看吧!
嘿,Felix,你的作品真的很棒,我很喜欢!
正如我在文章中所说,我已经知道我们可以在上面放置一些内容(指的是“点击我”伪元素),并且通过修复边框半径,我也知道它可以完全响应。顺便说一下,我们可以使用
transform: scale()
属性来放大或缩小它。;)但你的渐变作品看起来很棒!我肯定需要深入研究径向渐变。无论如何,感谢你制作了这样一个演示。:)
嗨!感谢 Chris,能够在 CSS Tricks 上看到我的作品真是荣幸。:)
感谢 Joshua Hibbert,我终于找到了如何解决边框半径的响应式问题。
没错,我们无法做到这一点。
但是,我们可以使用“复杂”的边框半径属性来实现它,只需执行以下操作
这样,如果我们想让整个加载器变大或变小,就不必更改边框半径值。:)
@Felix Kiunke:我很想看看你是如何处理径向渐变的。如果你制作了演示,请告诉我。:)
要使不透明度更改立即生效,您可能想使用
steps(1,start/end)
,我认为它效果很好!Hugo,写得很棒!
一个纯 CSS 旋转器:http://codepen.io/tmyg/pen/bwLom
目前仅限 Webkit,可能可以在 Firefox 中运行。
剧透警报……:)
以防你想在 Hugo 的(精彩!)讲解中找到一个可视化的东西,看看这里发生了什么。更改几个背景颜色就可以了。
http://codepen.io/anon/pen/tBryp
Hugo,干得好!
可能这在上面帖子中显示过,但由于某种原因,帖子的一部分给了我一个“抱歉,出现了一些错误”的错误。可能是因为我在工作笔记本电脑上。
感谢 Keith!
这使得理解所有内容的运作方式变得容易得多。使用的技术非常酷。
Chris,虽然不太一样,而且由于我缺乏设计技巧,所以并不那么漂亮,但是看看 这个基于 SVG 的演示 中的内部圆圈。
我还没有被 CSS3 动画说服。我喜欢我们能够做到这一点,但 IMO 存在太多负面因素。
浏览器支持不足显然是最主要的问题,但作为一名开发人员而不是设计师,我发现基于 Jquery 的动画包更易于使用和交互。
我认为这是一种很棒的方式,我只是无法为做一些用 JQ 几行代码就能完成的事情而花费这么多 CSS 代码。
顺便说一句,这篇文章很棒:)
我完全同意你的观点。这纯粹是为了乐趣(和挑战)。:)
Rich,我完全同意,如果你想在生产网站上实际使用,应该把类似的事情留给 Javascript 和 jQuery。但作为找出可能性的演示,这令人印象深刻。
不过,对于较小的事情来说,CSS 动画非常完美。它们降级良好,是您必须完成的样式的一部分,并且您不必担心可访问性推动者,因为无论您做什么,它都不依赖于 javascript,并且仍然可以被屏幕阅读器等看到。
对于简单的动画来说,它也绝对不长!如果有什么区别的话,它更短!
例如,要将悬停时导航按钮的背景淡化为更深的颜色,使用以下 HTML
使用 CSS 动画
使用 jQuery
如果您的浏览器不支持,该按钮将使用 CSS 动画从一个状态瞬间变为另一个状态。如果您使用的是不支持的浏览器并且使用 jQuery,则什么也不会发生!
它确实有它的优势,像这样的小细节正是它的用途!
您忘记了 CSSS 动画速度更快 http://jsperf.com/jquery-animate-vs-css - 我切换到 CSS 的主要变革是在移动设备上 jQuery 导致严重滞后时。
看起来不错。但我的 CPU 可能更热了。它消耗了大约 10%。
这是一个很棒的小教程,感谢分享。
Hugo 总是领先我两步。我做了一个 透明递减器(递减旋转器),我正在努力解决其中的问题。
如果您还没有查看 CodePen,请务必查看。它目前处于测试阶段,所以别忘了提供您的支持。
略有不同,但同样有效,
http://codepen.io/anon/pen/ItEni
Ludvig Lindblom 做了一个略有不同但非常巧妙的版本,允许在圆圈上使用线性渐变:http://codepen.io/ludviglindblom/pen/dfjAt.
实际上,他让遮罩旋转而不是旋转器,并在动画的后半部分玩弄 z 索引和不透明度。
一定要看看它,它也非常棒。;)
不要将此与 CSS3 PIE 项目混淆。谷歌错误地把我带到这里了!
我最近发现了一种方法可以删除三个动画中的一个来制作饼图计时器。
animation-direction 属性允许我们控制动画播放的方式:向前或向后。得益于此,我们不再需要两个动画来控制不透明度:只需一个,以及反转值。
根据 MDN
因此,我使用从 1 到 0 的不透明度动画,而不是使用从 0 到 1 的不透明度动画,并将其反向播放。因此,我删除了“填充”动画,并将此设置到 .fill 元素
查看演示以了解其工作方式:http://codepen.io/HugoGiraudel/pen/BHEwo
永远是工匠。
当嵌入到此页面时,此演示在 Chrome 中不起作用。
使用一点 jQuery,我在这里创建了一个灵活的饼图倒计时器(您可以更改总时间)。希望对您有用 :D
http://codepen.io/elaelation/pen/CxgmH?editors=001