D3.js 柱状图的拖拽动画效果

起因:


不久之前遇到了一个朋友、叫吴钟深,他说他刚刚入门数据可视化,本来在纠结是使用大家都在用的Echarts还是学一个更加自由灵活的可视化库,后来还是学了D3.js。在观摩学习了各个大佬的教学之后初步掌握了D3的基本api的使用,但是他说他们项目经理最近想要把图表的翻页按钮给干掉,他不知道用那种方法代替翻页按钮。我作为一个练习时长两个半月的前端数据可视化菜鸟当场就表示直接使用D3的拖拽不就好了吗。

//drag
var startX = undefined;
function dragstarted(event, d) {
  startX = event.x
}
function dragged(event, d) {
  var glx = startX - event.x
  if (glx >= 70) {
    console.log(1)
    startX = event.x; 
  }
  if (glx <= -70) {
    console.log(2)
    startX = event.x; 
  }
}
function dragended() {
  startX = undefined;
}

我给他说你就用d3.drag()把这3个function挂在你的svg上面测试一下,自己把console.log给换成刷新图表翻页就行了。如果是纵向的图表就把event.x换成event.y,其他参数稍微改改名字就行了。他一听豁然开朗,说这就回去试试。

几天后。。。


过了几天我又看到他了,连忙问问他的项目怎么样了,他一脸愁容的给我说拖拽确实可以把翻页按钮干掉。不过拖拽的操作和自己以前写的动画不是很匹配。我让他打开电脑给我康康。他的柱状图的翻页效果是每个rect的高进行一个平滑过渡动画。之前有翻页按钮确实没什么问题,但现在确实不匹配拖拽的操作。我说其实也简单,直接把动画的duration改成0,然后图表改成一条一条刷新而不是整页数据刷新就行了。调整一下拖拽判定的距离、看起来是整个图表被拖过来。

他说那不就和直接把图表remove再重新生成一个一样了吗。我想了想,确实。但也不影响。他说他们项目经理还是想要一个动画效果,刚刚这个肯定是过不了的。
我没办法,只好登录我的代码仓库把我之前在公司摸鱼随便写的代码下下来。

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <link rel="stylesheet" href="./style.css">
    </head>

    <body>
        <!-- <script src="../../d3.min.js"></script> -->
        <script src="https://d3js.org/d3.v6.min.js"></script>
        <div id="cont" class="cont" style="width:60%; margin-top: 100px;">
            <div id="wrapper1"></div>
        </div>
        <script src="./bar_chart.js"></script>
    </body>

</html>


 

//各种参数设置
  var width = 560
  var rectH = 30;//rect宽

  let dimensions = {
    width: width,//条形图宽
    width2: width * 0.9,//饼图宽
    height: 520,//  width*0.6
    pieHeight: 400,

    margin: {
      top: 20,
      right: 30,
      bottom: 80,
      left: 100,
      left2: 20
    }
  }

  var avgLen = data1.length
  dimensions.boundedWidth =
    dimensions.width - dimensions.margin.left - dimensions.margin.right
  dimensions.boundedHeight =
    dimensions.height - dimensions.margin.top - dimensions.margin.bottom

  var colorList = ['#6D6AF4', '#22B061', '#AfD0B9', '#92a8d1', '#F0B4F0', '#86af49', '#92a8d1', '#f6f1e9', '#4fc9da']

  const xAccessor = (d) => d.group
  const yAccessor = (d) => d.var1
  
  const svg1 = d3.select('#wrapper1').append('svg')
    .attr('width', dimensions.width).attr('height', dimensions.height)
