d3.js 教程 模仿echarts柱状图
由于最近工作不是很忙,隧由把之前的charts项目用d3.js重写的一下,其实d3.js文档很多,但是入门不是很难,可是想真的能做一个完成的,交互良好的图还是要下一番功夫的。今天在echarts找到了一个柱状图,如图。
模仿了一番,废话不多说。下面就开始我们的代码(注意是D3.v4版本)。
1. js 类
class Bar {
    constructor() {
        this._width = 1000;
        this._height = 700;
        this._padding = 10;
        this._offset = 35;
        this._margins = {right: 40,bottom: 40,left: 40,top: 40};
        this._scaleX = d3.scaleBand().rangeRound([0, this._width - this._margins.left - this._margins.right]);
        this._scaleY = d3.scaleLinear().range([this._height - this._margins.top - this._margins.bottom, 0]);
        this._color = '#3398DB';
        this._data = [];
        this._svg = null;
        this._body = null;
        this._tooltip = null;
        this._shadow = null;
        this._ticks = 5;
        this._key = 'key';
        this._value = 'value';
    }
    render() {
        if(!this._tooltip) {
            this._tooltip = d3.select('body')
            .append('div')
            .style('left', '40px')
            .style('top', '30px')
            .attr('class', 'tooltip')
            .html('');
        }
        if(!this._svg) {
            this._svg = d3.select('body')
                .append('svg')
                .attr('width', this._width)
                .attr('height', this._height)
            this.renderAxes();
            this.renderClipPath();
        }
        this.renderBody();
    }
    renderAxes() {
        let axes = this._svg.append('g')
            .attr('class', 'axes');
        this.renderXAxis(axes);
        this.renderYAxis(axes);
    }
    renderXAxis(axes) {
        let xAxis = d3.axisBottom().scale(this._scaleX)
        axes.append('g')
            .attr('class', 'x axis')
            .attr('transform', `translate(${this.xStart()}, ${this.yStart()})`)
            .call(xAxis)
    }
    renderYAxis(axes) {
        let yAxis = d3.axisLeft().scale(this._scaleY).ticks(this._ticks);
        axes.append('g')
            .attr('class', 'y axis')
            .attr('transform', `translate(${this.xStart()}, ${this.yEnd()})`)
            .call(yAxis)
        d3.selectAll('.y .tick')
            .append('line')
            .attr('class', 'grid-line')
            .attr('x1', 0)
            .attr('y1', 0)
            .attr('x2', this.quadrantWidth())
            .attr('y2', 0)
    }
    renderClipPath() {
        this._svg.append('defs')
            .append('clip-path')
            .attr('id', 'body-clip')
            .append('rect')
            .attr('x', 0)
            .attr('y', 0)
            .attr('width', this.quadrantWidth())
            .attr('height', this.quadrantHeight())
    }
    renderBody() {
        if(!this._body) {
            this._body = this._svg.append('g')
                .attr('class', 'body')
                .attr('transform', `translate(${this._margins.left},${this._margins.top})`)
                .attr('clip-path', 'url(#clipPath)')
            this.renderShadow()
        }
        this.renderBar();
        this.listenMousemove();
    }
    renderShadow() {
        this._shadow = this._body.append('rect')
            .attr('x', 0)
            .attr('y', 0)
            .attr('width', this.everyWidth())
            .attr('height', this._scaleY(0))
            .attr('fill', '#000')
            .attr('fill-opacity', 0)
    }
    renderBar() {
        let barElements = this._body
            .selectAll('rect.bar')
            .data(this._data);
        let barEnter =  barElements
            .enter()
            .append('rect')
            .attr('class', 'bar')
            .attr('x', d => this._scaleX(d[this._key]) + this.everyWidth() * 0.18)
            .attr('y', () => this._scaleY(0))
            .attr('width', this.everyWidth() * 0.64)
            .attr('height', () => this.quadrantHeight() - this._scaleY(0))
        let barUpdate = barEnter
            .merge(barElements)
            .transition()
            .duration(800)
            .ease(d3.easeCubicOut)
            .attr('y', d => this._scaleY(d[this._value]))
            .attr('height', d => {
                console.log(this.quadrantHeight() - this._scaleY(d[this._value]))
                return this.quadrantHeight() - this._scaleY(d[this._value])
            });
        let barExit = barElements
            .exit()
            .transition()
            .attr('y', () => this._scaleY(0))
            .attr('height', () => this.quadrantHeight() - this._scaleY(0))
            .remove();
    }
    listenMousemove() {
        this._svg.on('mousemove', () => {
            let px = d3.event.offsetX;
            let py = d3.event.offsetY;
            if(px < this.xEnd() && px > this.xStart() && py < this.yStart() && py > this.yEnd()) {
                this.renderShadowAndTooltip(px, py, px - this.xStart());
            } else {
                this.hideShadowAndTooltip();
            }
        })
    }
    renderShadowAndTooltip(x, y, bodyX) {
        let cutIndex = Math.floor(bodyX / this.everyWidth());
        this._shadow.transition().duration(50).ease(d3.easeLinear).attr('fill-opacity', .12).attr('x', cutIndex * this.everyWidth());
        if(x > this.quadrantWidth() - this._tooltip.style('width').slice(0,-2) - this._padding * 2) {
            x = x - this._tooltip.style('width').slice(0,-2) - this._padding * 2 - this._offset * 2;
        }
        if(y > this.quadrantHeight() - this._tooltip.style('height').slice(0,-2) - this._padding * 2) {
            y = y - this._tooltip.style('height').slice(0,-2) - this._padding * 2 - this._offset * 2;
        }
        this._tooltip.html(`${this._data[cutIndex][this._key]}<br/>数量统计: ${this._data[cutIndex][this._value]}`).transition().duration(100).ease(d3.easeLinear).style('display', 'inline-block').style('opacity', .6).style('left', `${x + this._offset + this._padding}px`).style('top', `${y + this._offset + this._padding}px`);
    }
    hideShadowAndTooltip() {
    this._shadow.transition().duration(10).attr('fill-opacity', 0);
    this._tooltip.transition().duration(50).style('opacity', 0).on('end', function() {d3.select(this).style('display', 'none')})
  }
    everyWidth() {
        return this.quadrantWidth() / this._data.length;
    }
    quadrantWidth() {
        return this._width - this._margins.left - this._margins.right;
    }
    quadrantHeight() {
        return this._height - this._margins.top - this._margins.bottom;
    }
    xStart() {
        return this._margins.left;
    }
    xEnd() {
        return this._width - this._margins.right;
    }
    yStart() {
        return this._height - this._margins.bottom;
    }
    yEnd() {
        return this._margins.top;
    }
    scaleX(a) {
        this._scaleX = this._scaleX.domain(a);
    }
    scaleY(a) {
        this._scaleY = this._scaleY.domain(a)
    }
    key(k) {
        if(!arguments.length) return this._key;
        this._key = k;
        this.scaleX(this._data.map(d => d[this._key]))
        return this;
    }
    value(v) {
        if(!arguments.length) return this._value;
        this._value = v;
        let arr = this._data.map(d => d[this._value]);
        let ele = Math.pow(10, d3.max(arr).toString().length - 1);
        let max = Math.ceil(d3.max(arr) / ele) * ele;
        this.scaleY([0, max]);
        return this;
    }
    data(data) {
        if(!arguments.length) return this._data;
        this._data = data;
        return this;
    }
}
2 CSS 文件很简单
.domain {
  stroke-width:;
  fill: none;
  stroke: #888;
  shape-rendering: crispEdges;
}
.x .tick line {
  opacity: 0  ;
}
.tick text {
  font-size: 14px;
}
.grid-line {
  fill: none;
  stroke: #888;
  opacity: .4;
  shape-rendering: crispEdges;
}
.bar {
  fill: #3398DB;
}
.tooltip{
  font-size: 15px;
  width: auto;
  padding: 10px;
  height: auto;
  position: absolute;
  text-align: center;
  background-color: #000000;
  opacity: .6;
  border-radius:5px;
  color: #ffffff;
  display: none;
}
3 加下来就是html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>$Title$</title>
<link rel="stylesheet" type="text/css" href="css/base.css"/>
<script type="text/javascript" src="js/d3.v4.js"></script>
<script type="text/javascript" src="js/bar.js"></script>
</head>
<body>
<script>
var dataset = [{date: 'Mon', label: 15},{date: 'Tue', label: 52},{date: 'Wed', label: 200},{date: 'Thu', label: 235},{date: 'Fri', label: 390},{date: 'Sat', label: 330},{date: 'Sun', label: 221}];
var bar = new Bar();
bar
.data(dataset)
.key('date')
.value('label')
.render();
</script>
</body>
</html>
4 接着是效果图

