代码之家  ›  专栏  ›  技术社区  ›  Aenaon

D3画笔和剪贴画:主图表超出界限

  •  1
  • Aenaon  · 技术社区  · 7 年前

    我正在尝试使用画笔组件制作条形图(基于 https://bl.ocks.org/SevenChan07/495cd567e0ede0deeb14bb3599dce685 )并使用Enter合并退出模式,但我不能使刷子正常工作。当我移动画笔时,图表超出了它的界限。我可能和剪报有关,但我不知道怎么修。

    var defs = focus.append('defs');
    
    // use clipPath
    defs.append('clipPath')
        .attr('id', 'my-clip-path')
        .append('rect')
        .attr('width', width)
        .attr('height', height);
    

    任何帮助都将不胜感激。

    这是 fiddle 下面是一个片段:

    let barData = []
    for(let i = 0;i < 100; i++){
      barData.push({
        Prob: Math.random()*10,
        labels: 'test' + i
      })
    }
    
    barchart(barData)
        
        
    function barchart(data) {
    
        var ordinals = data.map(function (d) {
            return d.labels;
        });
    
    
        var svg = d3.select("#myPlot").select("svg");
        var margin = {
                top: 20,
                right: 20,
                bottom: 0.3 * svg.attr("height"),
                left: 40
            },
            width = +svg.attr("width") - margin.left - margin.right,
            height = +svg.attr("height") - margin.top - margin.bottom,
            margin2 = {
                top: 20 + margin.top + height,
                right: 20,
                bottom: 30,
                left: 40
            },
            height2 = height / 5;
    
        // the scale
        var scale = {
            x: d3.scaleLinear().range([0, width]).nice(),
            x2: d3.scaleLinear().range([0, width]).nice(),
            y: d3.scaleLinear().range([height, 0]).nice(),
            y2: d3.scaleLinear().range([height2, 0]).nice()
        };
    
        let xBand = d3.scaleBand().domain(d3.range(-1, ordinals.length)).range([0, width])
    
        var axis = {
            x: d3.axisBottom(scale.x).tickFormat((d, e) => ordinals[d]),
            y: d3.axisLeft(scale.y)
        };
    
        var brush = d3.brushX()
            .extent([[0, 0], [width, height2]])
            .on("brush", brushed)
    
    
        var focus = svg.select('.focus')
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
        focus.select(".axis").attr("transform", "translate(0," + height +")");
    
        var context = svg.select('.context')
            .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
    
    
        var defs = focus.append('defs');
    
        // use clipPath
        defs.append('clipPath')
            .attr('id', 'my-clip-path')
            .append('rect')
            .attr('width', width)
            .attr('height', height);
    
        function updateScales(data) {
            scale.x.domain([-1, ordinals.length])
            scale.y.domain([0, d3.max(data, d => d.Prob)])
            scale.x2.domain(scale.x.domain())
            scale.y2.domain([0, d3.max(data, d => d.Prob)])
        }
    
    
        svg.call(renderPlot, data)
    
        function renderPlot(selection, data) {
            updateScales(data);
    
            selection.select(".context")
                .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")")
                .select('.brush')
                .call(brush)
                .call(brush.move, scale.x.range())
    
            selection.select(".axis2")
                .attr("transform", "translate(0," + height2 +")");
    
            selection.select(".focus").select(".axis").call(axis.x);
            selection.select(".focus").select(".axis.axis--y").call(axis.y);
    
            selection
                .call(renderPoints, data);
        }
    
    
        function renderPoints(selection, data) {
            
            var points = selection.select('.focus')
              	.selectAll('.bar').data(data);
    
            var newPoints = points.enter().append('rect')
                .attr('class', 'bar')
                .attr('x', (d, i) => {
                    return scale.x(i) - xBand.bandwidth() * 0.9 / 2
                })
                .attr('y', (d, i) => {
                    return scale.y(d.Prob)
                })
                .attr('width', xBand.bandwidth() * 0.9)
                .attr('height', d => {
                    return height - scale.y(d.Prob)
                });
    
            points.merge(newPoints)
                .transition().duration(1000)
                .attr('x', (d, i) => {
                    return scale.x(i) - xBand.bandwidth() * 0.9 / 2
                })
                .attr('y', (d, i) => {
                    return scale.y(d.Prob)
                })
                .attr('width', xBand.bandwidth() * 0.9)
                .attr('height', d => {
                    return height - scale.y(d.Prob)
                });
    
            points.exit()
                .transition().duration(1000)
                .remove();
    
    
            var sPoints = selection.select('.context').selectAll('.bar').data(data);
    
            var newsPoints = sPoints.enter().append('rect')
                .attr('class', 'bar')
                .attr('x', (d, i) => {
                    return scale.x2(i) - xBand.bandwidth() * 0.9 / 2
                })
                .attr('y', (d, i) => scale.y2(d.Prob))
                .attr('width', xBand.bandwidth() * 0.9)
                .attr('height', d => {
                    return height2 - scale.y2(d.Prob)
                });
    
            sPoints.merge(newsPoints)
                .transition().duration(1000)
                .attr('x', (d, i) => {
                    return scale.x2(i) - xBand.bandwidth() * 0.9 / 2
                })
                .attr('y', (d, i) => scale.y2(d.Prob))
                .attr('width', xBand.bandwidth() * 0.9)
                .attr('height', d => {
                    return height2 - scale.y2(d.Prob)
                });
    
            sPoints.exit()
                .transition().duration(1000)
                .remove();
    
    
    
        }
    
    
        function brushed() {
            var s = d3.event.selection || scale.x2.range()
            scale.x.domain(s.map(scale.x2.invert, scale.x2))
            focus.select('.axis').call(axis.x)
            focus.selectAll('.bar')
                .attr('x', (d, i) => {
                    return scale.x(i) - xBand.bandwidth() * 0.9 / 2
                })
        }
    
    
    }
    <!DOCTYPE html>
    <html lang='en'>
    <head>
    <meta charset='utf-8'>
        <style type="text/css">
            .bar { fill: steelblue; }
        </style>
    
    <script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
    
    
    </head>
    <body>
      <div class='chart span4' id='myPlot'>
        <svg width="700" height="500">
          <g class="focus">
            <g class="axis"></g>
            <g class="axis axis--y"></g>
          </g>
          <g class="context">
            <g class="axis2"></g>
            <g class="brush"></g>
          </g>
        </svg>
       </div>
    </body>
    </html>
    1 回复  |  直到 7 年前
        1
  •  2
  •   Andrew Reid    7 年前

    你已经差不多了,你只需要把剪辑路径应用到某个东西上。我们可以很容易地用你的酒吧(你可以用 g 包含 只有 酒吧也一样):

    var newPoints = points.enter().append('rect')
       .attr('class', 'bar')
       .....  // other attributes
       .attr("clip-path","url(#my-clip-path)");
    

    我们只需要在输入时执行,因为剪辑路径不需要更新(我们不会更改它)。下面是一个片段:

    let barData = []
    for(let i = 0;i < 100; i++){
      barData.push({
        Prob: Math.random()*10,
        labels: 'test' + i
      })
    }
    
    barchart(barData)
        
        
    function barchart(data) {
    
        var ordinals = data.map(function (d) {
            return d.labels;
        });
    
    
        var svg = d3.select("#myPlot").select("svg");
        var margin = {
                top: 20,
                right: 20,
                bottom: 0.3 * svg.attr("height"),
                left: 40
            },
            width = +svg.attr("width") - margin.left - margin.right,
            height = +svg.attr("height") - margin.top - margin.bottom,
            margin2 = {
                top: 20 + margin.top + height,
                right: 20,
                bottom: 30,
                left: 40
            },
            height2 = height / 5;
    
        // the scale
        var scale = {
            x: d3.scaleLinear().range([0, width]).nice(),
            x2: d3.scaleLinear().range([0, width]).nice(),
            y: d3.scaleLinear().range([height, 0]).nice(),
            y2: d3.scaleLinear().range([height2, 0]).nice()
        };
    
        let xBand = d3.scaleBand().domain(d3.range(-1, ordinals.length)).range([0, width])
    
        var axis = {
            x: d3.axisBottom(scale.x).tickFormat((d, e) => ordinals[d]),
            y: d3.axisLeft(scale.y)
        };
    
        var brush = d3.brushX()
            .extent([[0, 0], [width, height2]])
            .on("brush", brushed)
    
    
        var focus = svg.select('.focus')
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
        focus.select(".axis").attr("transform", "translate(0," + height +")");
    
        var context = svg.select('.context')
            .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
    
    
        var defs = focus.append('defs');
    
        // use clipPath
        defs.append('clipPath')
            .attr('id', 'my-clip-path')
            .append('rect')
            .attr('width', width)
            .attr('height', height);
    
        function updateScales(data) {
            scale.x.domain([-1, ordinals.length])
            scale.y.domain([0, d3.max(data, d => d.Prob)])
            scale.x2.domain(scale.x.domain())
            scale.y2.domain([0, d3.max(data, d => d.Prob)])
        }
    
    
        svg.call(renderPlot, data)
    
        function renderPlot(selection, data) {
            updateScales(data);
    
            selection.select(".context")
                .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")")
                .select('.brush')
                .call(brush)
                .call(brush.move, scale.x.range())
    
            selection.select(".axis2")
                .attr("transform", "translate(0," + height2 +")");
    
            selection.select(".focus").select(".axis").call(axis.x);
            selection.select(".focus").select(".axis.axis--y").call(axis.y);
    
            selection
                .call(renderPoints, data);
        }
    
    
        function renderPoints(selection, data) {
            
            var points = selection.select('.focus')
              	.selectAll('.bar').data(data);
    
            var newPoints = points.enter().append('rect')
                .attr('class', 'bar')
                .attr('x', (d, i) => {
                    return scale.x(i) - xBand.bandwidth() * 0.9 / 2
                })
                .attr('y', (d, i) => {
                    return scale.y(d.Prob)
                })
                .attr('width', xBand.bandwidth() * 0.9)
                .attr('height', d => {
                    return height - scale.y(d.Prob)
                })
                .attr("clip-path","url(#my-clip-path)");
    
            points.merge(newPoints)
                .transition().duration(1000)
                .attr('x', (d, i) => {
                    return scale.x(i) - xBand.bandwidth() * 0.9 / 2
                })
                .attr('y', (d, i) => {
                    return scale.y(d.Prob)
                })
                .attr('width', xBand.bandwidth() * 0.9)
                .attr('height', d => {
                    return height - scale.y(d.Prob)
                })
                
    
            points.exit()
                .transition().duration(1000)
                .remove();
    
    
            var sPoints = selection.select('.context').selectAll('.bar').data(data);
    
            var newsPoints = sPoints.enter().append('rect')
                .attr('class', 'bar')
                .attr('x', (d, i) => {
                    return scale.x2(i) - xBand.bandwidth() * 0.9 / 2
                })
                .attr('y', (d, i) => scale.y2(d.Prob))
                .attr('width', xBand.bandwidth() * 0.9)
                .attr('height', d => {
                    return height2 - scale.y2(d.Prob)
                });
    
            sPoints.merge(newsPoints)
                .transition().duration(1000)
                .attr('x', (d, i) => {
                    return scale.x2(i) - xBand.bandwidth() * 0.9 / 2
                })
                .attr('y', (d, i) => scale.y2(d.Prob))
                .attr('width', xBand.bandwidth() * 0.9)
                .attr('height', d => {
                    return height2 - scale.y2(d.Prob)
                });
    
            sPoints.exit()
                .transition().duration(1000)
                .remove();
    
    
    
        }
    
    
        function brushed() {
            var s = d3.event.selection || scale.x2.range()
            scale.x.domain(s.map(scale.x2.invert, scale.x2))
            focus.select('.axis').call(axis.x)
            focus.selectAll('.bar')
                .attr('x', (d, i) => {
                    return scale.x(i) - xBand.bandwidth() * 0.9 / 2
                })
        }
    
    
    }
    <!DOCTYPE html>
    <html lang='en'>
    <head>
    <meta charset='utf-8'>
        <style type="text/css">
            .bar { fill: steelblue; }
        </style>
    
    <script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
    
    
    </head>
    <body>
      <div class='chart span4' id='myPlot'>
        <svg width="700" height="500">
          <g class="focus">
            <g class="axis"></g>
            <g class="axis axis--y"></g>
          </g>
          <g class="context">
            <g class="axis2"></g>
            <g class="brush"></g>
          </g>
        </svg>
       </div>
    </body>
    </html>