javascript - d3 画笔和 clipPath : Main chart outside bounds

标签 javascript d3.js

我正在尝试使用画笔组件(基于 https://bl.ocks.org/SevenChan07/495cd567e0ede0deeb14bb3599dce685 )并使用进入-合并-退出模式来制作条形图,但我无法使画笔正常工作。当我移动画笔时,图表超出了它的范围。我可能与剪辑有关,但我不知道如何修复它。

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>

最佳答案

你几乎已经搞定了,你只需要将剪辑路径应用于某些东西。我们可以很容易地对您的条形图执行此操作(您也可以使用包含条形图的 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>

关于javascript - d3 画笔和 clipPath : Main chart outside bounds,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51234429/

相关文章:

javascript - 如何让按钮在特定条件下工作?

javascript - 纯 CSS 方法在显示 :table element? 内保持图像比例

javascript - 克隆、播放和替换克隆的 Dom - Javascript/Jquery

javascript - D3JS 的小 map

grails - 从grails Controller 将json对象传递给渲染d3图表的gsp?

d3.js - d3.js transform 和 translate 函数的解释

javascript - 使用 d3.js 上的下拉菜单将圆圈添加到多线图

JavaScript 的 setInterval 和函数何时完成

javascript - 加载外部 XML 文件后对其进行操作

javascript - 如何在修改我的 View 后再次初始化 dragula