关于如何使用原生HTML + JS + CSS绘制简单折线柱状图

栏目: CSS · 发布时间: 6年前

内容简介:CSS确实很重要,且有点奇技淫巧,看起来规则十分简单,但是创意更重要,如何用css构造出自己想要的效果,写的代码好看优雅十分重要。 在看了先上结果图:总结下自己觉得关于几点比较难以理解的点:

CSS确实很重要,且有点奇技淫巧,看起来规则十分简单,但是创意更重要,如何用css构造出自己想要的效果,写的代码好看优雅十分重要。 在看了 不借助Echarts等图形框架原生JS快速实现折线图效果 并自己重新实现了以后,实在是感慨CSS的强大之处,并作出记录。

正文

先上结果图:

关于如何使用原生HTML + JS + CSS绘制简单折线柱状图

总结下自己觉得关于几点比较难以理解的点:

1. 如何实现以下效果:

关于如何使用原生HTML + JS + CSS绘制简单折线柱状图

以上是由一个div配合其after伪元素完成的

  • 中间的横线其实很简单,在我之前关于背景渐变的文章里有提到,直接 上代码:
<style>
.chartX {
  width: 670px;
  height: 250px;
  position: relative;
  border-top: 1px solid #ccc;
  margin: 100px auto;
  background: linear-gradient(to top, #ccc 0, #ccc 1px, transparent 1px);
  background-size: 100% 50px;
  font-size: 0;
  text-align: center;
}
</style>
<div class="chartX">
</div>
复制代码
  • 难点在于如何实现侧边栏的数字排列? 先上结论:
  1. 从0 - 100其实是在一个盒子里,这个盒子的高度应该是由line-height来确定的
  2. 由于line-height具有垂直居中的特点,只要让line boxes排在左边就好了
  3. 利用absolute定位来定位该盒子

再上代码:

<style>
.chartX::after {
  content: '100 \a 80 \a 60 \a 40 \a 20 \a 0 ';
  line-height: 50px;
  white-space: pre;
  position: absolute;
  font-size: 12px;
  top: -25px;
  left: -2rem;
  text-align: right;
}
</style>
复制代码

最后解释: 我们应该让每一个元素占据一行,且这一行的高度和背景横线之间的间距相等然后让其中的文字居中显示,这样就有6行文字分别与背景线对齐了。 所以我们要做的第一点就是写出6行文字:即代码中的

content: '100 \a 80 \a 60 \a 40 \a 20 \a 0 ';
white-space: pre;
复制代码

content定义了内容,‘\a' 为换行,同时设置 white-space: pre; 保持文字的空格符和换行,说白了就是让每个数字间换行,于是就有了从上至下排列的 100 80 60 40 20 0这样一列数字。

上一步完成后就需要保证每一行的高度为横线间距相等在本文中即为:50px。怎么做呢?其实在我的之前一篇文章中的关于CSS:line-height中有了答案,在没有height属性下,我们通过line-height来控制盒子的高度,即:

line-height: 50px;
复制代码

这样每一行都是50px的高度,再将盒子整体往上移动25px就做到了使得背景横线与line-height的中线处于同一高度,即数字被横线纵向对半分割。

完成了坐标系的绘制后,应该实现柱状图的绘制

单个柱状图怎么绘制

如何实现下面的这个效果呢?

关于如何使用原生HTML + JS + CSS绘制简单折线柱状图

几个点注意一下:

  • 如何再底部显示月份?
  • 如何绘制中间的圆点?

直接上代码:

<style>
.result-bg {
  display: inline-block;
  position: relative;
  width: calc((100% - 16px * 13) / 12);
  height: 100%;
  background: #eee;
}
.result-bg::after {
  content: attr(data-month)'月';
  font-size: 12px;
  color: grey;
  position: absolute;
  bottom: -1rem;
  left: 0;
  right: 0;
}
.dot {
  border: 2px solid #97cd74;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  position: absolute;
  left: 0;
  right: 0;
  top: 15px;
  margin: auto;
}
</style>
<div class="chartX">
    <div class="result-bg" data-month="1">
      <div class="result-bar" style="height: 82%">
        <div class="dot"></div>
      </div>
    </div>
<div>
复制代码

再来解释: 首先式底部文字的问题: 使用伪元素after的content属性 这里要普及以下,content属性中可以使用attr了,即获取元素的自定义属性值,所以才有了以上代码:

content: attr(data-month)'月';
 <div class="result-bg" data-month="1">
复制代码

配合absolute定位自然绘出了文字

至于中间的点的问题: 画出一个这个样式的点不稀奇,如何让它居中呢?参考我的另一篇文章:关于CSS:关于absolute定位 即让bounding-box的宽度等同于父元素高度,然后我们让圆点的margin为auto自然就居中啦,表现代码如上,不做多余解释,需要读者自行尝试代码。

多个柱状图的圆点间怎么连线

需要实现下面的效果:

关于如何使用原生HTML + JS + CSS绘制简单折线柱状图

这里就真的只能用到js了,因为要手动计算距离和旋转的角度啊

总结关键点:

  1. 动态计算出两点之间的距离
  2. 计算出需要偏转的角度
  3. 利用transform:rotate()来实现旋转

以上都不是难点,需要注意的是,rotate的时候需要以左边端点为中心进行旋转。先上线段的CSS代码:

<style>
.dot i {
  display: inline-block;
  box-sizing: border-box;
  position: absolute;
  left: 50%;
  top: 50%;
  margin-top: -1px;
  height: 2px;
  background: #97cd74;
  border-right: 3px solid #fff;
  border-left: 3px solid #fff;
  transform-origin: left;
  z-index: 1;
}
</style>
<div class="chartX">
    <div class="result-bg" data-month="11">
      <div class="result-bar" style="height: 82%">
        <div class="dot">
            <i></i>
        </div>
      </div>
    </div>
    <div class="result-bg" data-month="12">
      <div class="result-bar" style="height: 62%">
        <div class="dot">
            <i></i>
        </div>
      </div>
    </div>
<div>
复制代码

i标签就是我们的线段啦,然后为线段设置背景颜色即可 其中的 transform-origin:left 即为设置旋转中心点

最后只需要用js来动态的给每个柱子添加i标签,并设置其长度和旋转角度就可以了,代码如下:

const bars = document.querySelectorAll('.result-bar .dot')
bars.forEach((bar, index) => {
  const nextBar = bars[index + 1]
  if (!nextBar) {
    return
  }
  let elLine = bar.querySelector('i')
  if (!elLine) {
    elLine = document.createElement('i')
    elLine.setAttribute('line', '')
    bar.appendChild(elLine)
  }
  // 计算线段长度和旋转弧度
  let boundThis = bar.getBoundingClientRect(),
      boundNext = nextBar.getBoundingClientRect(),
      x1 = boundThis.left,
      y1 = boundThis.top,
      x2 = boundNext.left,
      y2 = boundNext.top,
      distance = Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2)),
      radius = Math.atan((y2 - y1) / (x2 - x1))
  console.log(distance, radius)
  elLine.style.width = `${distance}px`
  elLine.style.transform=`rotate(${radius}rad)`
})
复制代码

至此结束。

结语

以上实现方式真的只是拾人牙慧,能够从张鑫旭前辈的代码中学习到这么多东西真的感到敬畏。 以上完整代码均在 github 中,欢迎指正学习。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java in a Nutshell, 6th Edition

Java in a Nutshell, 6th Edition

Benjamin J Evans、David Flanagan / O'Reilly Media / 2014-10 / USD 59.99

The latest edition of Java in a Nutshell is designed to help experienced Java programmers get the most out of Java 7 and 8, but it's also a learning path for new developers. Chock full of examples tha......一起来看看 《Java in a Nutshell, 6th Edition》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具