D3是一种数据可视化工具,数据的可视化其实就是把数据以图表等直观的方式展示给用户,让用户更直观的感受到数据的走势和变化。这种应用在项目中越来越多的被使用,话说,千句话不如一张图,说的一点都不为过。那么D3作为这种轻巧免费公开的图表制作工具,在应用中被越来越多的使用。D3有很多让人眼前一亮的功能,现在我们所处理的是一些基本的图表,比如柱状图,饼图,折线图等。

话不多说,这篇博客主要是在项目中用到的柱状图的画法。先看一下效果图:


在显示报表的时候我们都喜欢将数据按照横纵坐标来展示数据的变化,Y轴一般是数值,X轴一般都是时间,当然也可以是其他的字符串用于显示某种类别。

代码的创建方式大同小异,都是绑定一个div,在div里面使用svg来做文章

前端代码详见:

<div id="ham-guest-summary-common-group-chart" class="ham-summary-common-group-chart" style="width: 100%"></div>

我们创建了一个容器来存放生成的svg。

我们来创建画布的高宽以及边距:

 //定义柱状图的宽高边距属性
            var margin = {top: 20,right:40,bottom: 0,left: 40},width = 900,height = 300;

定义X,Y轴的比例尺和作用范围:

//定义X轴作用范围
            var x0 = d3.scale.ordinal()
                .rangeRoundBands([0,width],.1);

            var x1 = d3.scale.ordinal();
            //定义Y轴作用范围
            var y = d3.scale.linear()
                .range([height,0]);
            //定义X比例尺
            var xAxis = d3.svg.axis()
                .scale(x0)
                .orient("bottom");
            //定义Y比例尺
            var yAxis = d3.svg.axis()
                .scale(y)
                .orient("left")
                .tickFormat(d3.format(".2s"));

定义柱状图颜色:

//定义标准颜色样式
            var colorRange = d3.scale.category20();
            var color = d3.scale.ordinal()
                    .range(colorRange.range());
定义svg画布:

var svg = d3.select("#ham-guest-summary-common-group-chart").append("svg")
                .attr("preserveAspectRatio","xMidYMid meet")
                .attr("viewBox","0 0 1000 350")
                .append("g")
                .attr("transform","translate(" + margin.left + "," + margin.top + ")");

这里preserveAspectRatio和viewBox可以让svg画布支持缩放功能,前提是该viewBox的宽高必须大于画布的宽高才能做到缩放的效果。

接下来我们来使用data方法来绑定数据,以及x的作用域和y的值域:

var options = d3.keys(dataset[0]).filter(function(key) { return key !== "label"; });

            dataset.forEach(function(d) {
                d.valores = options.map(function(name) { return {name: name,value: +d[name]}; });
            });

            x0.domain(dataset.map(function(d) { return d.label; }));
            x1.domain(options).rangeRoundBands([0,x0.rangeBand()]);
            y.domain([0,d3.max(dataset,function(d) { return d3.max(d.valores,function(d) { return d.value; }); })]);


数据绑定之后需要创建x轴和y轴,使用前面定义到的比例尺和作用域和值域来构建x轴y轴:

 //画X轴
            svg.append("g")
                .attr("class","x axis")
                .attr("transform","translate(0," + height + ")")
                .call(xAxis);
            //画Y轴
            svg.append("g")
                .attr("class","y axis")
                .call(yAxis)
                .append("text")
                .attr("transform","rotate(-90)")
                .attr("y",6)
                .attr("dy",".71em")
                .style("text-anchor","end")
                .text("Numbers");
将数据绑定到画布区域中,每一个柱状图都成为bar:

var bar = svg.selectAll(".bar")
                .data(dataset)
                .enter().append("g")
                .attr("class","rect")
                .attr("transform",function(d) { return "translate(" + x0(d.label) + ",0)"; });
每一个bar都是由多个rect矩形组成,所以我们需要跟rect绑定数值,label属性:

bar.selectAll("rect")
                .data(function(d) { return d.valores; })
                .enter().append("rect")
                .attr("width",x1.rangeBand())
                .attr("x",function(d) { return x1(d.name); })
                .attr("y",function(d) { return y(d.value); })
                .attr("value",function(d){return d.name;})
                .attr("height",function(d) { return height - y(d.value); })
                .style("fill",function(d) { return color(d.name); });