新上手的朋友们可以先学习一下ES6,然后在学习类的思想,d3.v3和v4 v5的版本差异比较大,直接学习d3.v4就可以了,最最后推荐一本书。D3 4.x数据可视化实战手册。这本书比较基础但是能够通过它养成良好的d3编程习惯。祝大家d3学习顺利。
如果想下载代码或者预览这个DEMO请移步到原文!!!
原文链接:http://www.bettersmile.cn
d3.js 教程 模仿echarts柱状图的更多相关文章
- d3.js 教程 模仿echarts折线图
		今天我们来仿echarts折线图,这个图在echarts是折线图堆叠,但是我用d3改造成了普通的折线图,只为了大家学习(其实在简单的写一个布局就可以).废话不多说商行代码. 1 制作 Line 类 c ... 
- d3.js 教程 模仿echarts legend功能
		上一节记录没有加上echarts的legend功能,这一小节补一下. 1. 数据 我们可以从echarts中看出,折线数据并不是我们传进入的原始数据(多数情况下我们也不会修改原始数据),而是原始数组的 ... 
- javascript实例教程使用canvas技术模仿echarts柱状图
		canvas 画布是HTML5中新增的标签,可以通过js操作 canvas 绘图 API在网页中绘制图像. 百度开发了一个开源的可视化图表库ECharts,功能非常强大,可以实现折线图.柱状图.散点图 ... 
