在每个D3条形区域的顶部添加图像

时间:2018-11-02 16:20:55

标签: javascript d3.js

我正在尝试在D3条形图的每个条形区域中添加图像。因此,首先,rect将在顶部rect边缘具有半径,并且图像将浮动(在顶部)。完成此操作后,我将尝试为每个条形矩形使用不同的图像(我的购物车中只有3个条形矩形)。我想要实现的是这里:

enter image description here

我的第一个无效尝试是在这里

  var data = [{"letter":"A","frequency":0.08167},{"letter":"B","frequency":0.01492},{"letter":"C","frequency":0.02782}];

  var svg = d3.select("svg"),
    margin = { top: 20, right: 20, bottom: 30, left: 40 },
    x = d3.scaleBand().padding(0.1),
    y = d3.scaleLinear();

  var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  g.append("g")
    .attr("class", "axis axis--x");

  g.append("g")
    .attr("class", "axis axis--y");

  g.append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", "0.71em")
    .attr("text-anchor", "end")
    .text("Frequency");

  // DRAWING

  function draw() {

    var bounds = svg.node().getBoundingClientRect(),
      width = bounds.width - margin.left - margin.right,
      height = bounds.height - margin.top - margin.bottom;

    x.rangeRound([0, width]);
    y.rangeRound([height, 0]);

    g.select(".axis--x")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

    g.select(".axis--y")
      .call(d3.axisLeft(y).ticks(10, "%"));

    var bars = g.selectAll(".bar")
      .data(theData);

    // ENTER
    bars
      .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function (d) { return x(d.letter); })
      .attr("y", function (d) { return y(d.frequency); })
      .attr("width", x.bandwidth())
      .attr("height", function (d) { return height - y(d.frequency); });

    // UPDATE
    bars.attr("x", function (d) { return x(d.letter); })
      .attr("y", function (d) { return y(d.frequency); })
      .attr("width", x.bandwidth())
      .attr("height", function (d) { return height - y(d.frequency); });

    // ADD IMAGE 
     bars.enter()
        .append('image')
        .attr({
          'xlink:href': 'http://www.clker.com/cliparts/P/Z/w/n/R/W/red-smiley-face-hi.png',
          x: function (d) { return x(d.letter); },
          y: 0,
          width: x.bandwidth(),
          height: 28
        });


    bars.exit().remove();

  }

      theData = data;

      x.domain(theData.map(function (d) { return d.letter; }));
      y.domain([0, d3.max(theData, function (d) { return d.frequency; })]);

      draw();
  
  window.addEventListener("resize", draw);
  .bar {
    fill: purple;
  }

  .bar:hover {
    fill: pink;
  }

  .axis--x path {
    display: none;
  }
<svg width="100%" height="300px"></svg>

<script src="https://d3js.org/d3.v4.min.js"></script>

然后,我尝试查看该想法是否在其他D3条形图中起作用(我将不使用它,因为它没有响应能力)。该图像正常显示,但y位置必须更改。由于需要在第一个条形图上进行操作,因此我不会在此代码段上做进一步的工作:

var nutritionFields = ["calories", "protein", "sodium"];