前面的代码就可以生成柱状图了,那么现在当我们鼠标移动到柱状图上时,我们需要提示当前bar的数值信息,需要添加tooltips功能,需要一些样式的支持:

var divTooltip = d3.select("#ham-guest-summary-common-group-chart").append("div").attr("class","toolTip");
.ham-summary-common-group-chart .legend {
    font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
    font-size: 60%;
}

.ham-summary-common-group-chart rect {
    stroke-width: 2;
}

.ham-summary-common-group-chart text {
    font: 10px sans-serif;
}

.ham-summary-common-group-chart .axis text {
    font: 10px sans-serif;
}

.ham-summary-common-group-chart .axis path{
    fill: none;
    stroke: #000;
}

.ham-summary-common-group-chart .axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}

.ham-summary-common-group-chart .axis .tick line {
    stroke-width: 1;
    stroke: rgba(0,0.2);
}

.ham-summary-common-group-chart .axisHorizontal path{
    fill: none;
}

.ham-summary-common-group-chart .axisHorizontal line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}

.ham-summary-common-group-chart .axisHorizontal .tick line {
    stroke-width: 1;
    stroke: rgba(0,0.2);
}

.ham-summary-common-group-chart .bar {
    fill: steelblue;
    fill-opacity: .9;
}

.ham-summary-common-group-chart .x.axis path {
    display: none;
}


所谓的提示信息就是鼠标移动时,bar去监听鼠标事件,然后追加一个div用于显示数据信息。

bar.on("mousemove",function(d){
                var scoll = getScrollTop();
                divTooltip.style("left",d3.event.pageX + "px");
                divTooltip.style("top",d3.event.pageY - scoll + "px");
                //divTooltip.style("top",d3.event.pageY+"px");
                divTooltip.style("display","inline-block");
                var x = d3.event.pageX,y = d3.event.pageY;
                var elements = document.querySelectorAll(':hover');
                var l = elements.length;
                l = l-1;
                var elementData = elements[l].__data__;
                var title_name = "";
                if(elementData.name == "newCreatedAccount"){
                    title_name = "New Created Account";
                }else if(elementData.name == "totalGuestAccount"){
                    title_name = "Total Guest Account";
                }else if(elementData.name == "activeGuestAccount"){
                    title_name = "Active Guest Account";
                }else if(elementData.name == "totalGuestDevice"){
                    title_name = "Total Guest Device";
                }
                divTooltip.html((d.label)+"<br>"+title_name+"<br>"+elementData.value);
            });
            bar.on("mouSEOut",function(d){
                divTooltip.style("display","none");
            });



在这里提到一个问题,当Y轴方向出现滚动条的时候,鼠标悬浮的提示信息,会发生变化,跟鼠标的位置有差距,这个问题我在其他的博客中写到过,可以参考:

获取滚动条的上边距,然后当前的坐标减去上边距就行:

//get scroll distance to top
        function getScrollTop() {
            var scrollPos;
            if (window.pageYOffset) {
                scrollPos = window.pageYOffset;
            }else if (document.compatMode && document.compatMode != 'BackCompat') {
                scrollPos = document.documentElement.scrollTop;
            }else if (document.body) {
                scrollPos = document.body.scrollTop;
            }
            return scrollPos;
        }
后台返回数据格式:

{
    "result": "success","errorCode": 0,"errorMessage": null,"data": [
        {
            "id": 20,"newCreatedAccount": 0,"totalGuestAccount": 3,"activeGuestAccount": 1,"totalGuestDevice": 1,"statisticalDate": "30/08/17"
        },{
            "id": 21,"statisticalDate": "31/08/17"
        },{
            "id": null,"totalGuestAccount": 0,"activeGuestAccount": 0,"totalGuestDevice": 0,"statisticalDate": "01/09/17"
        },"statisticalDate": "02/09/17"
        },"statisticalDate": "03/09/17"
        },"statisticalDate": "04/09/17"
        },"statisticalDate": "05/09/17"
        },"statisticalDate": "06/09/17"
        },"statisticalDate": "07/09/17"
        },"statisticalDate": "08/09/17"
        }
    ],"translated": {}
}
需要将后台的数据做处理,转为柱状图识别的json格式:

