Построение сетчатой трехмерной гистограммы с помощью SVG


Во-первых, вы можете проверить результаты моего приложения в продакшене: Playground of SSR-Contributions-svg.

Нарисуйте базовый куб

Давайте начнем с одного из самых простых кубов.

Подумайте о нем как о комбинации нескольких геометрических фигур, расположенных в декартовой системе координат, это может выглядеть так (Обратите внимание, что в системе координат svg положительное направление оси y направлено вниз):

Определите основные понятия вершин и граней, как показано на схеме ниже. Мы используем вершину O в качестве начала координат куба.

Здесь перспективы фактически определяются формой верхней грани. Поэтому нам нужно изменить перспективы, определив длину двух диагоналей в горизонтальном и вертикальном направлениях.

Затем мы можем получить координаты всех точек (предположим, что координаты начала координат O равны ‘ox’, ‘oy’):

Начните кодирование с определения переменных (пишите внутри тега <script> html-файла):

const sizeX = 20;
const ratio = 2;
const sizeY = sizeX / ratio;
const height = 40;
const ox = 100;
const oy = 100;
const pA = [ox - sizeX, oy + sizeY];
const pB = [ox, oy + 2 * sizeY];
const pC = [ox + sizeX, oy + sizeY];
const pD = [ox - sizeX, oy - height + sizeY];
const pE = [ox, oy - height + 2 * sizeY];
const pF = [ox + sizeX, oy - height + sizeY];
const pG = [ox, oy - height];
const pO = [ox, oy];
const face_l = [pE, pD, pA, pB];
const face_r = [pE, pF, pC, pB];
const face_t = [pE, pD, pG, pF];
Войдите в полноэкранный режим Выход из полноэкранного режима

Мы будем рисовать геометрическую поверхность с помощью тега path в svg, поэтому сначала определим метод генерации критических путей, чтобы облегчить написание кода:

function d(points) {
    const raw = points
     .map((point) => `${point[0]} ${point[1]}`)
     .join(' ');
    return "M" + raw + "z";
}
Вход в полноэкранный режим Выход из полноэкранного режима

Затем мы можем получить код svg и записать его в документ:

const svg = `<svg width="200" height="200">
    <path d="${d(face_l)}" stroke="#000" fill="transparent" />
    <path d="${d(face_r)}" stroke="#000" fill="transparent" />
    <path d="${d(face_t)}" stroke="#000" fill="transparent" />
</svg>`;
document.write(svg);
Войти в полноэкранный режим Выйти из полноэкранного режима

Нарисуйте сетку из кубиков

Определите сетку, указав rowNum и colNum:

Предположим, что начало координат svg лежит на 0, 0 сетки (желтый блок), нам нужно переместить всю сетку по горизонтали. И установите maxBarHeight = 100.

Ширина сетки должна быть : sizeX * (rowNum + colNum), высота должна быть: sizeY * (rowNum + colNum) + maxBarHeight , translateX и translateY всей сетки: translateX = rowNum * sizeX , translateY = maxBarHeight.

Определите функцию для рисования базового куба (так же, как указано выше, но ox, oy, height должны быть переданы как параметры функции):

function cube(ox, oy, height) {
    const pO = [ox, oy];
    const pA = [ox - sizeX, oy + sizeY];
    const pB = [ox, oy + 2 * sizeY];
    const pC = [ox + sizeX, oy + sizeY];
    const pD = [ox - sizeX, oy - height + sizeY];
    const pE = [ox, oy - height + 2 * sizeY];
    const pF = [ox + sizeX, oy - height + sizeY];
    const pG = [ox, oy - height];
    const face_l = [pE, pD, pA, pB];
    const face_r = [pE, pF, pC, pB];
    const face_t = [pE, pD, pG, pF];

    return `<g>
        <path d="${d(face_l)}" stroke="#000" fill="#555" />
        <path d="${d(face_r)}" stroke="#000" fill="#888" />
        <path d="${d(face_t)}" stroke="#000" fill="#aaa" />
    </g>`;
}
Войти в полноэкранный режим Выход из полноэкранного режима

Определите функцию для координации rowIndex и colIndex в пикселях:

function coordIndex(rowIndex, colIndex) {
    return [
        (rowIndex - colIndex) * sizeX, 
        (rowIndex + colIndex) * sizeY
    ];
}
Войти в полноэкранный режим Выход из полноэкранного режима

Генерировать случайные данные:

const colNum = 6; // num of cols
const rowNum = 4; // num of rows
const cubes = [];
for (let i = 0; i < colNum; i++) {
    for (let j = 0; j < rowNum; j++) {
        cubes.push([
            i, 
            j, 
            Math.floor(Math.random() * maxBarHeight)
        ]);
    }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Генерировать svg-код:

// render
const svg = `<svg width="${svgWidth}" height="${svgHeight}">
    <g transform="translate(${translateX}, ${translateY})">
    ${cubes.map((opt) => cube(
        ...coordIndex(...opt), 
        opt[2]
    )).join('')}
    </g>
</svg>`;
Войти в полноэкранный режим Выйти из полноэкранного режима

Окончательные коды на codepen:

Оцените статью
devanswers.ru
Добавить комментарий