-
-
Save hmmhmmhm/85d9dbc166f94c5693bad6852e092353 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copy-paste into your console (or minify and save as a bookmarklet) to see any DOM in an almost-sorta-working 3D stack of DOM elements. | |
// The front faces are colored for debugging, if this gist can be fixed, they can return to their normal styling. | |
(function () { | |
const MAX_ROTATION = 180; | |
const DEPTH_INCREMENT = 25; | |
const PERSPECTIVE = 1000; | |
const SIDE_FACE_CLASS = 'side-face'; | |
const MAX_DOM_DEPTH = getMaxDepth(document.body); | |
// Calculate color based on depth, ensuring lighter colors for deeper elements | |
function getColorByDepth(depth) { | |
return `hsl(190, 75%, ${Math.min(10 + depth * (1 + 60 / MAX_DOM_DEPTH), 90)}%)`; | |
} | |
// Calculate the maximum depth of the DOM tree | |
function getMaxDepth(element) { | |
return Array.from(element.children).reduce((max, child) => | |
Math.max(max, getMaxDepth(child)), 0) + 1; | |
} | |
// Freate side faces for an element to give it a 3D appearance | |
function createSideFaces(element, depthLevel) { | |
const width = element.offsetWidth; | |
const height = element.offsetHeight; | |
const color = getColorByDepth(depthLevel); | |
const faces = [ | |
{ | |
transform: `translateX(${width}px) rotateY(90deg)`, | |
width: DEPTH_INCREMENT, | |
height: height | |
}, | |
{ | |
transform: `translateX(-${DEPTH_INCREMENT}px) rotateY(-90deg)`, | |
width: DEPTH_INCREMENT, | |
height: height | |
}, | |
{ | |
transform: `translateY(-${DEPTH_INCREMENT}px) rotateX(90deg)`, | |
width: width, | |
height: DEPTH_INCREMENT | |
}, | |
{ | |
transform: `translateY(${height}px) rotateX(-90deg)`, | |
width: width, | |
height: DEPTH_INCREMENT | |
} | |
]; | |
// Create a face for each side of the element | |
faces.forEach(({ transform, width, height }) => { | |
const face = document.createElement('div'); | |
face.className = SIDE_FACE_CLASS; | |
Object.assign(face.style, { | |
transformStyle: "preserve-3d", | |
position: 'absolute', | |
width: `${width}px`, | |
height: `${height}px`, | |
background: color, | |
transform: transform, | |
overflow: 'hidden', | |
backfaceVisibility: 'hidden', | |
willChange: 'transform', | |
zIndex: `${depthLevel}` | |
}) | |
element.appendChild(face); | |
}); | |
} | |
// Recursive function to traverse child nodes, apply 3D styles, and create side faces | |
function traverseChildren(parentNode, depthLevel, offsetX, offsetY) { | |
for (let children = parentNode.childNodes, childrenCount = children.length, i = 0; i < childrenCount; i++) { | |
const childNode = children[i]; | |
if (1 === childNode.nodeType && !childNode.classList.contains(SIDE_FACE_CLASS)) { | |
Object.assign(childNode.style, { | |
transform: `translateZ(${DEPTH_INCREMENT}px)`, | |
position: 'relative', | |
backfaceVisibility: 'hidden', | |
overflow: "visible", | |
transformStyle: "preserve-3d", | |
zIndex: `${depthLevel}`, | |
backgroundColor: `#${Math.floor(Math.random() * 16777215).toString(16)}`, // Random color face while debugging | |
willChange: 'transform', | |
}); | |
childNode.offsetParent === parentNode && (offsetX += parentNode.offsetLeft, offsetY += parentNode.offsetTop); | |
createSideFaces(childNode, depthLevel); | |
traverseChildren(childNode, depthLevel + 1, offsetX, offsetY); | |
} | |
} | |
} | |
// Apply initial styles to the body to enable 3D perspective | |
const body = document.body; | |
body.style.overflow = "visible"; | |
body.style.transformStyle = "preserve-3d"; | |
body.style.backfaceVisibility = 'hidden'; | |
body.style.perspective = PERSPECTIVE; | |
const perspectiveOriginX = (window.innerWidth / 2); | |
const perspectiveOriginY = (window.innerHeight / 2); | |
body.style.perspectiveOrigin = body.style.transformOrigin = `${perspectiveOriginX}px ${perspectiveOriginY}px`; | |
traverseChildren(body, 0, 0, 0); | |
// Event listener to rotate the DOM based on mouse movement | |
document.addEventListener("mousemove", (event) => { | |
const rotationY = (MAX_ROTATION * (1 - event.screenY / screen.height) - (MAX_ROTATION / 2)); | |
const rotationX = (MAX_ROTATION * event.screenX / screen.width - (MAX_ROTATION / 2)); | |
body.style.transform = `rotateX(${rotationY}deg) rotateY(${rotationX}deg)`; | |
}); | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment