Problema: estoy tratando de crear un eje X lineal paneable/ampliable con marcas en cada número entero desde 0 hasta un número grande predefinido (digamos 1000 pero podría ser mucho más alto). Obviamente, esto no cabrá en la pantalla ni será legible, por lo que necesito que salga de la pantalla y sea paneable. Empezar con 0 a 10 es aceptable. Hay una serie de problemas con mi solución hasta ahora, pero el primero es que si hago zoom, D3 automáticamente agrega marcas entre los números enteros en lugares decimales para continuar teniendo 10 marcas en la pantalla. El segundo es cuando alejo el zoom, el rango se mostrará más allá de 1000.
Una nota rápida: los datos que se mostrarán son irrelevantes. El eje tiene que ir de 0 a 1000, independientemente de si hay un punto de datos en 1000 o no, por lo que no puedo determinar el dominio en función de mi conjunto de datos.
Soluciones que he probado:
Aquí están las partes relevantes de mi código usando la última versión de 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) }En lugar de lidiar con los métodos del eje, simplemente puede seleccionar las marcas en el grupo de contenedores, eliminando los números no enteros:
xAxisG.call(zx) .selectAll(".tick") .filter(e => e % 1) .remove();Aquí está su código con ese cambio:
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>