当容器应用了转换时,如何将我的数据适合容器

时间:2019-11-05 15:13:54

标签: javascript svg

我有一个SVG图表,并且y轴是使用负像素位置创建的。这使我应用了变换并将图表平移35px以补偿负位置。这样做会切断我数据的最后一部分。我该如何重新排列以便所有数据都适合容器?

我曾尝试将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>

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

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

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

2 个答案:

答案 0 :(得分:1)

您的父div设置为600像素。 因此,父级太小而无法容纳SVG。 通过使用transform,您还将SVG进一步推向了包装。

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

更改为:

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

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

答案 1 :(得分:1)

在Javascript中,如下更改const totalChartWidth

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

- 50会在左侧给您50px,并将您的图表完全放在600px的父分区中。

您可以将此- 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>