//拖拽动画
function drawBar(metric, t) {
    //g
    var g = svg1.append('g')
      .attr('transform', 'translate(' + dimensions.margin.left + ',' + dimensions.margin.top + ')')

    var gs = g.selectAll('rect').data(metric).enter().append('g');
    //比例尺
    var scaleY = d3.scaleLinear()
      .range([dimensions.boundedHeight, 0])
      .domain([0, d3.max(metric.map(yAccessor))]).nice();
    var scaleX = d3.scaleBand()
      .range([0, dimensions.boundedWidth])
      .domain(metric.map(xAccessor));

    g.selectAll('rect').data(metric).enter()
      .append('rect')
      .attr('x', (d) => scaleX(xAccessor(d)))
      .attr('y', dimensions.boundedHeight)
      .attr('width', rectH)
      .attr('height', 0)
      .attr('rx', 5)
      .attr('fill', colorList[4])
      //.attr('id', function (d,i) {return 'rect'+ i})
      .transition()
      .duration(t)
      .attr('y', (d) => scaleY(yAccessor(d)))
      .attr('height', (d) => dimensions.boundedHeight - scaleY(yAccessor(d)))
  }
  drawBar(data1.slice(page1, page1 + 5), 500)
  // drag bar chart
  function dragBar(metric, metric2, t, dr) {
    //g
    var g = svg1.append('g')
      .attr('transform', 'translate(' + dimensions.margin.left + ',' + dimensions.margin.top + ')')

    //比例尺
    var scaleY = d3.scaleLinear()
      .range([dimensions.boundedHeight, 0])
      .domain([0, d3.max(metric.map(yAccessor))]).nice();
    var scaleX = d3.scaleBand()
      .range([0, dimensions.boundedWidth])
      .domain(metric.map(xAccessor));

    var scaleY2 = d3.scaleLinear()
      .range([dimensions.boundedHeight, 0])
      .domain([0, d3.max(metric2.map(yAccessor))]).nice();
    var scaleX2 = d3.scaleBand()
      .range([0, dimensions.boundedWidth])
      .domain(metric2.map(xAccessor));

    g.selectAll('rect').data(metric.slice((1 - dr) / 2, (9 - dr) / 2)).enter()
      .append('rect')
      .attr('x', (d) => scaleX(xAccessor(d)))
      .attr('y', (d) => scaleY(yAccessor(d)))
      .attr('height', (d) => dimensions.boundedHeight - scaleY(yAccessor(d)))
      .attr('width', rectH)
      .attr('rx', 5)
      .attr('fill', colorList[4])
      .transition()
      .duration(t)
      .attr('x', (d) => scaleX(xAccessor(d)) + dimensions.boundedWidth / 5 * dr)
      .attr('y', (d) => scaleY2(yAccessor(d)))
      .attr('height', (d) => dimensions.boundedHeight - scaleY2(yAccessor(d)))

    g.append('rect')
      .attr('x', scaleX(xAccessor(metric[2 + 2 * dr])))
      .attr('y', scaleY(yAccessor(metric[2 + 2 * dr])))
      .attr('height', dimensions.boundedHeight - scaleY(yAccessor(metric[2 + 2 * dr])))
      .attr('width', rectH)
      .attr('rx', 5)
      .attr('fill', colorList[4])
      .transition()
      .duration(t)
      .attr('x', scaleX(xAccessor(metric[2 + 2 * dr])) + dimensions.boundedWidth / 5 * dr)
      .attr('y', dimensions.boundedHeight)
      .attr('height', 0)

    g.append('rect')
      .attr('x', scaleX2(xAccessor(metric2[2 - 2 * dr])) - dimensions.boundedWidth / 5 * dr)
      .attr('y', dimensions.boundedHeight)
      .attr('height', 0)
      .attr('width', rectH)
      .attr('rx', 5)
      .attr('fill', colorList[4])
      .transition()
      .duration(t)
      .attr('x', scaleX2(xAccessor(metric2[2 - 2 * dr])))
      .attr('y', scaleY2(yAccessor(metric2[2 - 2 * dr])))
      .attr('height', dimensions.boundedHeight - scaleY2(yAccessor(metric2[2 - 2 * dr])))
  }

  //拖拽
  let GoR = (d) => d + 6 > GetLength(data1) ? d : d + 1;
  let GoL = (d) => d - 1 < 0 ? d : d - 1;

  svg1.call(d3.drag().on("start", dragstarted)
    .on("drag", function (event, d) {
      var glx = startX - event.x
      if (glx >= 70) {
        startX = event.x;
        page1 = GoR(page1);
        if (page1 != page2) {
          svg1.selectAll('g').remove();
          dragBar(data1.slice(page2, page2 + 5), data1.slice(page1, page1 + 5), 300, -1)
        }
        page2 = page1
      }
      if (glx <= -70) {
        startX = event.x;
        page1 = GoL(page1);
        if (page1 != page2) {
          svg1.selectAll('g').remove();
          dragBar(data1.slice(page2, page2 + 5), data1.slice(page1, page1 + 5), 300, 1)
        }
        page2 = page1
      }
    })
    .on("end", dragended))

