D3.js 甘特图垂直滚动并精确缩放

问题描述 投票:0回答:1

我正在尝试使用 d3.js V4 创建甘特图组件。我使用的是旧版本,因此我也可以使用 d3-xyzoom,这有助于解决其他滚动问题。经过大量的试验和错误以及复制和粘贴之后,我想要的行为几乎已经实现。我不是一个 JS 编码员,而且对 d3.js 很陌生。

当我在计算机浏览器上运行下面的代码片段时,其行为正是我想要的:我可以平移块并仅在 X 轴上放大它们。当我在 iPad 上尝试相同的代码时,滚动行为发生了变化,我发现如果所有内容都向上平移,我就无法再放大特定块。当我再次缩小时,一切都会平移回来。

这是我到目前为止所得到的:

        const WIDTH = 1180;
        const HEIGHT = 820;

        const START_DATE = new Date(2023, 0, 1);
        const END_DATE = new Date(2023, 11, 31);

        const BLOCK_HEIGHT = 20;
        const BLOCKS = makeBlocks();

        var xyzoom = d3.xyzoom();
        xyzoom.scaleRatio([1, 0]);

        const svg = d3.select("#chart").append("svg")
            .attr("width", WIDTH)
            .attr("height", HEIGHT)
            .on("touchstart", function () {
                d3.event.preventDefault();
            });

        const g = svg.append("g")
            .attr("cursor", "grab");

        var xScale = d3.scaleTime()
            .domain([START_DATE, END_DATE])
            .range([20, WIDTH - 20]);

        var xAxis = d3.axisTop(xScale)
            .tickFormat(d3.timeFormat('%b %Y'));

        var axisG = svg.append("g")
            .attr("transform", "translate(0,50)")
            .call(xAxis);

        g.selectAll("rect.block")
            .data(BLOCKS)
            .enter()
            .append("rect")
            .attr("class", "block")
            .attr("x", d => xScale(d.start))
            .attr("width", d => xScale(d.end) - xScale(d.start))
            .attr("y", d => d.y)
            .attr("height", BLOCK_HEIGHT)
            .attr("fill", d => d.colour);

        svg.call(xyzoom
            .extent([[0, 0], [WIDTH, HEIGHT]])
            .scaleExtent([.5, 8])
            .on("zoom", zoomer));

        function zoomer() {
            const FULL_DATE_RANGE_MILLISECS = 604800000;
            const WEEK_NUMBER_RANGE_MILLISECS = 21427200000;

            let transform = d3.event.transform;

            transform.ky = 1;
            g.attr("transform", `translate(${transform.x},${transform.y}) scale(${transform.kx},${transform.ky})`);

            var new_xScale = transform.rescaleX(xScale);
            var visibleTimeSpan = new_xScale.domain()[1] - new_xScale.domain()[0];
            if (visibleTimeSpan < FULL_DATE_RANGE_MILLISECS) {
                xAxis.tickFormat(d3.timeFormat('%b %d, %y'));
            } else if (visibleTimeSpan < WEEK_NUMBER_RANGE_MILLISECS) {
                xAxis.tickFormat(function (date) {
                    return "#" + getWeekNumber(date) + ', \'' + d3.timeFormat('%y')(date);
                });
            } else {
                xAxis.tickFormat(d3.timeFormat('%b %Y'));
            }

            axisG.call(xAxis.scale(new_xScale));
        }

        function makeBlocks() {
            const NUMBER_OF_BLOCKS = 50;

            let yPos = 40;
            let blocks = Array.from({ length: NUMBER_OF_BLOCKS }).map((_, index) => {
                let randomStartDate = new Date(START_DATE.getTime() + Math.random() * (END_DATE.getTime() - START_DATE.getTime()));
                let randomEndDate = new Date(randomStartDate.getTime() + Math.random() * (END_DATE.getTime() - randomStartDate.getTime()));
                yPos += 25;

                let colour = index === 5 ? 'darkred' : 'steelblue';

                return {
                    start: randomStartDate,
                    end: randomEndDate,
                    y: yPos,
                    colour
                };
            });

            return blocks;
        }

        function getWeekNumber(d) {
            const DAY_LENGTH_MILLISECS = 86400000;

            var date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
            var dayNum = date.getUTCDay() || 7;
            date.setUTCDate(date.getUTCDate() + 4 - dayNum);
            var yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));

            return Math.ceil((((date - yearStart) / DAY_LENGTH_MILLISECS) + 1) / 7);
        }

