【问题标题】:How to fit my data to the container when the container has a transform applied to it当容器应用了转换时如何将我的数据适合容器
【发布时间】:2020-03-02 00:02:43
【问题描述】:

我有一个 SVG 图表,y 轴是用负像素位置创建的……这让我应用了一个变换并将图表平移 35 像素以补偿负位置。这样做,它会切断我数据的最后一部分。我怎样才能重新安排它,以便数据都适合容器?

我尝试将 y 轴标签分离到它们自己的 svg 中,并使用 flexbox 将其放置在主要数据 svg 旁边,但仍然会出现不可见的数据悬垂。也许我还没有解决这个问题?

<div class="total-chart-wrapper" id="total-chart-wrapper">
  <svg id="total-chart" height="285" width="100%">
    <defs>
        <linearGradient id="total-gradient" gradientTransform="rotate(90)">
          <stop offset="0" stop-color="rgba(76, 150, 254, 1.00)"/>
          <stop offset="0.5" stop-color="rgba(76, 150, 254, 0.70)"/>
          <stop offset="1" stop-color="rgba(255, 255, 255, 0.000)"/>
        </linearGradient>
    </defs>
    <g transform="translate(40,40)">
    <g class="y axis" id="total-y-ticks">
      <line class="y-axis-zero-line axis-line" id="y-axis-zero-line-total" x1="0" y1="0" x2="0" y2="240"></line>
    </g>
      <g><polygon id="total-chart-polygon" fill="url('#total-gradient')"></polygon></g>
      <g class="x axis" id="total-x-ticks" transform="translate(0,0)">
        <line class="x-axis-zero-line axis-line" id="x-axis-zero-line-total" x1="0" y1="240" x2="600" y2="240"></line>
      </g>
    </g>
  </svg>
</div>

把所有代码都放在这里太长了,所以这里是一个功能性的codepen:https://codepen.io/Finches/pen/eYYVEgW

如果您检查数据/多边形,您可以看到它在最后切断了一些数据。

预期结果是将所有数据放入 svg。 实际结果是不可见的数据悬垂,可能是由于在包含图表的 g 上应用了变换。到目前为止,由于 y 标签中的负边距使它们位于图表原点的左侧,我一直无法解决此问题。有什么帮助吗?

【问题讨论】:

  • 根据您在 codepen 中的代码,您是否有无法展开父 div 的原因?如果这不能接受,我可以再看看它。否则,似乎有人在下面提出了这个建议。
  • 是的,我需要它来适应所有容器宽度(响应宽度),所以我只选择了 600,因为我知道这比它需要的要小。
  • 那么下面的答案令人满意吗?抱歉,您的评论不清楚。还有你不使用像 D3.js 这样的 javascript 图表库的原因吗?
  • 下面的答案不起作用,不幸的是我不允许使用图书馆。感谢您抽出宝贵时间。
  • 我添加了一个解决方案。看看这是否有利于您的需求。

标签: javascript svg


【解决方案1】:

在您的 Javascript 中更改 const totalChartWidth 如下:

const totalChartWidth = document.getElementById('total-chart-wrapper').offsetWidth - 50;

- 50 将在左侧为您提供 50px 并完全适合您的图表在 600px 父 div 中。

您可以将此- 50 更改为一些代码,该代码会动态计算此间隔器的大小,以使其更具响应性。

请记住,尽管代码的设置方式是,这些值仅在页面加载时获得,因此图表不会对窗口的简单调整大小做出响应。调整大小后需要刷新页面才能看到效果。

// Total bar container width 35 px
// Labels before formatting
const labels = [
  ['Wed', '11 PM'],
  ['Thu', '12 AM'],
  ['Thu', '1 AM'],
  ['Thu', '2 AM'],
  ['Thu', '3 AM'],
  ['Thu', '4 AM'],
  ['Thu', '5 AM'],
  ['Thu', '6 AM'],
  ['Thu', '7 AM'],
  ['Thu', '8 AM'],
  ['Thu', '9 AM'],
  ['Thu', '10 AM'],
  ['Thu', '11 AM'],
  ['Thu', '12 PM'],
  ['Thu', '1 PM'],
  ['Thu', '2 PM'],
  ['Thu', '3 PM'],
  ['Thu', '4 PM'],
  ['Thu', '5 PM'],
  ['Thu', '6 PM'],
  ['Thu', '7 PM'],
  ['Thu', '8 PM'],
  ['Thu', '9 PM'],
  ['Thu', '10 PM'],
  ['Thu', '11 PM'],
  ['Fri', '12 AM'],
  ['Fri', '1 AM'],
  ['Fri', '2 AM'],
  ['Fri', '3 AM'],
  ['Fri', '4 AM'],
  ['Fri', '5 AM'],
];