function getAccountAndDeviceNumber() {

            var dataset = [];
            amGuestHomeService.getAccountAndDeviceNumber()
                .then(function success(response) {
                    if (response.data != null && response.data.length > 0) {
                        angular.forEach(response.data,function (item) {
                            var data = {};
                            data.label = item.statisticalDate;
                            data.newCreatedAccount = item.newCreatedAccount;
                            data.totalGuestAccount = item.totalGuestAccount;
                            data.activeGuestAccount = item.activeGuestAccount;
                            data.totalGuestDevice = item.totalGuestDevice;
                            dataset.push(data);

                        })
                        amGuestHomeService.drawGroupChart(dataset);
                    }
                })
                .catch(function fail(/*e*/) {
                    //handle error
                })
                .finally(function () {
                    vm.hideLoading();
                });
        }


最后附上全部代码:


function drawGroupChart(dataset){
            //定义柱状图的宽高边距属性
            var margin = {top: 20,height = 300;
            //定义X轴作用范围
            var x0 = d3.scale.ordinal()
                .rangeRoundBands([0,0]);
            //定义标准颜色样式
            var colorRange = d3.scale.category20();
            var color = d3.scale.ordinal()
                .range(colorRange.range());
            //定义X比例尺
            var xAxis = d3.svg.axis()
                .scale(x0)
                .orient("bottom");
            //定义Y比例尺
            var yAxis = d3.svg.axis()
                .scale(y)
                .orient("left")
                .tickFormat(d3.format(".2s"));
            //这里是牵扯到查询会不断切换柱状图,将之前生成的svg删除
            d3.select("#ham-guest-summary-common-group-chart svg").remove();
            //定义鼠标移动时提示信息
            var divTooltip = d3.select("#ham-guest-summary-common-group-chart").append("div").attr("class","toolTip");
            //创建svg画布preserveAspectRatio,viewBox这两个属性可以支持画布的缩放功能
            var svg = d3.select("#ham-guest-summary-common-group-chart").append("svg")
                .attr("preserveAspectRatio"," + margin.top + ")");
            //获取label
            var options = d3.keys(dataset[0]).filter(function(key) { return key !== "label"; });

            dataset.forEach(function(d) {
                d.valores = options.map(function(name) { return {name: name,function(d) { return d.value; }); })]);
            //画X轴
            svg.append("g")
                .attr("class","end")
                .text("Numbers");

            var bar = svg.selectAll(".bar")
                .data(dataset)
                .enter().append("g")
                .attr("class",0)"; });

            bar.selectAll("rect")
                .data(function(d) { return d.valores; })
                .enter().append("rect")
                .attr("width",function(d) { return color(d.name); });

            bar.on("mousemove","none");
            });

        }

D3实现柱状图的更多相关文章

  1. canvas中普通动效与粒子动效的实现代码示例

    canvas用于在网页上绘制图像、动画,可以将其理解为画布,在这个画布上构建想要的效果。本文详细的介绍了粒子特效,和普通动效进行对比,非常具有实用价值,需要的朋友可以参考下

  2. H5混合开发app如何升级的方法

    本篇文章主要介绍了H5混合开发app如何升级的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  3. 详解Canvas实用库Fabric.js使用手册

    这篇文章主要介绍了详解Canvas实用库Fabric.js使用手册的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. canvas学习和滤镜实现代码

    这篇文章主要介绍了canvas学习和滤镜实现代码,利用 canvas,前端人员可以很轻松地、进行图像处理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. localStorage的过期时间设置的方法详解

    这篇文章主要介绍了localStorage的过期时间设置的方法详解的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  6. 详解HTML5 data-* 自定义属性

    这篇文章主要介绍了详解HTML5 data-* 自定义属性的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. html5中canvas图表实现柱状图的示例

    本篇文章主要介绍了html5中canvas图表实现柱状图的示例,本文使用canvas来实现一个图表,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  8. 【HTML5】3D模型--百行代码实现旋转立体魔方实例

    本篇文章主要介绍【HTML5】3D模型--百行代码实现旋转立体魔方实例,具有一定的参考价值,有需要的可以了解一下。

  9. HTML5的postMessage的使用手册

    HTML5提出了一个新的用来跨域传值的方法,即postMessage,这篇文章主要介绍了HTML5的postMessage的使用手册的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  10. 教你使用Canvas处理图片的方法

    本篇文章主要介绍了教你使用Canvas处理图片的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

