动态创建可滚动的SVG

时间:2017-04-04 14:30:14

标签: javascript css d3.js svg

我正在尝试创建一个可滚动的列表。我在这里看过其他教程,但它似乎没有用。基本上我将SVG附加到div。在这个SVG里面是一个D3JS堆叠条形图。在这个条形图的右边,我附加一个带有svg的'g'元素。我为这个正确的SVG设置了一个高度。在这里我已经填充了一个超出SVG高度的列表。我已将此svg的CSS设置为'overflow-y:scroll'。

尽管如此,我还是不能让这个svg滚动。相反,它只会增长到列表的大小并延伸到预期的范围。请参阅下面的代码。

var barSVG = d3.select("#documents_reviewed_bar_chart").append("svg")
    .classed('barChart-docs-reviewed', true)
    .attr('id', 'barSVG')
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr('id', 'gElement')
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");    

var count = 0;

var graph = barSVG.append('g')
    .attr('id', 'graphElement')

color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Date"; }));


data.forEach(function(d) {
  var myDate = d.Date; //add to stock code
  var y0 = 0;

  d.people = color.domain().map(function(name) { return {myDate:myDate, name: name, y0: y0, y1: y0 += +d[name]}; });
  d.total = d.people[d.people.length - 1].y1;
  count = isNaN(d.total) ? count : count + d.total

});


