javascript - 如何使用 D3 制作响应式标签
问题描述
我正在使用 D3 charts
,通过反应,我创建了条形图并使其具有响应性。当我调整浏览器的大小时,栏会相应地占用宽度。
问题
我想要实现的一件事是动态标签,比如当我调整页面大小时,标签的长度有点大,所以它比其他的
所以我想克服这个问题,我用谷歌搜索了很多,发现了一个例子,当它全屏呈现时,它看起来像
对于上面,这是我找到的参考
所以我想实现但没有任何想法。
我做了什么
import React, { useRef, useEffect, useState } from 'react';
import { select, axisBottom, axisRight, scaleLinear, scaleBand } from 'd3';
import ResizeObserver from 'resize-observer-polyfill';
const useResizeObserver = (ref) => { // doing this for resizing
const [dimensions, setDimensions] = useState(null);
useEffect(() => {
const observeTarget = ref.current;
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
setDimensions(entry.contentRect);
});
});
resizeObserver.observe(observeTarget);
return () => {
resizeObserver.unobserve(observeTarget);
};
}, [ref]);
return dimensions;
};
function BarChart({ data }) {
const svgRef = useRef();
const wrapperRef = useRef();
const dimensions = useResizeObserver(wrapperRef);
// will be called initially and on every data change
useEffect(() => {
const svg = select(svgRef.current);
if (!dimensions) return;
// scales
const xScale = scaleBand()
.domain(data.map((value, index) => index + '2021+01+06'))
.range([0, dimensions.width]) // change
.padding(0.5);
const yScale = scaleLinear()
.domain([0, 150]) // todo
.range([dimensions.height, 0]); // change
// create x-axis
const xAxis = axisBottom(xScale).ticks(data.length);
svg
.select('.x-axis')
.style('transform', `translateY(${dimensions.height}px)`)
.call(xAxis);
// create y-axis
const yAxis = axisRight(yScale);
svg
.select('.y-axis')
.style('transform', `translateX(${dimensions.width}px)`)
.call(yAxis);
// draw the bars
svg
.selectAll('.bar')
.data(data)
.join('rect')
.attr('class', 'bar')
.style('transform', 'scale(1, -1)')
.attr('x', (value, index) => xScale(index + '2021+01+06'))
.attr('y', -dimensions.height)
.attr('width', xScale.bandwidth())
.transition()
.attr('fill', '#015e89')
.attr('height', (value) => dimensions.height - yScale(value));
}, [data, dimensions]);
return (
<div ref={wrapperRef} style={{ marginBottom: '2rem' }}>
<svg ref={svgRef}>
<g className="x-axis" />
<g className="y-axis" />
</svg>
</div>
);
}
export default BarChart;
这是我的密码箱
解决方案
一个常见的解决方案是旋转标签,以便在没有宽度时它们不会重叠:
svg
.select(".x-axis")
.selectAll("text")
.attr("y", 0)
.attr("x", 0)
.attr("dy", "2em")
.attr("transform", "rotate(330)")
.style("text-anchor", "end");
这种方法的好处是它很容易实现,并且通过一些小的调整(当你知道标签中的数据时)可以覆盖 90% 的情况。
Codesandbox - 旋转标签
编辑
我已尝试执行示例中的操作,它需要进行一些调整才能工作,因为目前您正在根据 index.html 生成标签。
这部分负责跳过 X 刻度上的值,我从您的示例中获取它,它按预期工作。确保正确设置刻度宽度:
const tickWidth = 80;
const width = xScale.range()[1];
const tickN = Math.floor(width / tickWidth);
const keepEveryNth = Math.ceil(xScale.domain().length / tickN);
const xScaleDomain = xScale
.domain()
.filter((_, i) => i % keepEveryNth === 0);
xScale.domain(xScaleDomain);
但它会产生一个视觉错误,因为您根据 scale( .attr("x", (value, index) => xScale(index + "2021+01+06"))
) 定位条形图,但比例尺不再具有该标签,因此它返回未定义。这导致酒吧定位在x = 0
.
我已经尝试过滤掉条形的数据以仅包括在比例尺上找到的条形:
const diplayData = data.filter((_, i) => {
return xScaleDomain.includes(i + "2021+01+06");
});
但这行不通,标签是用索引构建的,而索引是一种不可靠的识别方式。
玩玩这个沙盒,我相信你可以从这里得到你需要的东西。
Codesandbox - 动态刻度
编辑 2
在绘制条形图时,我可以使用以下行来处理任何数据:
.attr("x", (_, i) => xScale(xScale.domain()[i]))
我还冒昧地添加了动态 yScale 大小:
const yScale = scaleLinear()
.domain([0, 1.1 * Math.max(...data.map(({ consumo }) => consumo))])
编辑 3
我想我已经达到了你所描述的。我为条形添加了单独的比例,因此始终显示所有条形。另一方面,标签会随着可用空间的减少而消失。我不确定为同一轴设置 2 个刻度是否是一个好习惯。
确保为两个比例保持相同的填充,否则会有奇怪的偏移。
这是带有示例数据的最终版本,它应该适用于任何数据,只要您正确映射项目:
Codesandbox - 最终版本
推荐阅读
- python - 删除转义字符python
- javascript - 通过Javascript更改用户在画布中上传的图像特定区域的颜色
- jquery - 有没有办法通过点击事件多次运行 Boomerang 带宽(由 AKAMAI 提供)?
- php - 如何在 symfony 5 中验证 POST 请求
- javascript - 比较 RRule 字符串是否重叠
- telegram - 为什么有些电报频道使用名称而其他不使用?
- reactjs - 如何在没有 npm install 的情况下从 github 运行 react 项目
- angular - 如何在 Angular 中保存购物车?
- c# - 如何将具有字段的对象存储为数据库 SQL 的字符串列表
- javascript - 在安装 npm 包时出现 ETIMEDOUT 错误?