// Data points
const data = [
  0.25,
  0.56,
  0.5,
  0.5,
  0.86,
  0.45,
  0.3,
  0,
  0.3,
  0.6,
  0.4,
  0,
  0,
  0.8,
  0,
  0,
  0.4,
  0.3,
  0.1,
  0,
  0,
  0.2,
  0,
  0,
  0.4,
  0.7,
  0.1,
  0.6,
  0.4,
  0.6,
  0.4,
];

// Function to figure out max Y Tick
function yAxisRangeImperial(max) {
  if (max < 1) {
    return 1;
  } else if (max < 2) {
    return 2;
  } else if (max < 4) {
    return 4;
  } else if (max < 8) {
    return 8;
  } else if (max < 12) {
    return 12;
  } else if (max < 24) {
    return 24;
  } else if (max < 48) {
    return 48;
  } else if (max < 72) {
    return 72;
  } else if (max < 96) {
    return 96;
  } else {
    return 192;
  }
};

// Area chart

// Sum of all snowfall
const maxTotalValue = data.reduce(function(acc, val) { return acc + val; }, 0);
// Getting chart components by ID
const totalChart = document.getElementById('total-chart');
const totalXAxis = document.getElementById('total-x-ticks');
const totalYAxis = document.getElementById('total-y-ticks');
const totalPolygon = document.getElementById('total-chart-polygon');

const totalChartWidth = document.getElementById('total-chart-wrapper').offsetWidth - 50;

// Function to determine hour display interval
function hoursLabelTotalInterval(hours) {
  if (hours <= 6) {
    return 1;
  } else if (hours <= 12) {
    return 2;
  } else if (hours <= 24) {
    return 4;
  } else if (hours <= 36) {
    return 6;
  } else if (hours <= 48) {
    return 8;
  } else if (hours <= 60) {
    return 10;
  } else if (hours <= 72) {
    return 12;
  } else {
    return 24;
  }
}

// Function to create x axis labels
function createTotalLabels(labels) {
  const hoursInterval = hoursLabelTotalInterval(labels.length);
  let spacing = totalChartWidth/labels.length;
  let currentDay = '';
  const formattedLabels = labels.map((label, i) => {
      if (i % hoursInterval === 0) {
          if (label[0] !== currentDay) {
            currentDay = label[0];
              return [label[0], label[1]]
        }
          return ['', label[1]]
      }
      return ['','']
  });
  for (let i=0; i<formattedLabels.length; i++) {
    let translateDistance;
    if (i === 0) {
      translateDistance = 15;
    } else {
      translateDistance = i*spacing + 15;
    }
    if (formattedLabels[i][1].length && i !== 0) {
      totalXAxis.innerHTML += 
      '<g class="tick" transform="translate(' + translateDistance + ',0)"><line class="y-axis-zero-line axis-line dash gray" stroke-dasharray="4" stroke-width="1" x1="-13" y1="0" x2="-13" y2="240"></line><text class="label-day" dy=".71em" y="-20" x="0">' + formattedLabels[i][0] + '</text><text class="label-time" dy=".71em" y="-10" x="0">' + formattedLabels[i][1] + '</text></g>';
    } else {
      totalXAxis.innerHTML += 
      '<g class="tick" transform="translate(' + translateDistance + ',0)"><text class="label-day" dy=".71em" y="-20" x="0">' + formattedLabels[i][0] + '</text><text class="label-time" dy=".71em" y="-10" x="0">' + formattedLabels[i][1] + '</text></g>';
    }
  }
}

