翻译自 《Animated 3D Bar Chart with CSS3》
首先,咱们看一看要实现的效果: css
这篇文章所实现的动画效果起源于一个小小的想法,这个想法来自于另外一个网站的一篇文章,它介绍了如何在网页中使用CSS、图片和JavaScript建立立体的柱状图。在阅读了那篇文章以后,我想挑战一下,尝试使用纯CSS来实现相同的效果。一开始的难点在于建立一个六面半透明的立方体,然后面的难点在于如何建立一个完整的带有动画效果的3D柱状图。下面,咱们就一块儿来看一下如何解决这些难点。css3
让咱们先列举一些要实现的要求,咱们所实现的柱状图应该是:浏览器
计划是任何项目中最重要的一个部分。因此咱们要先制定一个计划。app
在实际编码以前,我一般会列出项目中我会遇到的潜在挑战和解决这些挑战的方案,而后重复这个过程,直到我获得一个看起来能够执行的策略的东西。下面是我为这个项目提出的挑战列表和解决方案:布局
挑战1 - 带有可伸缩内核的柱子动画
咱们知道:网站
因此,咱们须要:ui
总共有五个div。编码
你可能想知道为何咱们须要两个容器?嗯,这是一个很差解释的问题,但我会尝试着说明清楚。spa
每一个柱体咱们须要至少一个容器(以保证前三个div的位置),因为咱们的柱体内核是可伸缩的,因此咱们使用百分比来操纵内核的高度,这就要求容器的高度等于条形图Y轴的高度。
这看起来很好,可是,有另一个问题,应该有一个选项能够隐藏移动中的内核,这意味着它应该“低于柱体”而且隐藏。你可能会说有一个解决方法 - overflow: hidden,是的,可是它不适用于这里的容器,由于它的高度要比实际的柱体高度短,这就是咱们为何要添加另外一个容器的缘由。
但愿我说清楚了,下面咱们继续。
挑战2 - 坐标轴
坐标轴应该:
因此,咱们须要:
实现
如今咱们有了一个整体的计划,让咱们把它转换成代码。
请注意,文章中的代码没有写浏览器前缀。在实际的项目中请不要省略。
挑战1 - 带有可伸缩内核的柱子
<div class="bar-wrapper">
<div class="bar-container">
<div class="bar-background"></div>
<div class="bar-inner">50</div>
<div class="bar-foreground"></div>
</div>
</div>复制代码
让咱们再次回顾每一个元素的用途:
首先,让咱们设置容器的样式。
/* Bar wrapper容器 - 当内核低于柱体高度时隐藏内核,必需的 */
.bar-wrapper {
overflow: hidden;
}
/* Bar container容器 - 这家伙是柱形图里真正的家长——子元素都是相对于它定位的。 */
.bar-container {
position: relative;
margin-top: 2.5em;
width: 12.5em;
}
/* 右下角的小块 - 确保内核向下滑动时右下角被“切割” */
.bar-container:before {
content: "";
position: absolute;
z-index: 3;
bottom: 0;
right: 0;
width: 0;
height: 0;
border-style: solid;
border-width: 0 0 2.5em 2.5em;
border-color: transparent transparent rgba(183,183,183,1);
}复制代码
请注意,咱们将.bar-container的宽度设置为12.5em。这个数字是柱体的正面和右侧宽度的总和-在咱们的示例中,它是10+2.5=12.5。
咱们还使用border属性来建立三角形,并将其放置在 .bar-container的右下角,以确保内核的侧边在垂直移动时能被“切割”。咱们使用:before伪类来生成这个元素。
下面咱们来设置.bar-background:
/* 背面 */
.bar-background {
width: 10em;
height: 100%;
position: absolute;
top: -2.5em;
left: 2.5em;
z-index: 1;
}
.bar-background:before,
.bar-background:after {
content: "";
position: absolute;
}
/* 底面 */
.bar-background:before {
bottom: -2.5em;
right: 1.25em;
width: 10em;
height: 2.5em;
transform: skew(-45deg);
}
/* 左后面 */
.bar-background:after {
top: 1.25em;
right: 10em;
width: 2.5em;
height: 100%;
/* 仅倾斜Y轴 */
transform: skew(0deg, -45deg);
}复制代码
如你所见,咱们将.bar-background向上和向右移动2.5em。固然,咱们把左后面和底面倾斜45度。请注意,:after伪元素中将第一个倾斜值设置为0deg,第二个设置为-45度,这样只倾斜元素的Y轴。
接着来设置.bar-foreground:
/* 前面 */
.bar-foreground {
z-index: 3; /* 在 .bar-background 和.bar-inner 之上 */
}
.bar-foreground,
.bar-inner {
position: absolute;
width: 10em;
height: 100%;
top: 0;
left: 0;
}
.bar-foreground:before,
.bar-foreground:after,
.bar-inner:before,
.bar-inner:after {
content: "";
position: absolute;
}
/* 右前面 */
.bar-foreground:before,
.bar-inner:before {
top: -1.25em;
right: -2.5em;
width: 2.5em;
height: 100%;
background-color: rgba(160, 160, 160, .27);
transform: skew(0deg, -45deg);
}
/* 前面 */
.bar-foreground:after,
.bar-inner:after {
top: -2.5em;
right: -1.25em;
width: 100%;
height: 2.5em;
background-color: rgba(160, 160, 160, .2);
transform: skew(-45deg);
}复制代码
这里没什么新鲜的,一切都和.bar-background的样式同样,只是方向不一样。
其中,部分的样式同时应用在了.bar-foreground和.bar-inner元素上,由于它们的样子是彻底相同的。
好了,下面咱们继续设置内核的样式。
.bar-inner {
z-index: 2; /* 在.bar-background的上面 */
top: auto; /* 重置 top属性 */
background-color: rgba(5, 62, 123, .6);
height: 0;
bottom: -2.5em;
color: transparent; /* 隐藏文字 */
transition: height 1s linear, bottom 1s linear;
}
/* 右面 */
.bar-inner:before {
background-color: rgba(5, 62, 123, .6);
}
/* 上面 */
.bar-inner:after {
background-color: rgba(47, 83, 122, .7);
}复制代码
好了,柱体的样式就设置好了,接下来咱们来看坐标轴。
挑战2 - 坐标轴
<ul class="graph-container">
<li>
<span>2011</span>
<-- 此处显示柱状图图的HTML标记 -->
</li>
<li>
<span>2012</span>
<-- 此处显示柱状图图的HTML标记 -->
</li>
<li>
<ul class="graph-marker-container">
<li><span>25%</span></li>
<li><span>50%</span></li>
<li><span>75%</span></li>
<li><span>100%</span></li>
</ul>
</li>
</ul>复制代码
如您所见,咱们在项目中使用无序列表和span元素来定位X轴和Y轴标签。
/** 坐标轴容器 **/
.graph-container {
position: relative;
display: inline-block;
padding: 0;
list-style: none; /* 去除列表元素自带的小黑点 */
/* 背景 */
background-image: linear-gradient(left , rgba(255, 255, 255, .3) 100%, transparent 100%);
background-repeat: no-repeat;
background-position: 0 -2.5em;
}复制代码
这里有一个小点,咱们使用线性渐变填充容器背景并将其提高2.5em,为何?由于坐标轴的底端(咱们将在下一个样式中设置)高度是2.5em。并且坐标轴倾斜了45度,因此右下角有一个空白区域。
坐标轴的X轴样式:
/* X轴 */
.graph-container:before {
position: absolute;
content: "";
bottom: 0;
left: -1.25em; /* 倾斜会将它向左推,因此咱们将它向相反的方向移动一点。 */
width: 100%; /* 确保它和整个组件同样宽 */
height: 2.5em;
background-color: rgba(183, 183, 183, 1);
transform: skew(-45deg);
}复制代码
咱们把它倾斜45度,而后向左移动一点,以确保它的位置正确。
坐标轴的Y轴样式:
/* Y轴 */
.graph-container:after {
position: absolute;
content: "";
top: 1.25em; /* 倾斜会将其向上推,所以咱们将其向下移动一点。 */
left: 0em;
width: 2.5em;
background-color: rgba(28, 29, 30, .4);
transform: skew(0deg, -45deg);
}复制代码
这里没什么特别的。同样将元素倾斜45度,而后向下推一点,以便正肯定位。
坐标轴的基本设置就是这些,接下来咱们继续设置列表项里面的样式:
.graph-container > li {
float: left; /* 水平排列 */
position: relative;
}
.graph-container > li:nth-last-child(2) {
margin-right: 2.5em;
}
/* X轴的文字标签 */
.graph-container > li > span {
position: absolute;
left: 0;
bottom: -2em;
width: 80%;
text-align: center;
font-size: 1.5em;
color: rgba(200, 200, 200, .4);
}复制代码
这里有两个要注意的点。首先,咱们使用浮动将柱体水平排列。一般状况下都应该很是当心地使用浮动,它会带来高度塌陷等布局问题。因此,在这里你能够尝试变为设置display:inline-block来实现。
第二,咱们在最后一个柱体上添加了一些右边距。这样咱们就能够确保给坐标轴底部留出足够的空间,试着去掉它,你就会明白个人意思。
OK,咱们就快完成了。最后要作的是添加Y轴的文字标记。
/* 文字标记的容器 */
.graph-container > li:last-child {
width: 100%;
position: absolute;
left: 0;
bottom: 0;
}
/* Y轴文字标记列表 */
.graph-marker-container > li {
position: absolute;
left: -2.5em;
bottom: 0;
width: 100%;
margin-bottom: 2.5em;
list-style: none;
}
/* Y轴线条常规样式 */
.graph-marker-container > li:before,
.graph-marker-container > li:after {
content: "";
position: absolute;
border-style: none none dotted;
border-color: rgba(100, 100, 100, .15);
border-width: 0 0 .15em;
background: rgba(133, 133, 133, .15);
}
/* Y轴侧线 */
.graph-marker-container > li:before {
width: 3.55em;
height: 0;
bottom: -1.22em;
left: -.55em;
z-index: 2;
transform: rotate(-45deg);
}
/* Y轴背景线 */
.graph-marker-container li:after {
width: 100%;
bottom: 0;
left: 2.5em;
}
/* Y轴文本标签 */
.graph-marker-container span {
color: rgba(200, 200, 200, .4);
position: absolute;
top: 1em;
left: -3.5em;
width: 3.5em;
font-size: 1.5em;
}复制代码
如您所见,咱们将文字标记容器的宽度设置为100%,使得背景线可以覆盖整个坐标轴,使用虚线边框设置Y轴线条的样式并定位span元素,使文字标签位于坐标轴的外侧。使用:before和:after伪元素,能够减小HTML的代码量,让页面保持干净。
到这里,咱们已经完成了柱状图的全部样式设置,可是咱们缺乏一些重要的变量——大小、颜色和条形填充值!上面说过咱们的图表是可定制的,因此,我决定不把变量和其余代码混合在一块儿,这样你就能够更方便的自定义它们了。
/**************** * 尺寸 * ****************/
/* 图表的总体大小 */
.graph-container,
.bar-container {
font-size: 8px;
}
/* 柱体的高度 */
.bar-container,
.graph-container:after,
.graph-container > li:last-child {
height: 40em;
}
/**************** * 间距 * ****************/
/* 柱体的间距 */
.graph-container > li .bar-container {
margin-right: 1.5em;
}
/* 第一个柱体的左边距 */
.graph-container > li:first-child {
margin-left: 1.5em;
}
/* 最后一个柱体的右边距 */
.graph-container > li:nth-last-child(2) .bar-container {
margin-right: 1.5em;
}
/**************** * 颜色 * ****************/
/* 柱体的背面颜色 */
.bar-background {
background-color: rgba(160, 160, 160, .1);
}
/* 柱体的底面颜色 */
.bar-background:before {
background-color: rgba(160, 160, 160, .2);
}
/* 柱体的左后面颜色 */
.bar-background:after {
background-color: rgba(160, 160, 160, .05);
}
/* 柱体的正面颜色 */
.bar-foreground {
background-color: rgba(160, 160, 160, .1);
}
/* 内核的颜色 */
.bar-inner,
.bar-inner:before { background-color: rgba(5, 62, 123, .6); }
.bar-inner:after { background-color: rgba(47, 83, 122, .7); }
/************************************* * 内核的高度 * *************************************/
.graph-container > li:nth-child(1) .bar-inner { height: 25%; bottom: 0; }
.graph-container > li:nth-child(2) .bar-inner { height: 50%; bottom: 0; }
.graph-container > li:nth-child(3) .bar-inner { height: 75%; bottom: 0; }复制代码
在下载的源码中,您将没法找到这一部分代码,由于我在那里作了一些更有趣的事情——我使用了单选按钮让您在不修改代码的状况下使用变量。可是,若是您只须要定制一个静态图形,那么就从上面获取代码片断,并根据您的喜爱对其进行定制。
总结
让咱们回顾一下文章中介绍的一些CSS规范/技术。