放大D3.J时,如何删除非整数tick虫?

发布于 2025-01-25 13:14:40 字数 1949 浏览 3 评论 0原文

问题:我正在尝试创建一个可调/可缩放的线性X轴,每个整数从0到预定义的大数字(例如1000,但可能更高)。显然,这并不适合屏幕,并且可以阅读,因此我需要它脱离屏幕并可以放松。从0到10开始是可以接受的。到目前为止,我的解决方案存在许多问题,但是第一个是如果我放大,D3会自动在小数点位置的整数之间添加tick,以在屏幕上继续在屏幕上继续有10个滴答。第二个是我缩小时,范围将显示到1000以上。

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

我尝试过的解决方案:

  • .ticks()。这使我可以指定我想要的壁虱数量。但是,这似乎将所有壁虱都放入指定的初始范围。如果我增加了超过SVG尺寸的范围的宽度,它会散布滴答,但并非以任何可控的方式(据我所知)。此外,我遇到了平移和缩放的性能问题。我发现这种行为很奇怪,因为如果我不指定刻度,它会使我可以在没有任何滞后的情况下泛滥。 i 假设发生滞后是因为它试图立即渲染所有1000个tick,并且默认的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)
}

Problem: I am trying to create a pannable/zoomable Linear X axis with ticks on each integer from 0 to a predefined large number (let's say 1000 but could be much higher). Obviously this will not all fit on the screen and be readable, so I need it to go offscreen and be pannable. Starting with 0 through 10 is acceptable. There are a number of problems with my solution so far, but the first being that if I zoom in, D3 automatically adds ticks in between the integers at decimal places to continue to have 10 ticks on the screen. The second being when I zoom out, the range will display beyond 1000.

A quick note: The data that will be displayed is irrelevant. The axis has to go from 0 to 1000 regardless if there is a datapoint on 1000 or not so I cannot determine the domain based on my dataset.

Solutions I've tried:

  • .ticks(). This lets me specify the number of ticks I want. However this appears to put all the ticks into the initial range specified. If I increase the width of the range past the svg size, it spreads out the ticks, but not in any controllable way (to my knowledge). Additionally I run into performance issues where the panning and zooming. I find this behavior strange since if I don't specify ticks, it makes an infinite number I can pan through without any lag. I assume the lag occurs because it's trying to render all 1000 ticks immediately and the default d3.js functionality renders them dynamically as you scroll. This seems like a potential solution, but I'm not sure how to execute it.
  • .tickValues(). I can provide an array of 0 through 1000 and this works but exhibits the exact same lag behavior as .ticks(). This also doesn't dynamically combine ticks into 10s or 100s as I zoom out.
  • .tickFormat(). I can run a function through so that any non-integer number is converted to an empty string. However, this still leaves the tick line.

Here are the relevant parts of my code using the latest version of 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)
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

屌丝范 2025-02-01 13:14:41

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

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>

Instead of dealing with the axis' methods, you can simply select the ticks in the container group itself, removing the non-integers:

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

Here is your code with that change:

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>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文