var data = [
  {
    "cereal": "100%_Bran",
    "manufacturer": "Nabisco",
    "type": "C",
    "calories": 70,
    "protein": 4,
    "fat": 1,
    "sodium": 130,
    "fiber": 10,
    "carbs": 5,
    "sugars": 6,
    "shelf": 3,
    "potassium": 280,
    "vitamins": 25,
    "serving size weight": 1,
    "cups per serving": 0.33
  },
  {
    "cereal": "100%_Natural_Bran",
    "manufacturer": "Quaker Oats",
    "type": "C",
    "calories": 120,
    "protein": 3,
    "fat": 5,
    "sodium": 15,
    "fiber": 2,
    "carbs": 8,
    "sugars": 8,
    "shelf": 3,
    "potassium": 135,
    "vitamins": 0,
    "serving size weight": 1,
    "cups per serving": -1
  },
  {
    "cereal": "All-Bran",
    "manufacturer": "Kelloggs",
    "type": "C",
    "calories": 70,
    "protein": 4,
    "fat": 1,
    "sodium": 260,
    "fiber": 9,
    "carbs": 7,
    "sugars": 5,
    "shelf": 3,
    "potassium": 320,
    "vitamins": 25,
    "serving size weight": 1,
    "cups per serving": 0.33
  },
  {
    "cereal": "All-Bran_with_Extra_Fiber",
    "manufacturer": "Kelloggs",
    "type": "C",
    "calories": 50,
    "protein": 4,
    "fat": 0,
    "sodium": 140,
    "fiber": 14,
    "carbs": 8,
    "sugars": 0,
    "shelf": 3,
    "potassium": 330,
    "vitamins": 25,
    "serving size weight": 1,
    "cups per serving": 0.5
  },
  {
    "cereal": "Almond_Delight",
    "manufacturer": "Ralston Purina",
    "type": "C",
    "calories": 110,
    "protein": 2,
    "fat": 2,
    "sodium": 200,
    "fiber": 1,
    "carbs": 14,
    "sugars": 8,
    "shelf": 3,
    "potassium": -1,
    "vitamins": 25,
    "serving size weight": 1,
    "cups per serving": 0.75
  },
  {
    "cereal": "Apple_Cinnamon_Cheerios",
    "manufacturer": "General Mills",
    "type": "C",
    "calories": 110,
    "protein": 2,
    "fat": 2,
    "sodium": 180,
    "fiber": 1.5,
    "carbs": 10.5,
    "sugars": 10,
    "shelf": 1,
    "potassium": 70,
    "vitamins": 25,
    "serving size weight": 1,
    "cups per serving": 0.75
  },
  {
    "cereal": "Apple_Jacks",
    "manufacturer": "Kelloggs",
    "type": "C",
    "calories": 110,
    "protein": 2,
    "fat": 0,
    "sodium": 125,
    "fiber": 1,
    "carbs": 11,
    "sugars": 14,
    "shelf": 2,
    "potassium": 30,
    "vitamins": 25,
    "serving size weight": 1,
    "cups per serving": 1
  }
];

                var cerealMap = {};
                data.forEach(function(d) {
                    var cereal = d.cereal;
                    cerealMap[cereal] = [];

                    // { cerealName: [ bar1Val, bar2Val, ... ] }
                    nutritionFields.forEach(function(field) {
                        cerealMap[cereal].push( +d[field] );
                    });
                });
                
      

            var makeVis = function(cerealMap) {
                // Define dimensions of vis
                var margin = { top: 30, right: 50, bottom: 30, left: 50 },
                    width  = 550 - margin.left - margin.right,
                    height = 250 - margin.top  - margin.bottom;

                // Make x scale
                var xScale = d3.scale.ordinal()
                    .domain(nutritionFields)
                    .rangeRoundBands([0, width], 0.1);

                // Make y scale, the domain will be defined on bar update
                var yScale = d3.scale.linear()
                    .range([height, 0]);

                // Create canvas
                var canvas = d3.select("#dropdown")
                .append("svg")
                .attr("width","100%")
                .attr("height","100%")
                .attr("viewBox","0 0 "+
                    (width+margin.left+margin.right)+
                    " "+
                    (height+margin.top+margin.bottom) )
                .append("g")
                .attr("transform","translate("+
                    margin.left+","+margin.top+")");

                // Make x-axis and add to canvas
                var xAxis = d3.svg.axis()
                    .scale(xScale)
                    .orient("bottom");

                canvas.append("g")
                    .attr("class", "x axis")
                    .attr("transform", "translate(0," + height + ")")
                    .call(xAxis);

                // Make y-axis and add to canvas
                var yAxis = d3.svg.axis()
                    .scale(yScale)
                    .orient("left");

                var yAxisHandleForUpdate = canvas.append("g")
                    .attr("class", "y axis")
                    .call(yAxis);

                yAxisHandleForUpdate.append("text")
                    .attr("transform", "rotate(-90)")
                    .attr("y", 6)
                    .attr("dy", ".71em")
                    .style("text-anchor", "end")
                    .text("Value");

                var updateBars = function(data) {
                    // First update the y-axis domain to match data
                    yScale.domain( d3.extent(data) );
                    yAxisHandleForUpdate.call(yAxis);

                    var bars = canvas.selectAll(".bar").data(data);

                    // Add bars for new data
                    bars.enter()
                      .append("rect")
                        .attr("class", "bar")
                        .attr("x", function(d,i) { return xScale( nutritionFields[i] ); })
                        .attr("width", xScale.rangeBand())
                        .attr("y", function(d,i) { return yScale(d); })
                        .attr("height", function(d,i) { return height - yScale(d); });
									 
                   bars.enter()
                   		.append('svg:image')
                      .attr({
                        'xlink:href': 'http://www.clker.com/cliparts/P/Z/w/n/R/W/red-smiley-face-hi.png',  // can also add svg file here
                        x: function(d,i) { return xScale( nutritionFields[i] ); },
                        y: 0,
                        width: xScale.rangeBand(),
                        height: 28
                      });
                   
                    // Update old ones, already have x / width from before
                    bars
                        .transition().duration(250)
                        .attr("y", function(d,i) { return yScale(d); })
                        .attr("height", function(d,i) { return height - yScale(d); });

                    // Remove old ones
                    bars.exit().remove();
                };

                // Get names of cereals, for dropdown
                var cereals = Object.keys(cerealMap).sort();

                var initialData = cerealMap[ cereals[0] ];
                updateBars(initialData);
            };
            
            makeVis(cerealMap);
        select {
            display: block;
        }
        .bar {
            fill: purple;
            opacity: 0.8;
            border-radius:15px;
        }

        .axis path,
        .axis line {
            fill: none;
            stroke: #000;
            shape-rendering: crispEdges;
        }
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>

<div id="dropdown"></div>

1 个答案:

答案 0 :(得分:0)

您几乎拥有它,但是您以非标准的方式在图像元素上设置了属性。注意,我添加了prepareAspectRatio = None和top = 1/2高度(-14px),因此它与您的示例图片更加匹配。如果要使宽高比与宽度匹配,则需要计算高度和顶部。另外,您还需要将高度增加最高图像的1/2高度,以免被截断。

// ADD IMAGE 
 bars.enter()
    .append('image')
    .attr('xlink:href', 'http://www.clker.com/cliparts/P/Z/w/n/R/W/red-smiley-face-hi.png')
    .attr("width", x.bandwidth())
    .attr("height", "28px")
    .attr("y", function (d) { return y(d.frequency) - 14; })
    .attr("x", function (d) { return x(d.letter); })
    .attr("preserveAspectRatio", "none");

请注意,y属性是计算得出的。