// Function to populate the y axis ticks and depth labels for total accumulation
function populateHourlyYAxisLabelsTotal(max, unit) {
  
  let maxYTick = yAxisRangeImperial(max);
  let tickContainer = document.getElementById('total-y-ticks');
  // Get x axis zero line
  let xAxisZeroLine = document.getElementById('x-axis-zero-line-total');
  
  let interval = maxYTick / 4;
  let ticks = [
    interval + unit,
    interval * 2 + unit,
    interval * 3 + unit,
    interval * 4 + unit,
  ];
  
  let width = totalChartWidth;
  let height = 240;
  
  tickContainer.innerHTML += '<line class="gray" x2="-30" y2="0"></line><line class="gray" x1="0" x2="' + width + '" y2="0"></line>';
  
  xAxisZeroLine.setAttribute("x2", width);
  
  for (let j=ticks.length-1; j>=0; j--) {
    if (j !== 0) {
      tickContainer.innerHTML += '<g class="tick" transform="translate(0,'+ (height - (60*j)) +')"><line class="gray" x2="-30" y2="0"></line><line class="gray" x1="0" x2="' + width + '" y2="0"></line><text class="y-tick" dy=".32em" x="-9" y="-30" style="text-anchor: end;">'+ ticks[j] +'</text></g>'
    } else {
      tickContainer.innerHTML += '<g class="tick" transform="translate(0,'+ height +')"><line x2="-30" y2="0"></line><text dy=".32em" x="-9" y="-30" style="text-anchor: end;">'+ ticks[j] +'</text></g>'
    }
  };
};

// Function to create area of accumulation
function accumulationAreaPoints(data, max) {
  let spacing = totalChartWidth/data.length;
  let maxYTick = yAxisRangeImperial(max);
  let summedDataSet = data.map(Number);
  summedDataSet = summedDataSet.map((elem, index) => summedDataSet.slice(0, index + 1).reduce((a, b) => a + b));
  summedDataSet = summedDataSet.map(function(each_element) {
    return Number(each_element.toFixed(2));
  });
  summedDataSet.unshift(0);
  for (let i=0; i<summedDataSet.length; i++) {
    let point = totalChart.createSVGPoint();
    let yLocation = (1 - summedDataSet[i]/maxYTick) * 240;
    point.x = spacing*i;
    point.y = yLocation;
    totalPolygon.points.appendItem(point);
  }
  let finalPoint = totalChart.createSVGPoint();
  finalPoint.x = summedDataSet.length*spacing - spacing;
  finalPoint.y = 240;
  totalPolygon.points.appendItem(finalPoint);
}

createTotalLabels(labels);
populateHourlyYAxisLabelsTotal(maxTotalValue, '"');
accumulationAreaPoints(data, maxTotalValue);

window.addEventListener('resize', populateHourlyYAxisLabelsTotal(maxTotalValue, '"'));
window.addEventListener('resize', accumulationAreaPoints(data, maxTotalValue));
h1 {
  font: 24px sans-serif;
}
.bar {
  fill: #4a93ff;
}

.axis {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}

body {
  font-family: 'Open Sans', sans-serif !important;
}

.axis-line {
  stroke-width: 1px;
}

.tick {
  opacity: 1;
}

.tick text {
  text-anchor: middle;
  font-family: 'Open Sans', sans-serif;
}

.tick line.gray, line.gray {
  stroke: #d9d9d9;
}
.label-day, .y-tick {
  font-weight: bold;
}
.label-time {
  font-weight: normal;
}

.total-chart-wrapper {
  width: 600px;
}
<link href="https://fonts.googleapis.com/css?family=Open+Sans&display=swap" rel="stylesheet">

<div class="total-chart-wrapper" id="total-chart-wrapper">
  <svg id="total-chart" height="285" width="100%">
    <defs>
        <linearGradient id="total-gradient" gradientTransform="rotate(90)">
          <stop offset="0" stop-color="rgba(76, 150, 254, 1.00)"/>
          <stop offset="0.5" stop-color="rgba(76, 150, 254, 0.70)"/>
          <stop offset="1" stop-color="rgba(255, 255, 255, 0.000)"/>
        </linearGradient>
    </defs>
    <g transform="translate(40,40)">
    <g class="y axis" id="total-y-ticks">
      <line class="y-axis-zero-line axis-line" id="y-axis-zero-line-total" x1="0" y1="0" x2="0" y2="240"></line>
    </g>
      <g><polygon id="total-chart-polygon" fill="url('#total-gradient')"></polygon></g>
      <g class="x axis" id="total-x-ticks" transform="translate(0,0)">
        <line class="x-axis-zero-line axis-line" id="x-axis-zero-line-total" x1="0" y1="240" x2="600" y2="240"></line>
      </g>
    </g>
  </svg>
</div>

【讨论】:

  • 天哪……这似乎是一种魅力。哇。非常感谢您抽出宝贵的时间。
【解决方案2】:

您的父 div 设置为 600 像素。 因此,父级太小而无法容纳 SVG。 通过使用转换,您还可以将 SVG 推离包装器。

.total-chart-wrapper {
    width: 600px;
}

改为:

.total-chart-wrapper {
    width: auto;
}

它会正常工作(它会选择 SVG 宽度)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多