• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

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

武飞扬头像
Simon_c_c
帮助1

起因:

不久之前遇到了一个朋友、叫吴钟深,他说他刚刚入门数据可视化,本来在纠结是使用大家都在用的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="https://juejin.cn/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☆[BINGO!]

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

学新通

学新通

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

学新通

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

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgiigjk
系列文章
更多 icon
同类精品
更多 icon
继续加载