原生js html画坐标轴,关于如何使用原生HTML + JS + CSS绘制简单折线柱状图

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

正文

先上结果图:

6a7ea50c2be721f8a55cfd0079b52c51.png

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

1. 如何实现以下效果:

c2328a8678a14685ab3d7439ba757710.png

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

中间的横线其实很简单,在我之前关于背景渐变的文章里有提到,直接 上代码:

.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;

}

复制代码

难点在于如何实现侧边栏的数字排列? 先上结论:

从0 - 100其实是在一个盒子里,这个盒子的高度应该是由line-height来确定的

由于line-height具有垂直居中的特点,只要让line boxes排在左边就好了

利用absolute定位来定位该盒子

再上代码:

.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;

}

复制代码

最后解释: 我们应该让每一个元素占据一行,且这一行的高度和背景横线之间的间距相等然后让其中的文字居中显示,这样就有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的中线处于同一高度,即数字被横线纵向对半分割。

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

单个柱状图怎么绘制

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

29a727da7dd071cad40f57838982a60b.png

几个点注意一下:

如何再底部显示月份?

如何绘制中间的圆点?

直接上代码:

.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;

}

复制代码

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

content: attr(data-month)'月';

复制代码

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

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

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

需要实现下面的效果:

ccc704a1060469cd1ccedd8e7c165797.png

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

总结关键点:

动态计算出两点之间的距离

计算出需要偏转的角度

利用transform:rotate()来实现旋转

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

.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;

}

复制代码

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 中,欢迎指正学习。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,作为一名人工智能,我无法绘制复杂的形和像。但是,我可以提供一些网上已有的资源供您参考。 以下是一个使用原生 JavaScript、HTMLCSS 实现的简单的千里江山示例: ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>千里江山</title> <style> canvas { border: 1px solid #000; } </style> </head> <body> <canvas id="myCanvas" width="800" height="400"></canvas> <script> var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); // 绘制背景 ctx.fillStyle = "#b0e0e6"; ctx.fillRect(0, 0, canvas.width, canvas.height); // 绘制大地 ctx.fillStyle = "#9acd32"; ctx.fillRect(20, 20, 760, 360); // 绘制山 ctx.fillStyle = "#a0522d"; ctx.beginPath(); ctx.moveTo(200, 280); ctx.lineTo(220, 250); ctx.lineTo(240, 280); ctx.closePath(); ctx.fill(); ctx.fillStyle = "#a0522d"; ctx.beginPath(); ctx.moveTo(400, 240); ctx.lineTo(420, 210); ctx.lineTo(440, 240); ctx.closePath(); ctx.fill(); ctx.fillStyle = "#a0522d"; ctx.beginPath(); ctx.moveTo(650, 280); ctx.lineTo(670, 250); ctx.lineTo(690, 280); ctx.closePath(); ctx.fill(); // 绘制水 ctx.fillStyle = "#add8e6"; ctx.fillRect(20, 240, 760, 140); // 绘制小船 ctx.fillStyle = "#ffa07a"; ctx.beginPath(); ctx.moveTo(350, 280); ctx.lineTo(365, 280); ctx.lineTo(365, 270); ctx.lineTo(380, 270); ctx.lineTo(380, 280); ctx.lineTo(395, 280); ctx.lineTo(395, 290); ctx.lineTo(350, 290); ctx.closePath(); ctx.fill(); </script> </body> </html> ``` 该示例代码使用 Canvas 元素绘制了一个简单的千里江山,包括大地、山和水,并在水中添加了一艘小船。您可以根据自己的需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值