JSFiddle - React, Tailwind, and code Playground
by Graham Dixon
HTML
<svg class="hidden">
<defs>
<g id="cube" class="cube-unit">
<rect width="21" height="24" fill="var(--lightColor)" stroke="var(--strokeColor)" transform="skewY(30)" />
<rect width="21" height="24" fill="var(--darkColor)" stroke="var(--strokeColor)" transform="skewY(-30) translate(21 24.3)" />
<rect width="21" height="21" fill="var(--mainColor)" stroke="var(--strokeColor)" transform="scale(1.41,.81) rotate(45) translate(0 -21)" />
</g>
</defs>
</svg>
CSS
body {
font-family: "Roboto", sans-serif;
margin: 0 auto;
text-align: center;
}
.hidden {
display: none;
}
.outer {
display: flex;
width: 100%;
}
.inner {
width: 50%;
flex-direction: column;
}
.cube-unit {
fill-opacity: 0.9;
stroke-miterlimit: 0;
}
.blue-cube, .blue-cube-generated {
--mainColor: #009cde;
--strokeColor: #0079ad;
--lightColor: #00affa;
--darkColor: #008bc7;
}
.pink-cube, .pink-cube-generated {
--mainColor: #de0063;
--strokeColor: #ad004e;
--lightColor: #fa0070;
--darkColor: #c7005a;
}
.m-left,
.m-right {
transform: translateX(0px);
}
.m-up,
.m-down {
transform: translateY(0px);
}
.m-left {
--translate: -50px;
}
.m-right {
--translate: 50px;
}
svg * {
transition: 0.2s transform;
}
svg:hover .m-left,
svg:hover .m-right {
transform: translateX(var(--translate));
}
svg:hover .m-up,
svg:hover .m-down {
transform: translateY(var(--translate));
}
JavaScript
// generate a n**n cube and seperate into 3 groups [left, middle, right]
function generateCube(n, values) {
// define dimensions and spacing
const cubeWidth = 21;
const cubeHeight = -12;
const horizontalSpacing = 21;
const verticalSpacing = 12;
// define starting position
const startX = 32;
const startY = 64;
// create a new svg
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
// produce the matrix and rotate for layout positions
const cubeMatrix = generateSquareMatrix(n);
const cubePositions = rotateMatrix45Deg(cubeMatrix);
// rotate in the opposite direction to gather groupings
const groupPositions = rotateMatrix45Deg(cubeMatrix, 1);
// fill with cubes before printing to screen
const cubes = [];
// create and position cubes
cubePositions.flat().forEach((dets) => {
// extract row/col
const {
x: row,
y: col
} = dets;
// get depth from position in the cubePositions array
const depth = cubePositions.findIndex((rows, indx) => {
return rows.indexOf(dets) !== -1
});
for (let layer = 0; layer < n; layer++) {
const x = startX + (col * (cubeWidth + horizontalSpacing)) + (depth * horizontalSpacing);
const y = startY + (row * (cubeHeight + verticalSpacing)) + (depth * verticalSpacing) - ((layer * ((1 - cubeHeight) + verticalSpacing - 1)));
// record the cube
cubes.push({
screenX: x,
screenY: y,
x: row,
y: col,
order: layer
});
}
});
// adjust allocations by weighting
const adjustedAllocations = adjustAllocations(values, n);
// group according to cubes position
const [leftGroup, middleGroup, rightGroup] = distributeCubes(adjustedAllocations, cubeMatrix, cubes);
// print the cubes
cubes.forEach((pos) => {
// Create a new cube element (assuming SVG)
const cube = document.createElementNS("http://www.w3.org/2000/svg", "use");
...