x.domain(data.map(function(d) { return d.Date; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);

graph.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
     .selectAll("text")  
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", "rotate(-65)" )
        .style("cursor", "pointer")
        .on('click', renderHorizontalChart);

graph.append("g")
    .attr("class", "y axis")
    .call(yAxis)
  .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end");
    //.text("Population");

graph.append('text')
  .text('Total: ' + count)
  .attr('x', 20)
  .attr('y', -10)


var state = graph.selectAll(".state")
    .data(data)
  .enter().append("g")
    .attr("class", "g")
    .attr("transform", function(d) { return "translate(" + "0" + ",0)"; });
    //.attr("transform", function(d) { return "translate(" + x(d.Date) + ",0)"; })

state.selectAll("rect")
    .data(function(d) {
      return d.people; 
    })
  .enter().append("rect")
    .attr("width", x.rangeBand())
    .attr("y", height)
    .attr("x",function(d) { //add to stock code
        return x(d.myDate)
      })
    .attr("height", 0 )
    .style("fill", function(d) { return color(d.name); })
    .transition()
    .duration(1000)
    .attr("y", function(d) { return y(d.y1); })
    .attr("height", function(d) { return y(d.y0) - y(d.y1); })
    .attr("class", function(d) {
      classLabel = d.name.replace(/,\s/g, ''); //remove spaces
      return "class" + classLabel;
    });


state.selectAll("rect")
     .on("mouseover", function(d){

        var delta = d.y1 - d.y0;
        var xPos = parseFloat(d3.select(this).attr("x"));
        var yPos = parseFloat(d3.select(this).attr("y"));
        var height = parseFloat(d3.select(this).attr("height"))

        d3.select(this).attr("stroke","black").attr("stroke-width",2);

        tooltip.style("visibility", "visible");
        tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");
        tooltip.style('background', 'black')
        tooltip.style('color', 'white')
        tooltip.style('border-radius', '3px')
        tooltip.style('padding', '5px')
        tooltip.style('opacity', '0.8')
        tooltip.style('font-size', '10px;')


        tooltip.text(d.name +": "+ delta)

     })
     .on("mouseout",function(){

        tooltip.style("visibility", "hidden");
        graph.select(".tooltip").remove();
        d3.select(this).attr("stroke","pink").attr("stroke-width",0.2);

      })


var itemsAmount = 0
var rightSVG = barSVG.append('svg').classed('rightSVG', true)
    .attr('height', '390')
    .attr('id', 'rightSVG')



var legendSVG = rightSVG.append('svg').classed('legendSVG', true)
    .attr('id', 'legendSVG')


var legend = legendSVG.selectAll(".legend")
    .data(color.domain().slice().reverse())
    .enter().append("g")
    //.attr("class", "legend")
    .attr("class", function (d) {
        itemsAmount = itemsAmount + 1
        legendClassArray.push(d.replace(/,\s/g, '')); //remove spaces
        return "legend";
    })
    .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

//reverse order to match order in which bars are stacked    
legendClassArray = legendClassArray.reverse();

legend.append("rect")
    .attr("x", width - 0)
    .attr("width", 18)
    .attr("height", 18)
    .style("fill", color)
    .attr("id", function (d, i) {
      return "id#" + d.replace(/,\s/g, '');
    })
    .on("mouseover",function(){      


      if (active_link === "0") d3.select(this).style("cursor", "pointer");
      else {
        if (active_link.split("class").pop() === this.id.split("id#").pop()) {
          d3.select(this).style("cursor", "pointer");
        } else d3.select(this).style("cursor", "auto");
      }
    })
    .on("click",function(d){    
      if (!this.id.includes('active')) { //nothing selected, turn on this selection

        d3.select(this)
            .attr('id', function(){
                return this.id + 'active'
            })
          .style("stroke", "black")
          .style("stroke-width", 2);


          active_link = this.id.split("id#").pop();
          plotSingle(this);

      } else { //deactivate

          d3.select(this)           
            .classed("active", false)
            .attr('id', function() {
                return this.id.replace('active', '')
            })
            .style("stroke", "none")
            .style("stroke-width", 0);

          plotSingle(this);

        }

    });

legend.append("text")
    .attr("x", width - 6)
    .attr("y", 9)
    .attr("dy", ".35em")
    .style("text-anchor", "end")
    .text(function(d) { return d; });

legendSVG.append("text")
    .classed('queryButton', true)
    .attr("x", width - 6)
    .attr("y", height)
    .attr("dy", ".35em")
    .style("text-anchor", "end")
    .text('Run Query')
    .on('click', function(){
        if (newArr.length > 0) {
            d3.select('#barSVG').remove();
            runScript(newArr)
        }
    });

legendSVG.append("text")
.classed('queryButton', true)
.attr("x", width - 6)
.attr("y", height + 18)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text('Reset')

我想要滚动的特定SVG是'rightSVG'。

正如您在图像中看到的那样,名称被截断。应该有一个可滚动的图例,我可以看到29个数据项。Sample Stacked Bar chart

另外,我添加了以下CSS:

#documents_reviewed_bar_chart, #gElement{
max-height: 390;
overflow-y: scroll;
}

1 个答案:

答案 0 :(得分:2)

简短回答:您无法在另一个SVG内部使用可滚动的SVG。 overflow-y: scroll适用于HTML元素,而不适用于SVG元素。

替代(和hacky)回答:从技术上讲,你想要什么是可能的,但是你必须将你的内部SVG包装在一个HTML元素中,该元素必须位于{{ 1}}。

这种替代方案不是最理想的,没什么意义,也不适用于IE。然而,仅仅为了好奇,这就是你如何做到的:



foreignObject

var outerSvg = d3.select("body")
  .append("svg")
  .attr("width", 500)
  .attr("height", 200)
  .style("background-color", "darkkhaki");
  
var foreign = outerSvg.append("foreignObject")
  .attr("x", 300)
  .attr("y", 10)
  .attr("width", 150)
  .attr("height", 180)
  .append("xhtml:div")
  .style("max-height", "180px")
  .style("overflow-y", "scroll");
  
var innerSvg = foreign.append("svg")
  .attr("width", 133)
  .attr("height", 1000)
  .style("background-color", "powderblue");
  
var color = d3.scaleOrdinal(d3.schemeCategory20);
  
var texts = innerSvg.selectAll("foo")
  .data(d3.range(65))
  .enter()
  .append("text")
  .attr("x", 40)
  .attr("y", (d,i)=> 20 + 15*i)
  .text("foo bar baz")
  
var rects = innerSvg.selectAll("foo")
  .data(d3.range(65))
  .enter()
  .append("rect")
  .attr("x", 10)
  .attr("y", (d,i)=> 8 + 15*i)
  .attr("width", 20)
  .attr("height", 13)
  .attr("fill", (d,i)=>color(i));




外部SVG是浅棕色(或卡其色)。内部SVG位于右侧,为蓝色,位于<script src="https://d3js.org/d3.v4.min.js"></script><div>