javascript - 如何在使用 D3.js 放大时删除非整数刻度?

标签 javascript d3.js

问题:我正在尝试创建一个可平移/可缩放的线性 X 轴,每个整数上的刻度从 0 到预定义的大数字(假设为 1000,但可能更高)。显然,这不会全部适合屏幕并且可读,所以我需要它离开屏幕并可平移。从 0 到 10 开始是可以接受的。到目前为止,我的解决方案存在很多问题,但第一个问题是,如果我放大,D3 会自动在小数位整数之间添加刻度,以继续在屏幕上显示 10 个刻度。第二个是当我缩小时,范围将显示超过 1000。

快速说明:将显示的数据无关紧要。无论是否有 1000 上的数据点,轴都必须从 0 到 1000,因此我无法根据我的数据集确定域。

我尝试过的解决方案:

  • .ticks()。这让我可以指定我想要的刻度数。然而,这似乎将所有刻度放入指定的初始范围内。如果我将范围的宽度增加到超过 svg 大小,它会分散刻度,但不会以任何可控的方式(据我所知)。此外,我还遇到了平移和缩放的性能问题。我发现这种行为很奇怪,因为如果我不指定刻度,它会产生无限的数量,我可以平移而不会出现任何延迟。我假设出现延迟是因为它试图立即渲染所有 1000 个刻度,并且默认的 d3.js 功能会在您滚动时动态渲染它们。这似乎是一个潜在的解决方案,但我不确定如何执行它。
  • .tickValues()。我可以提供 0 到 1000 的数组,这可以工作,但表现出与 .ticks() 完全相同的滞后行为。当我缩小时,这也不会动态地将刻度组合成 10 秒或 100 秒。
  • .tickFormat()。我可以运行一个函数,以便将任何非整数转换为空字符串。然而,这仍然留下了刻度线。

以下是我使用最新版本的 D3.js(7.3) 的代码的相关部分:

const height = 500
const width = 1200

const margin = {
    top: 20,
    right: 20,
    bottom: 20,
    left: 35
}

const innerWidth = width - margin.left - margin.right
const innerHeight = height - margin.top - margin.bottom


const xScale = d3.scaleLinear() 
    .domain([0, 10]) 
    .range([0, innerWidth]) 

const svg = d3.select('svg')  
    .attr("width", width) 
    .attr("height", height) 

svg.append('defs').append('clipPath')
    .attr('id', 'clip')
    .append('rect')
    .attr('width', innerWidth)
    .attr('height', innerHeight)

const zoom = d3.zoom()
    .scaleExtent([1, 8])
    .translateExtent([
        [0, 0],
        [xScale(upperBound), 0]
        ])
    .on('zoom', zoomed)

const g = svg.append('g')
    .attr('transform', `translate(${margin.left}, ${margin.top})`)

const xAxis = d3.axisBottom() 
    .scale(xScale) 

const xAxisG = g.append('g') 
    .style('clip-path', 'url(#clip)')
    .attr("class", "x-axis")
    .call(xAxis) 
    .attr('transform', `translate(0, ${innerHeight})`)

svg.call(zoom)

function zoomed(event) {
    const updateX = event.transform.rescaleX(xScale)
    const zx = xAxis.scale(updateX)
    xAxisG.call(zx)
}

最佳答案

您可以简单地选择容器组本身中的刻度,删除非整数,而不是处理轴的方法:

xAxisG.call(zx)
    .selectAll(".tick")
    .filter(e => e % 1)
    .remove();

这是经过更改的代码:

const upperBound = 1000
const height = 100
const width = 600

const margin = {
  top: 20,
  right: 20,
  bottom: 20,
  left: 35
}

const innerWidth = width - margin.left - margin.right
const innerHeight = height - margin.top - margin.bottom


const xScale = d3.scaleLinear()
  .domain([0, 10])
  .range([0, innerWidth])

const svg = d3.select('svg')
  .attr("width", width)
  .attr("height", height)

svg.append('defs').append('clipPath')
  .attr('id', 'clip')
  .append('rect')
  .attr('width', innerWidth)
  .attr('height', innerHeight)

const zoom = d3.zoom()
  .scaleExtent([1, 8])
  .translateExtent([
    [0, 0],
    [xScale(upperBound), 0]
  ])
  .on('zoom', zoomed)

const g = svg.append('g')
  .attr('transform', `translate(${margin.left}, ${margin.top})`)

const xAxis = d3.axisBottom()
  .scale(xScale)
  .tickFormat(d => ~~d)

const xAxisG = g.append('g')
  .style('clip-path', 'url(#clip)')
  .attr("class", "x-axis")
  .call(xAxis)
  .attr('transform', `translate(0, ${innerHeight})`)

svg.call(zoom)

function zoomed(event) {
  const updateX = event.transform.rescaleX(xScale)
  const zx = xAxis.scale(updateX)
  xAxisG.call(zx)
    .selectAll(".tick")
    .filter(e => e % 1)
    .remove();
}
<script src="https://d3js.org/d3.v7.min.js"></script>
<svg></svg>

关于javascript - 如何在使用 D3.js 放大时删除非整数刻度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72078915/

相关文章:

javascript - 检查背景中的特定图像

javascript - stats.nba.com 的 CORS/CORB

javascript - 使用 D3 将 SVG Canvas 附加到主体以外的元素

javascript - 创建 svg 媒体控件按钮

javascript - 为什么我的 X(时间)轴上没有刻度?

javascript - 通过 javascript 操作地理编码 web 服务结果?

javascript - 10 秒后自动停止加载页面

javascript - 在 Highstock 中重置/设置 ZoomType 控件

javascript - D3 强制布局 : Collapse Subset of Child Nodes (Opacity Change)

javascript - d3 onerror 默认图像,带有 html 工具提示标签