(D3.js)绘制柱状图、折线图实现运动步数可视化

统计了八周的运动步数数据,实现了运动步数的可视化,数据来源于手机APP的记录,分别以柱状图和折线图的形式展示。

效果图:
在这里插入图片描述
数据来源:
在这里插入图片描述
分别制作了柱状图及折线图,可通过按钮来互相切换。
在这里插入图片描述
柱状图有八张,分别对应八周的运动步数统计。
有排序功能,可实现当周每日的运动步数增序排序。
在这里插入图片描述
以7000步为界限,当周平均步数高于7000步的柱形为黄色,少于7000步的柱形为红色,并在图标上方有文字说明当周的运动情况。
在这里插入图片描述
在这里插入图片描述
柱形上方有当日的运动步数数值,有tooltip提示工具,当鼠标置于柱形上时可显示当日的运动步数。


折线图中有八条折线,分别代表每周的数据,可通过图例中的颜色加以区分。
在这里插入图片描述


代码如下:

HTML:

<!DOCTYPE html>
<html >

  <head>
    <title>MySports</title>
    <meta charset="utf-8">
    
	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
	<link rel="stylesheet" type="text/css" href="MySports.css">

  </head>

  <body  >
		
    <script src="http://d3js.org/d3.v5.min.js"></script>
    
    <div id="headline" class="title is-1" >每周运动步数统计图</div>
    
  <div class='tool'>
    
	<button id="btn-sort" class="button is-primary is-rounded">排序</button>
	<button id="btn-convert1"  class="button is-link is-rounded" >第一周</button>
    <button id="btn-convert2"  class="button is-link is-rounded" >第二周</button>
    <button id="btn-convert3"  class="button is-link is-rounded" >第三周</button>
    <button id="btn-convert4"  class="button is-link is-rounded" >第四周</button>
    <button id="btn-convert5"  class="button is-link is-rounded" >第五周</button>
    <button id="btn-convert6"  class="button is-link is-rounded" >第六周</button>
    <button id="btn-convert7"  class="button is-link is-rounded" >第七周</button>
	<button id="btn-convert8"  class="button is-link is-rounded" >第八周</button>
	<button id="btn-line" class="button is-rounded">折线图</button>

  </div>

    <script type="text/javascript" src="MySports.js"></script>

  </body>
</html>

