I have 5 horizontal lines and each has a distance of 30 px. The first horizontal line display the max value, the last the min value. Now I have an array with 13 items with different values. Basically I want show LINES (Chart) on the horizontal lines due the value of the array, for every item from the array.
I try to use what @Mina recommended me but it don't works correct.
var prices = [];
prices.push(28990, 30240, 30890, 32162, 31000, 31820, 31770, 30080, 28340, 28620, 28640, 27930, 28850);
var minPrice = Math.floor(Math.min(...prices)/1000)*1000;
var maxPrice = Math.ceil(Math.max(...prices)/1000)*1000;
function addText(x, y, t) {
var text = document.createElementNS('http://www.w3.org/2000/svg','text');
text.setAttribute('x', x);
text.setAttribute('y', y);
text.setAttribute('fill', 'black');
var textNode = document.createTextNode(t);
text.appendChild(textNode);
document.getElementById('main').appendChild(text);
}
function addLine(id, x1, y1, x2, y2, sc,sw) {
const line = document.createElementNS('http://www.w3.org/2000/svg','line');
line.setAttribute('id', id);
line.setAttribute('x1', x1);
line.setAttribute('y1', y1);
line.setAttribute('x2', x2);
line.setAttribute('y2', y2);
line.setAttribute('stroke', sc);
line.setAttribute('stroke-width', sw);
document.getElementById("main").appendChild(line);
}
function addHorizontalLinesPrice() {
var m = maxPrice;
addText(20, 55, m);
addText(20, 85, m = m-1500);
addText(20, 115, m = m-1500);
addText(20, 145, m = m-1500);
addText(20, 175, m = m-1500);
}
addHorizontalLinesPrice();
function addHorizontalLines() {
var y = 50.5;
for (var i = 0; i < 5; i++) {
addLine("horizontalLine"+(i+1), 90, y, window.innerWidth-20, y, "black", 0.5);
y = y + 30;
}
}
addHorizontalLines();
var totalHeight = 100;
function getYPosition(price) {
// How far the price is on the scale from minPrice to maxPrice
var scale = (price - minPrice) / (maxPrice - minPrice);
// Calculate y position based on the scale
return totalHeight - (scale * totalHeight);
}
prices.forEach(price => {
var x = 20;
var yPos = getYPosition(price);
addLine("", x, yPos, x+200, yPos, "green", 3);
x = x + 100;
});
</script>
<svg id="main" xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="220"></svg>
This is a matter of getting the mapping right from your "world" coordinates to view coordinates.
I would suggest defining all the "magical numbers" -- that relate to coordinates on the web page -- to constants, so they have a meaningful name.
Here is a possible implementation:
// Define the "magic numbers" as constants:
const labelLeft = 20, labelTop = 55,
graphLeft = 90, graphTop = 50.5,
graphWidth = window.innerWidth - graphLeft - 80,
graphStep = 30, numLines = 5;
function addText(x, y, t) {
const text = document.createElementNS('http://www.w3.org/2000/svg','text');
text.setAttribute('x', x);
text.setAttribute('y', y);
text.setAttribute('fill', 'black');
const textNode = document.createTextNode(t);
text.appendChild(textNode);
document.getElementById('main').appendChild(text);
}
function addLine(id, x1, y1, x2, y2, sc, sw) {
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('id', id);
line.setAttribute('x1', x1);
line.setAttribute('y1', y1);
line.setAttribute('x2', x2);
line.setAttribute('y2', y2);
line.setAttribute('stroke', sc);
line.setAttribute('stroke-width', sw);
document.getElementById("main").appendChild(line);
}
function createMapping(prices) {
const minPrice = Math.min(...prices);
const maxPrice = Math.max(...prices);
const step = (maxPrice - minPrice).toFixed(0).replace(/\B./g, "0") / (numLines - 1);
let topPrice = (maxPrice + minPrice + step) / 2 + step * (numLines >> 1);
return [topPrice - topPrice % step, step];
}
function drawLabelsAndLines(topPrice, step) {
for (let i = 0; i < numLines; i++) {
const y = graphTop + i * graphStep;
addText(labelLeft, labelTop + i * graphStep, topPrice - i * step);
addLine("horizontalLine"+(i+1), graphLeft, y, graphLeft + graphWidth, y, "black", 0.5);
}
}
function drawValues(getYPosition, prices) {
let x = graphLeft;
let yPos = getYPosition(prices[0]);
let stepX = graphWidth / (prices.length - 1);
for (const price of prices.slice(1)) {
addLine("", x, yPos, x += stepX, yPos = getYPosition(price), "green", 3);
}
}
function drawGraph(prices) {
const [topPrice, step] = createMapping(prices);
drawLabelsAndLines(topPrice, step);
drawValues(price => graphTop + (topPrice - price) / step * graphStep, prices);
}
// demo
const prices = [28990, 30240, 30890, 32162, 31000, 31820, 31770, 30080, 28340, 28620, 28640, 27930, 28850];
drawGraph(prices);
<svg id="main" xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="220"></svg>