D3.js + CSS按钮功能

时间:2016-06-28 17:31:42

标签: css d3.js

我一直在研究这个特殊图表,这是一张来自Mike Bostock模板的修改后的子弹图。我希望通过一些按钮事件来增强它,特别是:当您单击按钮时,它将调用过渡动画以将图形标记更改为新值。原始数据表单中的代码中都提供了旧值和旧值。为了清晰和背景,我已将代码全部包含在内。搜索我的评论“//下面的混淆”,以找到我定义按钮功能的部分。

<!DOCTYPE html>
<html>
<head>
  <title>Bullet Chart</title>
  <meta charset="utf-8">
</head>
<style>
  body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  margin: auto;
  padding-top: 40px;
  position: relative;
  /*width: 100%;*/
}
table{
  width:60%;
  margin-left:auto; 
  margin-right:auto;
}
td{width:50%;}
.bulleT { font: 10px sans-serif; margin-left:auto;margin-right:auto;}
.bulleT .marker { stroke: #4D4D4D; stroke-width: 2px;}
.bulleT .marker.s0 { fill-opacity:0; stroke: #999999; stroke-width: 2px; }
.bulleT .marker.s1 { fill-opacity:0; stroke: #000; stroke-width: 2px; }
.bulleT .tick line { stroke: #666; stroke-width: .5px; }

.bulleT .range.s0 { fill: #005C7A; }
.bulleT .range.s1 { fill: #29A3CC; }
.bulleT .range.s2 { fill: #c6dbef; }
.bulleT .range.s3 { fill: #29A3CC; } 
.bulleT .range.s4 { fill: #005C7A; }

.bulleT .measure.s0 { fill: #4D4D4D; }
.bulleT .measure.s1 { fill: #999999; } 
.bulleT .measure.s2 { fill: #eeeeee; }
.bulleT .measure.s3 { fill: #999999; }
.bulleT .measure.s4 { fill: #4D4D4D; }

.bulleT .title { font-size: 12px; font-weight: bold; }
.bulleT .subtitle.s04 { fill: #000000; font-size: 16px; font-weight: bold;} 
.bulleT .subtitle.s13 { fill: #999999; font-size: 12px; font-weight: bold;}
.bulleT .subtitle.s2  { fill: #999999; font-size: 10px;}
  
.option {
	font-family: Play;
	color: #ffffff;
	font-size: 12px;
  width: 6%;
	background: #303030;
	padding: 10px 20px 10px 20px;
}

.option:hover {
	background: #4c4d4d;
}

div#option2{
  position:relative;
  top: 10px;
}

</style>
<body>
  <script src="http://d3js.org/d3.v3.min.js"></script>
<div id="canvas-svg">
<div id="canvas-svg">
  <table>
    <tr>
      <td>
        <div id="BulleT_horizontal"></div>
      </td>
    </tr>
  </table>
</div>

<div id="option1" class="option"> Outcome 1</div>
<div id="option2" class="option"> Outcome 2</div>
<p>The Bullet Chart is here.</p>
<script>

(function() {
// Simple modification based on mbostock's Bullet Charts.
d3.bulleT = function() {
  var orient = "left",
      reverse = false,
      vertical = false,
      terjedelem = bulleTTerjedelem,
      ranges = bulleTRanges,
      markers = bulleTMarkers,
      measures = bulleTMeasures,
      width = 380,
      height = 30,
      tickFormat = null;

  // For each small multiple
  function bulleT(g) {
    g.each(function(d, i) {
      var terjedelemz = terjedelem.call(this, d, i),
          rangez = ranges.call(this, d, i).slice().sort(d3.descending),
          markerz = markers.call(this, d, i),
          measurez = measures.call(this, d, i).slice().sort(d3.descending),
          g = d3.select(this);

      var wrap = g.select("g.wrap");

      if (wrap.empty()) wrap = g.append("g").attr("class", "wrap");
      // Compute the x-scale.
      var x0 = d3.scale.linear()
          .domain([terjedelemz[0], terjedelemz[1]])
          .range(reverse ? [width, 0] : [0, width]);

      // Stash the new scale.
      this.__chart__ = x0;

      // Derive width-scales from the x-scales.
      var w = bulleTWidth(x0,terjedelemz[0]);

      // Update the range rects.
      rangez.unshift(terjedelemz[1]);
      var range = wrap.selectAll("rect.range")
          .data(rangez);
      range.enter().append("rect")
          .filter( function(d, i){ if(i != 3){ return d} })
          .attr("class", function(d, i) { return "range s" + i; })
          .attr("width", w)
          .attr("y", 0)
          .attr("height",height)
          .attr("x", reverse ? x0 : 0);
      range.enter().append("line")
          .filter( function(d, i){ if(i == 3){ return d} })
          .attr("class", "marker")
          .attr("x1", x0)
          .attr("x2", x0)
          .attr("y1", 0)
          .attr("y2", height);

      // Append the measure rects.
      measurez.unshift(terjedelemz[1]);
      var measure = wrap.selectAll("rect.measure")
          .data(measurez);
      measure.enter().append("rect")
          .attr("class", function(d, i) { return "measure s" + i; })
          .attr("width", w)
          .attr("height", height / 2)
          .attr("x", reverse ? x0 : 0)
          .attr("y", height / 4);
      // Append rect and line marker.
      var marker = wrap.selectAll("rect.marker")
          .data(markerz);
      marker.enter().append("rect")
          .filter( function(d, i){ if(i == 1){ return d} })
          .attr("class", "marker s1")
          .attr("width", 6)
          .attr("y", -(height/10))
          .attr("height",function(d) {return height+(height/5);})
          .attr("x", x0)
          .attr("transform", "translate(-3,0)");
//confusion below
      var option1 = d3.select("#option1");
      option1.on('click.outcome', outcome1);
      //option1.on('click.val', val=[100,200,1000,20]);

      var option2 = d3.select("#option2");
      option2.on('click.outcome', outcome2);

      function outcome1(val) {
        d.markerz = [1000, 2000, 10, 20]
        d3.selectAll('rect.marker')
        .transition()
        .duration(3000)
        .attr("x", function(d) {return d})
      }

      function outcome2(val) {
        d.markerz = [-1000, -2000, -10, -20]
        d3.selectAll('rect.marker')
        .transition()
        .duration(3000)
        .attr("x", function(d) {return d})
      };

      marker.enter().append("line")
          .filter( function(d, i){ if(i == 0){ return d} })
          .attr("class", "marker s0")
          .attr("x1", x0)
          .attr("x2", x0)
          .attr("y1", height / 4)
          .attr("y2", height-(height / 4) );

      // Compute the tick format.
      var format = tickFormat || x0.tickFormat(8);

      // Update the tick groups.
      var tick = g.selectAll("tick")
          .data(x0.ticks(8), function(d) {
            return this.textContent || format(d);
          });

      // Initialize the ticks with the old scale, x0.
      var tickEnter = tick.enter().append("g")
          .attr("class", "tick")
          .attr("transform", bulleTTranslate(x0))
          .style("opacity", 1);

      tickEnter.append("line")
          .attr("y1", height)
          .attr("y2", height * 7 / 6);

      tickEnter.append("text")
          .attr("text-anchor", "middle")
          .attr("transform", function(d){
            if (vertical) {
              return "rotate(90)";
            }
          })
          .attr("dy",  function(d){
            if(vertical){return width/60; }else{ return height+15 }
          })
          .attr("dx",  function(d){
            if(vertical){return height+15 ;}
          })
          .text(format);
    });
  }

  // left, right, top, bottom
  bulleT.orient = function(x) {
    if (!arguments.length) return orient;
    orient = x;
    reverse = orient == "right" || orient == "bottom";
    return bulleT;
  };

  // terjedelem
  bulleT.terjedelem = function(x) {
    if (!arguments.length) return terjedelem;
    terjedelem = x;
    return bulleT;
  };

  // ranges (bad, satisfactory, good)
  bulleT.ranges = function(x) {
    if (!arguments.length) return ranges;
    ranges = x;
    return bulleT;
  };
//*
  // markers (previous, goal)
  bulleT.markers = function(x) {
    if (!arguments.length) return markers;
    markers = x;
    return bulleT;
  };

  // measures (actual, forecast)
  bulleT.measures = function(x) {
    if (!arguments.length) return measures;
    measures = x;
    return bulleT;
  };
//*/
  bulleT.vertical = function(x) {
    if (!arguments.length) return vertical;
    vertical = x;
    return bulleT;
  };
  bulleT.width = function(x) {
    if (!arguments.length) return width;
    width = x;
    return bulleT;
  };

  bulleT.height = function(x) {
    if (!arguments.length) return height;
    height = x;
    return bulleT;
  };

  bulleT.tickFormat = function(x) {
    if (!arguments.length) return tickFormat;
    tickFormat = x;
    return bulleT;
  };
  return bulleT;
};

function bulleTTerjedelem(d) {
  return d.terjedelem;
}

function bulleTRanges(d) {
  return d.ranges;
}

function bulleTMarkers(d) {
  return d.markers;
}

function bulleTMeasures(d) {
  return d.measures;
}

function bulleTTranslate(x) {
  return function(d) {
    return "translate(" + x(d) + ",0)";
  };
}

function bulleTWidth(x,y) {
  var x0 = x(0);
  return function(d) {
    return Math.abs(x(d-y) - x0);
  };
}

})();

var Tscore_Man_Height = -1019;
var Tscore_Woman_Height = -261;
var Tscore_Man_Weight = -4.64;
var Tscore_Woman_Weight = -1.6;

var Tscore2 = 1300
// terjedelem is the hungarian translation of the statistical term of range
var data = [
  {"title":"Exper","dimension":"(diff)","subtitle":Tscore_Man_Height,"terjedelem":[-3000,3000],"ranges":[ -2000, -1000, 0, 1000, 2000],"measures":[-1366,-676,605,1108.81],"markers":[-23,Tscore_Man_Height]},
  {"title":"Gold","dimension":"(diff)","subtitle":Tscore_Woman_Height,"terjedelem":[-8000,8000],"ranges":[-5250,-2750,0,2750,5250],"measures":[-901,110,845.5,2107.5],"markers":[412,Tscore_Woman_Height]},
  {"title":"Tech","dimension":"(diff)","subtitle":Tscore_Man_Weight,"terjedelem":[-20,20],"ranges":[-13.33,-6.66,0,6.66,13.33],"measures":[-8.7,-.9,4.9,10.7],"markers":[1,Tscore_Man_Weight]},
  {"title":"Units","dimension":"(diff)","subtitle":Tscore_Woman_Weight,"terjedelem":[-80,80],"ranges":[-53.33,-26.66,0,26.66,53.33],"measures":[-18,-7.8,3.8,23],"markers":[-3,Tscore_Woman_Weight]}
]
var Width = 400, Height = 50;

var margin = {top: 5, right: 20, bottom: 20, left: 60},
    width = Width - margin.left - margin.right,
    height = Height - margin.top - margin.bottom;

var chart = d3.bulleT()
    .width(width)
    .height(height);

function bulleT(whichData,whereToPut,direction) {
  var a=Width, b=Height;
  if( direction == "vertical"){
    Height=a;Width=b+30;
    vertical = true;
  }else{
    Height=a-20;Width=b;
    vertical = false;
  }

  var svg = d3.select(whereToPut).selectAll("svg")
      .data(whichData)
    .enter().append("svg")
      .attr("class", "bulleT")
      .attr("width", Width)
      .attr("height", Height)
    .append("g")
      .attr("transform", function(){
        if( direction == "vertical"){
          return "rotate(-90)translate("+ -(Height-margin.left) +",10)";
        }else{
          return "translate("+ margin.left +","+ margin.top +")";
        }
      })
      .call(chart.vertical(vertical));

  var title = svg.append("g")
      .style("text-anchor", function(){
        if( direction == "vertical"){
          return "middle";
        }else{
          return "end";
        }
      })
      .attr("transform", function(){
        if( direction == "vertical"){
          return "rotate(90)translate("+ Width/4 +",20)";
        }else{
          return "translate(-16," + height / 3 + ")";
        }
      });

  title.append("text")
      .attr("class", "title")
      .text(function(d) { return d.title; });

  title.append("text")
      .attr("dy", "1.2em")
      .text(function(d) { return d.dimension; })

  title.append("text")
      .attr("class",function(d) {
          switch (true)
          {
            case ( (d.markers[1] < 30) || (70 < d.markers[1]) ):
              return "subtitle s04";
              break;
              break;
            case ( (30 <= d.markers[1]) && (d.markers[1] < 40) ):
              return "subtitle s13";
              break;
            case ( (40 <= d.markers[1]) && (d.markers[1] <= 60) ):
              return "subtitle s2";
              break;
            case ( (60 < d.markers[1]) && (d.markers[1] <= 70) ):
              return "subtitle s13";
              break;
          }
        }
      )
      .attr("dy", function(){
        return "2.4em";
      })
      .text(function(d) { return d.subtitle; });
};


bulleT(data,"#BulleT_vertical","vertical");  // "horizontal" or "vertical"
bulleT(data,"#BulleT_horizontal","horizontal");
</script>
</body>
</html>

当我点击按钮时,即使我将markerz更改为不同的值,它们也会做同样的事情。而且,它们甚至没有做正确的事情,价值似乎是错误的,因为一些标记从图形边界飞出。所以无论我做什么似乎都没有改变数据解析的方式。另外,我不完全确定这个原始值数据会缩放到图表的x刻度,或者如果我需要在它通过我的按钮功能时再次缩放它。缩放似乎与上面代码中的x0有关。由于某种原因,我无法使用我的按钮。

这里有一个类似的更新块,它具有功能按钮功能:

http://bl.ocks.org/CodeXmonk/6187523

我的图表不同之处在于我不会更改度量或标记所在位置以外的任何其他内容。而且我没有随机化数据,我预先编码了我想要使用我的css按钮来回切换的原始数据。

所以我正在寻找的是如何编写按钮来处理预先存在的比例并加载原始数据并将其放入markerz或.transition()动画的其他变量。这样,按钮将具有在图表上移动标记所需的效果。

抱歉,我无法缩短时间。我希望这会值得每个人,但因为d3.js + css界面是一个非常强大的组合。我希望我们都可以从这个例子中学习。

感谢您阅读

2 个答案:

答案 0 :(得分:0)

进一步辛苦工作后,我有个好消息。我通过纯粹的反复试验得到了它。我会在这里发布部分答案。我设法得到一个功能图表,使用.transition()与像素形式的预编码原始数据。注意:我实现此功能的唯一方法是为转换输入x的像素值。理想情况下,我只想输入原始数据(以数据形式)并使用现有比例处理它以自动计算像素值。我现在不会太挑剔。这是我到目前为止所做的。我煞费苦心地发布了自己的要点,因为我希望它比上面冗长的代码更容易查看。

https://bl.ocks.org/diggetybo/83188e161c39f2d0f7025087598ec075

我仍然需要以下答案:

  1. 如何利用现有的比例以及与我的css按钮功能相结合的原始数据

  2. 如何更新字幕文字?它不知道我更新了值,因此他们总是阅读&#34; 1153,1506,8.63,5.86&#34;。理想情况下,当我点击结果1时,我希望它们反映新的标记位置:&#34; -1019,-261,-4.64,-1.6和#34;。然后仍然可以恢复到&#34; 1153,1506,8.63,5.86&#34;点击结果2按钮。

  3. 关于2.,我尝试复制并粘贴title.append片段作为标记值,​​并使用另一个鼠标事件作为cue,但我做的每种方式都返回了一个错误。任何有经验的d3人都知道如何做到这一点?

    最后,我不确定原因,但每次点击按钮时,开发工具错误计数都会增加1.错误是,&#34;某些东西不是函数&#34;。我猜这不是一个好兆头。但是,该图表在大多数情况下都有效。我不知道我应该对这些错误感到害怕。

    再次感谢

答案 1 :(得分:-1)

我认为你必须为每个变量使用document.ready函数,然后给出.on点击功能。