欢迎提供有关如何在 iPad 上解决此问题的任何建议。

javascript d3.js ipad gantt-chart
1个回答
0
投票

我将您的代码移至片段并使用 d3v7。变焦在 ipad 上效果更好一点

const WIDTH = 1180;
const HEIGHT = 820;

const START_DATE = new Date(2023, 0, 1);
const END_DATE = new Date(2023, 11, 31);

const BLOCK_HEIGHT = 20;
const BLOCKS = makeBlocks();


const svg = d3.select("#chart").append("svg")
  .attr("width", WIDTH)
  .attr("height", HEIGHT)
  .on("touchstart", function() {
    d3.event.preventDefault();
  });

const g = svg.append("g")
  .attr("cursor", "grab");

var xScale = d3.scaleTime()
  .domain([START_DATE, END_DATE])
  .range([20, WIDTH - 20]);

var xAxis = d3.axisTop(xScale)
  .tickFormat(d3.timeFormat('%b %Y'));

var axisG = svg.append("g")
  .attr("transform", "translate(0,50)")
  .call(xAxis);

g.selectAll("rect.block")
  .data(BLOCKS)
  .enter()
  .append("rect")
  .attr("class", "block")
  .attr("x", d => xScale(d.start))
  .attr("width", d => xScale(d.end) - xScale(d.start))
  .attr("y", d => d.y)
  .attr("height", BLOCK_HEIGHT)
  .attr("fill", d => d.colour);

svg.call(d3.zoom()
  .extent([
    [0, 0],
    [WIDTH, HEIGHT]
  ])
  .scaleExtent([.5, 8])
  .on("zoom", zoomer));

function zoomer({
  transform
}) {

  let rescaleX = transform.rescaleX(xScale);
  g.selectAll("rect.block")
    .data(BLOCKS)
    .attr("x", d => rescaleX(d.start))
    .attr("width", d => rescaleX(d.end) - xScale(d.start))

  axisG.call(d3.axisTop(rescaleX)
    .tickFormat(d3.timeFormat('%b %Y')));
}

function makeBlocks() {
  const NUMBER_OF_BLOCKS = 50;

  let yPos = 40;
  let blocks = Array.from({
    length: NUMBER_OF_BLOCKS
  }).map((_, index) => {
    let randomStartDate = new Date(START_DATE.getTime() + Math.random() * (END_DATE.getTime() - START_DATE.getTime()));
    let randomEndDate = new Date(randomStartDate.getTime() + Math.random() * (END_DATE.getTime() - randomStartDate.getTime()));
    yPos += 25;

    let colour = index === 5 ? 'darkred' : 'steelblue';

    return {
      start: randomStartDate,
      end: randomEndDate,
      y: yPos,
      colour
    };
  });

  return blocks;
}

function getWeekNumber(d) {
  const DAY_LENGTH_MILLISECS = 86400000;

  var date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
  var dayNum = date.getUTCDay() || 7;
  date.setUTCDate(date.getUTCDate() + 4 - dayNum);
  var yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));

  return Math.ceil((((date - yearStart) / DAY_LENGTH_MILLISECS) + 1) / 7);
}
<!DOCTYPE html>
<html>

<head>

  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <style>

  </style>
</head>

<body>
  <div id="chart"></div>


  <script src="https://d3js.org/d3.v7.js" charset="utf-8"></script>

  </script>

© www.soinside.com 2019 - 2024. All rights reserved.