JS:

        var padding = {top:100,bottom:60,left:120,right:60}
    	var width = 1200;
		var height = 620;
		
		var dataset=[
    		{
        	week:"1",
        	datax:['周一','周二','周三','周四','周五','周六','周日'],
			datay:[10961,10882,11976,9935,6075,954,8794]
    		},
			{
        	week:"2",
        	datax:['周一','周二','周三','周四','周五','周六','周日'],
			datay:[7654,12617,5041,5823,6506,2257,7206]
    		},
			{
        	week:"3",
        	datax:['周一','周二','周三','周四','周五','周六','周日'],
			datay:[11526,11506,8935,8853,3251,4438,4248]
    		},
			{
        	week:"4",
        	datax:['周一','周二','周三','周四','周五','周六','周日'],
			datay:[7598,7576,10236,8450,3333,6673,6666]
    		},
			{
        	week:"5",
        	datax:['周一','周二','周三','周四','周五','周六','周日'],
			datay:[8376,11217,9790,6657,6302,7166,7127]
    		},
			{
        	week:"6",
        	datax:['周一','周二','周三','周四','周五','周六','周日'],
			datay:[10678,4177,5798,12920,7501,7320,2140]
    		},
			{
        	week:"7",
        	datax:['周一','周二','周三','周四','周五','周六','周日'],
			datay:[9133,7829,10438,6067,2138,7458,5758]
    		},
			{
        	week:"8",
        	datax:['周一','周二','周三','周四','周五','周六','周日'],
			datay:[8563,9621,5311,10406,10864,9637,4021]
    		},
        ];		 	
        
    //柱状图函数
	function drawbar(datay,datax,week){		

		//设置画布
		var svg = d3.select('body')
		            .append('svg')
		            .attr('width', width)
		            .attr('height', height);
		
		var g = svg.append("g")
			       .attr("transform","translate("+padding.top+","+padding.left+")");
		
		//绘制坐标轴
    	var xScale = d3.scaleBand()
    		           .domain(datax) 
				 	   .range([0,790])
		var xAxis = d3.axisBottom(xScale);
		
    	var yScale = d3.scaleLinear()
    				   .domain([0,d3.max(datay)]) 
    		           .range([height-padding.top-padding.bottom,0]);
    	var yAxis = d3.axisLeft(yScale);
    	
		g.append("g")
			.attr('class','axis')
    		.attr("transform","translate("+0+","+(height-padding.top-padding.bottom)+")")
    		.call(xAxis);
			
		g.append("g")
			.attr('class','axis')
    		.attr("transform","translate(0,0)")
    		.call(yAxis);

		//求每周步数的平均值
		var avg0 =(datay[0]+datay[1]+datay[2]+datay[3]+datay[4]+datay[5]+datay[6])/7
    	var avg=avg0.toFixed(0) //将平均值去小数点

    	//绘制矩形和文字
    	var bar = g.selectAll(".rect")
    		.data(datay)
    		.enter()
			.append("g");
			
    	//绘制矩形
    	var rectPadding = 10;//矩形之间的间隙
    	bar.append("rect")
		  .attr("class",'rects')
    	  .attr("x",function(d,i){
    			return xScale(datax[i])+rectPadding/2;
    		})	
		  .attr("y",function (d) {
			    var min=yScale.domain()[0];
			    return yScale(min);
			})	
    	  .attr("width",function(){
    			return xScale.step()-rectPadding;
    		})
		  .attr("height",function(d,i){
			    return 0;
			})
		  .attr('fill',function(){     //根据每周的平均步数给矩形上色
				if(avg>7000){
					return 'cornsilk';
				}else{
					return  'tomato';
				}
			})
		  .transition()              //为这个元素添加过渡;
		  .duration(800)            //设定元素从起始状态到终止状态的过渡时间;
		  .delay(function(d,i){      //设定元素执行过渡效果的时间间隔;
		      return i*50;
		  })
		  .ease(d3.easeBackOut)      //设定过渡的动画效果
		  .attr("y",function (d,i) {
		      return yScale(d)
		  })
		  .attr("height",function (d,i) {
		      return height-padding.top-padding.bottom-yScale(d);
		  });
			
		//设置tooltip提示框
		var tooltip = d3.select("body")
						.append("div")
			    	    .attr("class","tooltip") 
						.attr("opacity",0);
			    		
		//响应事件
	    //-鼠标移入事件
		bar.on("mouseover",function(d,i)
			 {	
			    //设置tooltip文字
			    tooltip.html(d)
			           //设置tooltip的位置(left,top 相对于页面的距离) 
			    	   .style("left",(d3.event.pageX)+"px")
			           .style("top",(d3.event.pageY+20)+"px")
			           .style("opacity",1);  //淡入效果
			 })
			//--鼠标移出事件
			.on("mouseout",function(d)
			{
				tooltip.style("opacity",0); //鼠标移出时,将透明度设定为0.0,完全透明
			}); 

    	//绘制文字
    	bar.append("text")
		   .attr('class','texts')
		   .attr('font-size','2em')
		   .attr('fill','maroon')
    	   .attr("x",function(d,i){
    			return (xScale(datax[i])+rectPadding/2);
    		})
    		.attr("y",function(d){
            var min=yScale.domain()[0];
            return yScale(min);
        	})
        	.attr("dx",function(){
        		(xScale.step()-rectPadding)/2;
        	})
        	.attr("dy",0)
        	.text(function(d){
        		return d;
        	})
			.transition()              //为这个元素添加过渡;
			.duration(800)            //设定元素从起始状态到终止状态的过渡时间;
			.delay(function(d,i){      //设定元素执行过渡效果的时间间隔;
			    return i*50;
			})
			.ease(d3.easeBackOut)      //设定过渡的动画效果
			.attr("y",function (d,i) {
			    return yScale(d)-10;
			})
			.attr("height",function (d,i) {
			    return height-padding.top-padding.bottom-yScale(d);
			});
		
		//根据每周平均步数添加不同文字
		if(avg>7000){
			svg.append('text')
			   .text('当前为第'+week+'周,本周平均步数为'+avg+',继续保持!')
			   .style('font-style','italic') //斜体
			   .attr('fill','teal')
			   .attr('font-size','2em')
			   .attr('x',100)
			   .attr('y',50)
			   
		}else{
			svg.append('text')
			   .text('当前为第'+week+'周,本周平均步数为'+avg+',需要多运动!')
			   .style('font-style','italic') //斜体
			   .attr('fill','navy')
			   .attr('font-size','2em')
			   .attr('x',100)
			   .attr('y',50)
		}
		
		//给坐标轴添加文字
		svg.append('text')
		   .text('步数')
		   .attr('font-size', '1.5em')
		   .attr('x',55)
		   .attr('y',100 )
		   .attr('fill','maroon')
		  
		svg.append('text')
		   .text('日期')
		   .attr('font-size', '1.5em')
		   .attr('x',900)
		   .attr('y',600 )
		   .attr('fill','maroon')
	
		//添加图例
		svg.append('rect')
		   .attr('x',1000)
		   .attr('y',100)
		   .attr('width',50)
		   .attr('height',20)
		   .attr('fill','cornsilk');
		   
		svg.append('text')
		   .text('当周平均步数多于7000步')
		   .attr('font-size', '1em')
		   .attr('fill','cornsilk')
		   .attr('x',1000)
		   .attr('y',140 )
		   .style()
		
		svg.append('rect')
		   .attr('x',1000)
		   .attr('y',160)
		   .attr('width',50)
		   .attr('height',20)
		   .attr('fill','tomato'); 
		   
		svg.append('text')
		   .text('当周平均步数少于7000步')
		   .attr('font-size', '1em')
		   .attr('fill','tomato')
		   .attr('x',1000)
		   .attr('y',200);


	    //排序按钮功能  
        d3.select('#btn-sort').on('click', ()=>{
		    svg.selectAll(".rects")
		       .sort()
			   .attr("x",function(d,i){
			       			return xScale(datax[i])+rectPadding/2;
			       		})	
			   .attr("y",function (d) {
			   			    var min=yScale.domain()[0];
			   			    return yScale(min);
			   			})	
			   .attr("width",function(){
			       			return xScale.step()-rectPadding;
			       		})
			   .attr("height",function(d,i){
			   			    return 0;
			   			})
			   .transition()              //为这个元素添加过渡;
			   .duration(800)            //设定元素从起始状态到终止状态的过渡时间;
			   .delay(function(d,i){      //设定元素执行过渡效果的时间间隔;
			       return i*50;
			   })
			   .ease(d3.easeBackOut)      //设定过渡的动画效果
			   .attr("y",function (d,i) {
			       return yScale(d)
			   })
			   .attr("height",function (d,i) {	
			       return height-padding.top-padding.bottom-yScale(d);
			   })

			svg.selectAll(".texts")
			   .sort()
			   .attr("x",function(d,i){
			   	return (xScale(datax[i])+rectPadding/2);
			   })
			   .attr("y",function(d){
			   var min=yScale.domain()[0];
			   return yScale(min);
			   })
			   .attr("dx",function(){
			   	(xScale.step()-rectPadding)/2;
			   })
			   .attr("dy",0)
			   .text(function(d){
			   	return d;
			   })
			   .transition()              //为这个元素添加过渡;
			   .duration(800)            //设定元素从起始状态到终止状态的过渡时间;
			   .delay(function(d,i){      //设定元素执行过渡效果的时间间隔;
			       return i*50;
			   })
			   .ease(d3.easeBackOut)      //设定过渡的动画效果
			   .attr("y",function (d,i) {
			       return yScale(d)-10;
			   })
			   .attr("height",function (d,i) {
			       return height-padding.top-padding.bottom-yScale(d);
			   })
		         })

					
		//根据按钮显示每周数据			
        d3.select('#btn-convert1').on('click', function(){	  
		 svg.remove();		
	  	 drawbar(dataset[0].datay,dataset[0].datax,dataset[0].week);		   
		}) ;  
	    d3.select('#btn-convert2').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[1].datay,dataset[1].datax,dataset[1].week);		   
	    }) ;  
	    d3.select('#btn-convert3').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[2].datay,dataset[2].datax,dataset[2].week);		   
	    }) ;  
	    d3.select('#btn-convert4').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[3].datay,dataset[3].datax,dataset[3].week);		   
	    }) ;  
	    d3.select('#btn-convert5').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[4].datay,dataset[4].datax,dataset[4].week);		   
	    }) ;  
	    d3.select('#btn-convert6').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[5].datay,dataset[5].datax,dataset[5].week);		   
	    }) ;  
	    d3.select('#btn-convert7').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[6].datay,dataset[6].datax,dataset[6].week);		   
	    }) ;  
	    d3.select('#btn-convert8').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[7].datay,dataset[7].datax,dataset[7].week);		   
	    }) ;  	
		d3.select('#btn-line').on('click', function(){	  
	     svg.remove();		
	     drawline();
	    }) ;  
     }

		var dataset2=[
            {
                Week:"OneWeek",
                gdp:[[1,10961],[2,10882],[3,11976],[4,9935],[5,6075],[6,954],[7,8794],]
            },
            {
                Week:"TwoWeek",
                gdp:[[1,7654],[2,12617],[3,5041],[4,5823],[5,6506],[6,2257],[7,7206],]
            },
            {
                Week:"ThreeWeek",
                gdp:[[1,11526],[2,11506],[3,8935],[4,8853],[5,3251],[6,4438],[7,4248],]
            },
            {
                Week:"FourWeek",
                gdp:[[1,7598],[2,7576],[3,10236],[4,8450],[5,3333],[6,6673],[7,6666],]
            },
            {
                Week:"FiveWeek",
                gdp:[[1,8376],[2,11217],[3,9790],[4,6657],[5,6302],[6,7166],[7,7127],]
            },
            {
                Week:"SixWeek",
                gdp:[[1,10678],[2,4177],[3,5798],[4,12920],[5,7501],[6,7320],[7,2140],]
			},
			{
                Week:"SevenWeek",
                gdp:[[1,9133],[2,7829],[3,10438],[4,6067],[5,2138],[6,7458],[7,5758],]
			},
			{
                Week:"EightWeek",
                gdp:[[1,8563],[2,9621],[3,5311],[4,10406],[5,10864],[6,9637],[7,4021],]
			},
		];
		
	    //折线图函数
	    function drawline(){

		//定义颜色
		var colors=[d3.rgb(0,0,255),d3.rgb(0,255,0),d3.rgb(255,0,0),d3.rgb(0,255,255),d3.rgb(255,0,255),
			d3.rgb(255,255,0),d3.rgb(0,0,125),d3.rgb(125,0,0)];
		
        //绘制坐标刻度
        var xScale=d3.scaleLinear()
					 .domain([1,7])
					 .range([100,850]);
					
        var yScale=d3.scaleLinear()
                     .domain([0,15000])
                     .range([height - padding.bottom, padding.top]);
         
         var gdpmax=0;
         for(var i=0;i<dataset2.length;i++){
             var currGdp=d3.max(dataset2[i].gdp,function(d){
                 return d[1];
             });
             if(currGdp>gdpmax)
                 gdpmax=currGdp;
         }
         console.log(gdpmax);
         
         
         //创建一个svg画图区域
         var svg=d3.select("body")
                   .append("svg")
                   .attr("width",width)
                   .attr("height",height);
         
         //创建一个直线生成器
         var linePath=d3.line()
                         .x(function(d){
                             return xScale(d[0]);
                         })
                         //获取每个节点的x坐标
                         .y(function(d){
                             return yScale(d[1]);
                         })
                        //获取每个节点的y坐标
                                
         //添加路径,将path添加进svg容器
         svg.selectAll("path")
             .data(dataset2)
             .enter()
             .append("path")
			 .attr("transform","translate(" + 0 + ",0)")
             //将直线的左端置于y轴坐标轴上
             .attr("d",function(d){
                 return linePath(d.gdp);
                 //返回线段生成器得到的路径
             })
             .attr("fill","none")//填充颜色为none
             .attr("stroke-width",3)//线的宽度
             .attr("stroke",function(d,i){
                 return colors[i];
             });//使用线段颜色
        
		var xAxis = d3.axisBottom(xScale)
					  .scale(xScale)
                      .ticks(5)
					  .tickFormat(d3.format("d"))
					  
		var yAxis = d3.axisLeft(yScale);

         //将坐标轴添加进svg容器
         //x轴
         svg.append("g")
             .attr("class","axis")
             .attr("transform","translate(0," + (height - padding.bottom) + ")")
             .call(xAxis)
 
         //y轴
         svg.append("g")
             .attr("class","axis")
             .attr("transform","translate(" + 100 + ",0)")
             .call(yAxis)
		
		//给坐标轴添加文字
		svg.append('text')
		   .text('步数')
		   .attr('font-size', '1.5em')
		   .attr('x',60)
		   .attr('y',80 )
		   .attr('fill','maroon')
		  
		
		svg.append('text')
		   .text('日期')
		   .attr('font-size', '1.5em')
		   .attr('x',860)
		   .attr('y',570 )
		   .attr('fill','maroon')
		   
		
		//图例数组,格式可自定义
        var data_legend = [
            {
                "name":"第一周",
                "color":"#0000FF"
            },
            {
                "name":"第二周",
                "color":"#00FF00"
            },
            {
                "name":"第三周",
                "color":"#FF0000"
            },
            {
                "name":"第四周",
                "color":"#00FFFF"
            },    
            {
                "name":"第五周",
				"color": "#FF00FF"
            },
            {
                "name":"第六周",
                "color":"#FFFF00"
			},
			{
                "name":"第七周",
                "color":"#00007D"
			},
			{
                "name":"第八周",
                "color":"#7D0000"
            },
        ];
        
        //初始化图例,将data_legend与图例绑定
        var legend = svg.selectAll(".legend") 
                     .data(data_legend)
			         .enter()
			         .append("g")
                     .attr("class", "legend")
					 .attr("transform", function(d, i) { 
						 return "translate(-200," + (i * 20 + 80) + ")";
						 });  
                     //transform属性便是整个图例的坐标
        
        //绘制文字后方的颜色框
        legend.append("rect")
              .attr("x", width - 25) //width是svg的宽度,x,y属性用来调整位置
              .attr("y", -12)
              .attr("width", 50)	//颜色框宽度
              .attr("height", 15) //颜色框高度
              .style("fill", function(d){
                  return d.color
              });
        
        //绘制图例文字
		legend.append("text")
			.attr('fill','maroon')
            .attr("x", width - 30)
            .attr("y", 0)
            .style("text-anchor", "end") //样式对齐
            .text(function(d) { 
                return d.name;
			});         
		
		//按钮切换到柱状图
		d3.select('#btn-convert1').on('click', function(){	  
		 svg.remove();		
	  	 drawbar(dataset[0].datay,dataset[0].datax,dataset[0].week);		   
		}) ;  
	    d3.select('#btn-convert2').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[1].datay,dataset[1].datax,dataset[1].week);		   
	    }) ;  
	    d3.select('#btn-convert3').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[2].datay,dataset[2].datax,dataset[2].week);		   
	    }) ;  
	    d3.select('#btn-convert4').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[3].datay,dataset[3].datax,dataset[3].week);		   
	    }) ;  
	    d3.select('#btn-convert5').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[4].datay,dataset[4].datax,dataset[4].week);		   
	    }) ;  
	    d3.select('#btn-convert6').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[5].datay,dataset[5].datax,dataset[5].week);		   
	    }) ;  
	    d3.select('#btn-convert7').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[6].datay,dataset[6].datax,dataset[6].week);		   
	    }) ;  
	    d3.select('#btn-convert8').on('click', function(){	  
	     svg.remove();		
	     drawbar(dataset[7].datay,dataset[7].datax,dataset[7].week);		   
		}) ;  	
		d3.select('#btn-line').on('click', function(){	  
	     svg.remove();		
	     drawline();
		}) ;  
	}

	 drawbar(dataset[0].datay,dataset[0].datax,dataset[0].week);

	

CSS:

.tooltip{
    font-family:simsun;
    font-size:30px;
    width:120;
    height:auto;
    position:absolute; 
    text-align:center;
    border-style:solid;
    border-width:1px;
    background-color:white;
    border-radius:5px;	
}
#headline {
    font-size: 30px;
    width: 100%;
    margin-bottom: 10px;
    text-align: center;
}
.tool {
 margin-top: 20px;
 text-align: left;
 }	   
body{
    background:url(bcg.jpg);
    background-size:100%;
 }

背景图片:
在这里插入图片描述


转载请申明

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值