【发布时间】:2021-04-09 16:45:58
【问题描述】:
我有一个项目,我正在使用 D3 js 创建一些图表。我试图让这些图表在窗口大小发生变化时做出响应。为此,我已经使用 viewbox 来定义 svg:
var svg = d3
.select(this.$refs["chart"])
.classed("svg-container", true)
.append("svg")
.attr("class", "chart")
.attr(
"viewBox",
`0 0 ${width + margin.left + margin.right} ${height +
margin.top +
margin.bottom}`
)
.attr("preserveAspectRatio", "xMinYMin meet")
.classed("svg-content-responsive", true)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
我还使用将宽度和高度设置为与 SVG 所在的 div 相同。所以这个图表使用与它里面的 div 相同的大小:
width = this.$refs["chart"].clientWidth - margin.left - margin.right,
height = this.$refs["chart"].clientHeight - margin.top - margin.bottom;
此 div 的宽度和高度设置为其父 div 的 100%。因此,当我调整窗口大小时,svg 所在的 div 可以更改大小和纵横比。 所以这就是页面加载时图表最初的样子。所以它从它所在的 div 中获取它的高度和宽度:
但是当我调整图表大小时,它仍然可以适应父 div 的新宽度。但高度也随之变化。所以我假设纵横比保持不变:
我尝试在窗口调整大小时更新 svg 视口。但是当我在 Chrome 中检查开发人员工具的 DOM 中的 SVG 元素时,viewuwport 没有更新。我添加了控制台日志以检查父级的宽度和高度是否也发生了变化,并且它们似乎发生了变化。但更新后的视口不会应用于 svg:
d3.select(window).on("resize", () => {
svg.attr(
"viewBox",
`0 0 ${this.$refs["chart"].clientWidth} ${this.$refs["chart"].clientHeight}`
);
});
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://unpkg.com/vue"></script>
<script src="https://d3js.org/d3.v6.js"></script>
<style>
.area {
fill: url(#area-gradient);
stroke-width: 0px;
}
body{
width: 100%;
height: 100%;
}
.app{
width: 100%;
height: 100%;
}
#page{
width: 100%;
height: 100%;
}
.my_dataviz{
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="app">
<div class="page">
<div id="my_dataviz" ref="chart"></div>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
type: Array,
required: true,
},
mounted() {
const minScale = 0,
maxScale = 35;
var data = [{
key: 'One',
value: 33,
},
{
key: 'Two',
value: 30,
},
{
key: 'Three',
value: 37,
},
{
key: 'Four',
value: 28,
},
{
key: 'Five',
value: 25,
},
{
key: 'Six',
value: 15,
},
];
console.log(this.$refs["chart"].clientHeight)
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 0,
bottom: 30,
left: 40
},
width =
this.$refs["chart"].clientWidth - margin.left - margin.right,
height =
this.$refs["chart"].clientHeight - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleBand().range([0, width]).padding(0.3);
var y = d3.scaleLinear().range([height, 0]);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3
.select(this.$refs['chart'])
.classed('svg-container', true)
.append('svg')
.attr('class', 'chart')
.attr(
'viewBox',
`0 0 ${width + margin.left + margin.right} ${
height + margin.top + margin.bottom
}`
)
.attr('preserveAspectRatio', 'xMinYMin meet')
.classed('svg-content-responsive', true)
.append('g')
.attr(
'transform',
'translate(' + margin.left + ',' + margin.top + ')'
);
// format the data
data.forEach(function(d) {
d.value = +d.value;
});
// Scale the range of the data in the domains
x.domain(
data.map(function(d) {
return d.key;
})
);
y.domain([minScale, maxScale]);
//Add horizontal lines
let oneFourth = (maxScale - minScale) / 4;
svg
.append('svg:line')
.attr('x1', 0)
.attr('x2', width)
.attr('y1', y(oneFourth))
.attr('y2', y(oneFourth))
.style('stroke', 'gray');
svg
.append('svg:line')
.attr('x1', 0)
.attr('x2', width)
.attr('y1', y(oneFourth * 2))
.attr('y2', y(oneFourth * 2))
.style('stroke', 'gray');
svg
.append('svg:line')
.attr('x1', 0)
.attr('x2', width)
.attr('y1', y(oneFourth * 3))
.attr('y2', y(oneFourth * 3))
.style('stroke', 'gray');
//Defenining the tooltip div
let tooltip = d3
.select('body')
.append('div')
.attr('class', 'tooltip')
.style('position', 'absolute')
.style('top', 0)
.style('left', 0)
.style('opacity', 0);
// append the rectangles for the bar chart
svg
.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', function(d) {
return x(d.key);
})
.attr('width', x.bandwidth())
.attr('y', function(d) {
return y(d.value);
})
.attr('height', function(d) {
console.log(height, y(d.value))
return height - y(d.value);
})
.attr('fill', '#206BF3')
.attr('rx', 5)
.attr('ry', 5)
.on('mouseover', (e, i) => {
d3.select(e.currentTarget).style('fill', 'white');
tooltip.transition().duration(500).style('opacity', 0.9);
tooltip
.html(
`<div><h1>${i.key} ${
this.year
}</h1><p>${converter.addPointsToEveryThousand(
i.value
)} kWh</p></div>`
)
.style('left', e.pageX + 'px')
.style('top', e.pageY - 28 + 'px');
})
.on('mouseout', (e) => {
d3.select(e.currentTarget).style('fill', '#206BF3');
tooltip.transition().duration(500).style('opacity', 0);
});
// Add the X Axis and styling it
let xAxis = svg
.append('g')
.attr('transform', 'translate(0,' + height + ')')
.call(d3.axisBottom(x));
xAxis
.select('.domain')
.attr('stroke', 'gray')
.attr('stroke-width', '3px');
xAxis.selectAll('.tick text').attr('color', 'gray');
xAxis.selectAll('.tick line').attr('stroke', 'gray');
// add the y Axis and styling it also only show 0 and max tick
let yAxis = svg.append('g').call(
d3
.axisLeft(y)
.tickValues([this.minScale, this.maxScale])
.tickFormat((d) => {
if (d > 1000) {
d = Math.round(d / 1000);
d = d + 'K';
}
return d;
})
);
yAxis
.select('.domain')
.attr('stroke', 'gray')
.attr('stroke-width', '3px');
yAxis.selectAll('.tick text').attr('color', 'gray');
yAxis.selectAll('.tick line').attr('stroke', 'gray');
d3.select(window).on('resize', () => {
svg.attr(
'viewBox',
`0 0 ${this.$refs['chart'].clientWidth} ${this.$refs['chart'].clientHeight}`
);
});
},
});
</script>
</body>
</html>
【问题讨论】:
-
我试图重新创建您的问题here。从上面的代码 sn-p 中添加一些缺少的变量,您的代码似乎工作得很好。
-
@Mark 感谢您让我的代码在浏览器编辑器中运行。我遇到的问题是,当父 div 由于调整窗口大小而改变大小时。然后图形会缩小,但纵横比不会改变。然后它不再占据父 div 的全部高度。它仍然适合父 div。但我想让图表动态更新高度和宽度以始终完全显示在父 div 中。
-
@Mark 我也认为问题可能是因为我使用了
.attr("preserveAspectRatio", "xMinYMin meet")。
标签: javascript d3.js responsive