【问题标题】:Dynamically create scrollable SVG动态创建可滚动的 SVG
【发布时间】:2017-04-04 14:30:13
【问题描述】:

我正在尝试创建一个可滚动列表。我在这里查看了其他教程,但似乎没有用。本质上,我将 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 个数据项。

另外,我添加了以下 CSS:

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

【问题讨论】:

  • 你考虑过使用zoom behavior吗?我认为如果您将最小/最大比例设置为 1,它会变得非常接近滚动。

标签: javascript css d3.js svg


【解决方案1】:

简短回答:您不能在另一个 SVG 中拥有可滚动的 SVG。 overflow-y: scroll 适用于 HTML 元素,不适用于 SVG 元素。

另类(和 hacky)答案:从技术上讲,您想要的都是可能的,但是您必须将您的内部 SVG 包装在一个 HTML 元素中,该元素必须位于 foreignObject 中。

这种替代方法不是最理想的,没有什么意义,并且不适用于 IE。但是,出于好奇,您可以这样做:

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));
<script src="https://d3js.org/d3.v4.min.js"></script>

外部 SVG 为浅棕色(或卡其色)。右侧的内部 SVG 是蓝色的,它位于 <div>overflow-y: scroll; 内。

【讨论】:

  • 谢谢!我采用了基本概念,并使其以一种不那么老套的方式工作。 +1
  • @Kerrin631 我想看看你想出的不那么老套的方法。
  • 这非常有帮助!我花了几个小时试图用 SVG 来做,直到我找到了这个。
猜你喜欢
  • 1970-01-01
  • 2014-06-25
  • 1970-01-01
  • 2021-04-13
  • 2015-07-09
  • 1970-01-01
  • 1970-01-01
  • 2021-01-24
  • 2016-04-02
相关资源
最近更新 更多