随机推荐

  1. Angular2 innerHtml删除样式

    我正在使用innerHtml并在我的cms中设置html,响应似乎没问题,如果我这样打印:{{poi.content}}它给了我正确的内容:``但是当我使用[innerHtml]=“poi.content”时,它会给我这个html:当我使用[innerHtml]时,有谁知道为什么它会剥离我的样式Angular2清理动态添加的HTML,样式,……

  2. 为Angular根组件/模块指定@Input()参数

    我有3个根组件,由根AppModule引导.你如何为其中一个组件指定@input()参数?也不由AppModalComponent获取:它是未定义的.据我所知,你不能将@input()传递给bootstraped组件.但您可以使用其他方法来做到这一点–将值作为属性传递.index.html:app.component.ts:

  3. angular-ui-bootstrap – 如何为angular ui-bootstrap tabs指令指定href参数

    我正在使用角度ui-bootstrap库,但我不知道如何为每个选项卡指定自定义href.在角度ui-bootstrap文档中,指定了一个可选参数select(),但我不知道如何使用它来自定义每个选项卡的链接另一种重新定义问题的方法是如何使用带有角度ui-bootstrap选项卡的路由我希望现在还不算太晚,但我今天遇到了同样的问题.你可以通过以下方式实现:1)在控制器中定义选项卡href:2)声明一个函数来改变控制器中的散列:3)使用以下标记:我不确定这是否是最好的方法,我很乐意听取别人的意见.

  4. 离子框架 – 标签内部的ng-click不起作用

    >为什么标签标签内的按钮不起作用?>但是标签外的按钮(登陆)工作正常,为什么?>请帮我解决这个问题.我需要在点击时做出回复按钮workingdemo解决方案就是不要为物品使用标签.而只是使用divHTML

  5. Angular 2:将值传递给路由数据解析

    我正在尝试编写一个DataResolver服务,允许Angular2路由器在初始化组件之前预加载数据.解析器需要调用不同的API端点来获取适合于正在加载的路由的数据.我正在构建一个通用解析器,而不是为我的许多组件中的每个组件设置一个解析器.因此,我想在路由定义中传递指向正确端点的自定义输入.例如,考虑以下路线:app.routes.ts在第一个实例中,解析器需要调用/path/to/resourc

  6. angularjs – 解释ngModel管道,解析器,格式化程序,viewChangeListeners和$watchers的顺序

    换句话说:如果在模型更新之前触发了“ng-change”,我可以理解,但是我很难理解在更新模型之后以及在完成填充更改之前触发函数绑定属性.如果您读到这里:祝贺并感谢您的耐心等待!

  7. 角度5模板形式检测形式有效性状态的变化

    为了拥有一个可以监听其包含的表单的有效性状态的变化的组件并执行某些组件的方法,是reactiveforms的方法吗?

  8. Angular 2 CSV文件下载

    我在springboot应用程序中有我的后端,从那里我返回一个.csv文件WheniamhittingtheURLinbrowsercsvfileisgettingdownloaded.现在我试图从我的角度2应用程序中点击此URL,代码是这样的:零件:服务:我正在下载文件,但它像ActuallyitshouldbeBook.csv请指导我缺少的东西.有一种解决方法,但您需要创建一个页面上的元

  9. angularjs – Angular UI-Grid:过滤后如何获取总项数

    提前致谢:)你应该避免使用jQuery并与API进行交互.首先需要在网格创建事件中保存对API的引用.您应该已经知道总行数.您可以使用以下命令获取可见/已过滤行数:要么您可以使用以下命令获取所选行的数量:

  10. angularjs – 迁移gulp进程以包含typescript

    或者我应该使用tsc作为我的主要构建工具,让它解决依赖关系,创建映射文件并制作捆绑包?

返回
顶部