这个动画满足了他平移的动画效果,function dragBar(metric, metric2, t, dr)中metric是拖动之前图表显示的数据,metric2是拖拽之后图表显示的数据,t是动画时间,dr是拖动的方向。在function dragBar中第一个g.append('rect')是将移动方向上的后4个rect(除了方向上的第一个,如向左移动则是除去左边第一个rect)的移动动画。后面两个g.append('rect')则是移动方向上第一个与最后一个rect的移出与进入的动画。

效果如下:

(效果不是很好,轻喷ヾ(≧O≦)〃)

 


我的朋友看完后十分满意。说就决定这么做了。接着他转头又说能不能帮他一个忙,我问他什么忙?他说他们公司要他们写软文为他们的产品打广告。我说等等,你们公司不是做嵌入式仿真平台的吗?你一个刚刚做前端的菜鸡为什么要干这个?
他( * ̄▽ ̄)((≧︶≦*)

于是


---------------------------------------------------------------------------------------------
好消息、好消息

一款线上时序级嵌入式仿真平台对个人开放了。(o゜▽゜)o☆[BINGO!]

这款是一个国产的在线虚拟仿真系统,采用时序级仿真硬件,能真实还原硬件时序,界面简洁,支持不同的处理器,拥有超过一百种的外设,与哈工大等数十所高校合作。  
它支持用户在线构建自己的电路,然后配置好引脚,编写好代码就可以在线进行编译,也可以在线运行,运行之后能直接在右边的电路图那儿看到是否是自己期望的效果。而编译好的可执行文件支持下载到本地,烧录进单片机就可以直接运行。只要用户线下搭建的硬件电路和我线上搭建的一致,那么线下的效果和线上的仿真结果就是一致的。

 

不仅如此还有3D场景实验课。

 

[深圳航天科技创新研究院嵌入式仿真实验教学平台](https://app.puliedu.com/) 这是央企深圳航天研究院开发的,因为之前是to B项目,目前需要注册账号后通过审核才可使用,所以建议同学们注册时填写真实手机号,因为会有工作人员来联系核对的。有意向的同学快去体验体验吧(´▽`ʃ♡ƪ)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对比柱状图可以使用d3.js中的矩形图(rect)元素来绘制。以下是一个简单的例子: HTML代码: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3.js对比柱状图</title> <script src="https://d3js.org/d3.v5.min.js"></script> </head> <body> <svg id="chart" width="400" height="300"></svg> <script src="script.js"></script> </body> </html> ``` JavaScript代码: ```javascript // 数据 var data = [ {name: 'A', value: 20}, {name: 'B', value: 35}, {name: 'C', value: 10}, {name: 'D', value: 15}, {name: 'E', value: 25} ]; // 定义svg画布大小 var width = 400; var height = 300; // 定义比例尺 var xScale = d3.scaleBand() .domain(data.map(function(d) { return d.name; })) .range([0, width]) .padding(0.1); var yScale = d3.scaleLinear() .domain([0, d3.max(data, function(d) { return d.value; })]) .range([height, 0]); // 创建svg元素 var svg = d3.select('#chart') .append('g') .attr('transform', 'translate(' + 50 + ',' + 10 + ')'); // 绘制矩形 svg.selectAll('rect') .data(data) .enter() .append('rect') .attr('x', function(d) { return xScale(d.name); }) .attr('y', function(d) { return yScale(d.value); }) .attr('width', xScale.bandwidth()) .attr('height', function(d) { return height - yScale(d.value); }) .style('fill', 'steelblue'); ``` 解释: 1. 定义数据,包含每个柱子的名称和值。 2. 定义画布大小。 3. 定义x轴和y轴的比例尺,x轴比例尺使用`scaleBand`,y轴比例尺使用`scaleLinear`。 4. 创建svg元素,并将其偏移50像素到右侧和10像素到下方。 5. 绘制矩形,使用`selectAll`和`data`方法绑定数据,使用`enter`方法进入数据,使用`append`方法添加矩形元素,使用`attr`方法设置x、y、width和height属性,使用`style`方法设置颜色。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值