- D3.js的v5版本入门教程(第三章)—— 选择元素和绑定数据
		D3.js的v5版本入门教程(第三章) 在D3.js中,选择元素和绑定元素是最基本的内容,也是很重要的内容,等你看完整个教程后你会发现,这些D3.js教程都是在选择元素和绑定元素的基础上展开后续工作的 ... 
- D3.js的v5版本入门教程(第九章)——完整的柱状图
		D3.js的v5版本入门教程(第九章) 一个完整的柱状图应该包括的元素有——矩形.文字.坐标轴,现在,我们就来一一绘制它们,这章是前面几章的综合,这一章只有少量新的知识点,它们是 d3.scaleBa ... 
- D3.js 入门教程
		最近需要用到d3, 记录下d3的教程 网上搜了几个关于d3的教程 D3.js 入门教程 http://wiki.jikexueyuan.com/project/d3wiki/author.h ... 
- 页面生成柱状图 --- D3.js
		转载自:https://www.cnblogs.com/fastmover/p/7779660.html D3.js从入门到"放弃"指南 前言 近期略有点诸事不顺,趁略有闲余之时, ... 
- D3.js的v5版本入门教程(第十一章)——交互式操作
		D3.js的v5版本入门教程(第十一章) 与图形进行交互操作是很重要的!所谓的交互操作也就是为图形元素添加监听事件,比如说当你鼠标放在某个图形元素上面的时候,就会显示相应的文字,而当鼠标移开后,文字就 ... 
- D3.js的v5版本入门教程(第八章)—— 坐标轴
		D3.js的v5版本入门教程(第八章) D3中没有现成的坐标轴图形,需要我们自己用其他组件拼凑而成.D3中提供了坐标轴组件,使得我们在SVG中绘制一个坐标轴变得像添加一个普通元素那样简单 为了表绘制一 ... 
随机推荐
- 使用GDAL实现DEM的地貌晕渲图(三)
			目录 1. 原理 1) ArcMap生成彩色晕渲图 2) 彩色色带赋值 3) 颜色叠加 2. 实现 3. 结语 4. 参考 1. 原理 之前在<使用GDAL实现DEM的地貌晕渲图(一)>和 ... 
- 如何在jsp中显示数据库的内容
			用Eclipse tomcat新建一个JSP页面(一)介绍了如何创建一个web程序和第一个jsp页面,以及Eclipse需要的一些必要配置.今天,我们重点说一下如何从数据库中查询数据,并且在JSP页面 ... 
- JDBC秒变C3P0连接池——再加连接解耦
			从JDBC连接到C3P0数据库连接池 在Java开发中,使用JDBC操作数据库的四个步骤如下: ①加载数据库驱动程序(Class.forName("数据库驱动类");) ② ... 
- Qtech 暑假未讲到的算法(不完全)
			一.数据结构: 优先队列.堆.RMQ问题(区间最值问题,可以用线段树解决,还有一个Sparse-Table算法).排序二叉树.划分树.归并树..... 字符串处理: KMP.字典树.后 ... 
- SpringMvc新建实例配置
			一.创建项目: 1.建立新的动态web项目: 2.为项目命名为:SpringMVC_01 3.添加tomcat运行时环境\依赖库 如果是MyEclipse的话创建web项目时就不需要此步骤 右键项目 ... 
- Cassandra之Docker环境实践
			Cassandra简介 Cassandra是一个开源分布式NoSQL数据库系统. 它最初由Facebook开发,用于储存收件箱等简单格式数据,集GoogleBigTable的数据模型与Amazon D ... 
- 分布式ID系列(4)——Redis集群实现的分布式ID适合做分布式ID吗
			首先是项目地址: https://github.com/maqiankun/distributed-id-redis-generator 关于Redis集群生成分布式ID,这里要先了解redis使用l ... 
- JAVA基础知识(九)Java 异常
			Throwable是Error和Exception的基类 Exception(异常) :是程序本身可以处理的异常. Error(错误): 是程序无法处理的错误.这些错误表示故障发生于虚拟机自身.或者发 ... 
- wwww
			public class MainActivity extends AppCompatActivity implements XListView.IXListViewListener{ ; priva ... 
- Spring Boot 中的同一个 Bug,竟然把我坑了两次!
			真是郁闷,不过这事又一次提醒我解决问题还是要根治,不能囫囵吞枣,否则相同的问题可能会以不同的形式出现,每次都得花时间去搞.刨根问底,一步到位,再遇到类似问题就可以分分钟解决了. 如果大家没看过松哥之前 ... 
