SVG를 사용하여 메시 3D 막대 차트 작성

18571 단어

First, you can check out the results of my application in production: Playground of SSR-Contributions-svg.



기본 정육면체 그리기



가장 기본적인 큐브 중 하나부터 시작하겠습니다.



Cartesian coordinate system에 배치된 여러 기하학적 도형의 조합으로 생각하면 다음과 같이 보일 수 있습니다(svg의 좌표계에서 y축의 양의 방향은 아래쪽입니다).



아래 그림과 같이 정점과 면의 기본 개념을 정의합니다. 정점 O를 입방체의 원점으로 사용합니다.



여기서 원근감은 실제로 윗면의 모양에 따라 결정됩니다. 따라서 가로 세로 방향의 두 대각선 길이를 정의하여 원근법을 수정해야 합니다.



그런 다음 모든 점의 좌표를 얻을 수 있습니다(원점 O의 좌표가 'ox', 'oy'라고 가정).



변수 정의로 코딩 시작(html 파일의 <script> 태그 안에 작성):

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];


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) , 높이는 다음과 같아야 합니다.

기본 정육면체를 그리는 함수를 정의합니다.

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>`;
}

sizeY * (rowNum + colNum) + maxBarHeighttranslateX를 픽셀로 조정하는 함수를 정의합니다.

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의 최종 코드:



https://codepen.io/catsjuice/pen/MWVqNdQ

좋은 웹페이지 즐겨찾기