рисовать на диаграмме холста html отрицательные и положительные области

Я создаю индивидуальную диаграмму на основе холста HTML. Я хочу рисовать отрицательные и положительные значения другими цветами, но пока не могу этого добиться.

Вот моя фактическая диаграмма:

введите здесь описание изображения

Вот чего я хочу добиться:

введите здесь описание изображения

Я могу придумать два решения. Либо я могу нарисовать его с градиентом, который опускается только до нуля.

Или каждая положительная и каждая отрицательная поверхность рисуется отдельно. Но тогда мне все еще нужны пересечения.

Вот мои исходные значения:

[
  { "x": 0, "y": 750 },
  { "x": 1, "y": -200 },
  { "x": 2, "y": 60 },
  { "x": 3, "y": 60 },
  { "x": 4, "y": 120 },
  { "x": 5, "y": 505 }
]

Вот мои преобразованные значения пикселей:

[
  { "x": 0, "y": 236}, // to draw the area
  { "x": 0, "y": 0},
  { "x": 173, "y": 300}, 
  { "x": 346, "y": 217}, 
  { "x": 519, "y": 217}, 
  { "x": 692, "y": 198}, 
  { "x": 865, "y": 77}, 
  { "x": 865, "y": 236} // to draw the area
]

Есть ли у вас представление о том, как можно реализовать цель? Благодарю вас!


person wittgenstein    schedule 03.08.2020    source источник
comment
Or each positive and each negative surface is drawn separately. But then I still need the intersections. это. Используйте линейную алгебру, чтобы найти нули и нарисовать отдельно.   -  person Jannes Carpentier    schedule 03.08.2020


Ответы (1)


Я немного повеселился, создавая базовый рендерер диаграмм с использованием холста. Я надеюсь, что вы найдете следующий код полезным. (если что-то непонятно, не стесняйтесь спрашивать)

const c = document.getElementById("mycanvas");
const cc = c.getContext("2d");

const points = [
  750, -200, 60, 60, 120, 505
];

const maxY = Math.max(...points);
const minY = Math.min(...points);

//used to scale and fit the graph into the canvas
const xaxis = (maxY / (maxY - minY)) * c.height;
const yscale = -c.height / (maxY - minY);
const xscale = c.width / (points.length - 1);

const poscolor = "cornflowerblue";
const negcolor = "crimson";
cc.fillStyle = points[0] >= 0 ? poscolor : negcolor;

//start at (0, 0) and line to first point
cc.beginPath();
cc.moveTo(0, xaxis);
cc.lineTo(0, points[0] * yscale + xaxis);
for (let i = 1; i < points.length; i++) {
  const a = {
    x: i - 1,
    y: points[i - 1]
  };
  const b = {
    x: i,
    y: points[i]
  };
  //if a.y and b.y have different sign, the line will intersect the x-axis
  if (a.y * b.y < 0) {
    //calculate intersection (point on x-axis)
    const intersection = -a.y / (b.y - a.y);
    const intersectionPoint = (a.x + intersection) * xscale;
    //complete the current shape
    cc.lineTo(intersectionPoint, xaxis);
    cc.fill();
    //start a new shape for the other side of the x-axis
    cc.fillStyle = b.y >= 0 ? poscolor : negcolor;
    cc.beginPath();
    cc.moveTo(intersectionPoint, xaxis);
  }
  //continue the shape to point b
  cc.lineTo(b.x * xscale, b.y * yscale + xaxis);
}
//draw a line back to the x-axis and finish the shape
cc.lineTo((points.length - 1) * xscale, xaxis);
cc.fill();
canvas {
  background-color: ivory;
  border: 2px solid darkgray;
}
<!DOCTYPE html>
<html>

<body>
  <canvas id="mycanvas" width=500 height=200></canvas>
</body>

</html>

person Jannes Carpentier    schedule 03.08.2020
comment
Спасибо за хороший пример. Я попытаюсь реализовать это в своем коде и заставить его работать с несколькими наборами данных. Выглядит действительно чистым и понятным на первый взгляд. Мерси - person wittgenstein; 03.08.2020