Во-первых, вы можете проверить результаты моего приложения в продакшене: 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: