我正在尝试使用画笔组件(基于https://bl.ocks.org/SevenChan07/495cd567e0ede0deeb14bb3599dce685)并使用enter-merge-exit模式制作条形图,但是我无法使画笔正常工作。当我移动画笔时,图表超出了范围。我可能与剪辑有关,但我不知道如何解决它。
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>
答案 0 :(得分:2)
您几乎已经明白了,您只需要将剪切路径应用于某些对象即可。我们可以轻松地使用您的栏(您也可以使用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>