Last active
December 21, 2024 18:11
-
-
Save OrionReed/4c3778ebc2b5026d2354359ca49077ca to your computer and use it in GitHub Desktop.
3D DOM viewer, copy-paste this into your console to visualise the DOM topographically.
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
// 3D Dom viewer, copy-paste this into your console to visualise the DOM as a stack of solid blocks. | |
// You can also minify and save it as a bookmarklet (https://www.freecodecamp.org/news/what-are-bookmarklets/) | |
(() => { | |
const SHOW_SIDES = false; // color sides of DOM nodes? | |
const COLOR_SURFACE = true; // color tops of DOM nodes? | |
const COLOR_RANDOM = false; // randomise color? | |
const COLOR_HUE = 190; // hue in HSL (https://hslpicker.com) | |
const MAX_ROTATION = 180; // set to 360 to rotate all the way round | |
const THICKNESS = 20; // thickness of layers | |
const DISTANCE = 10000; // ¯\\_(ツ)_/¯ | |
function getRandomColor() { | |
const hue = Math.floor(Math.random() * 360); | |
const saturation = 50 + Math.floor(Math.random() * 30); | |
const lightness = 40 + Math.floor(Math.random() * 30); | |
return `hsl(${hue}, ${saturation}%, ${lightness}%)`; | |
} | |
const getDOMDepth = element => [...element.children].reduce((max, child) => Math.max(max, getDOMDepth(child)), 0) + 1; | |
const domDepthCache = getDOMDepth(document.body); | |
const getColorByDepth = (depth, hue = 0, lighten = 0) => `hsl(${hue}, 75%, ${Math.min(10 + depth * (1 + 60 / domDepthCache), 90) + lighten}%)`; | |
// 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.perspective = DISTANCE; | |
const perspectiveOriginX = (window.innerWidth / 2); | |
const perspectiveOriginY = (window.innerHeight / 2); | |
body.style.perspectiveOrigin = body.style.transformOrigin = `${perspectiveOriginX}px ${perspectiveOriginY}px`; | |
traverseDOM(body, 0, 0, 0); | |
document.addEventListener("mousemove", (event) => { | |
const rotationY = (MAX_ROTATION * (1 - event.clientY / window.innerHeight) - (MAX_ROTATION / 2)); | |
const rotationX = (MAX_ROTATION * event.clientX / window.innerWidth - (MAX_ROTATION / 2)); | |
body.style.transform = `rotateX(${rotationY}deg) rotateY(${rotationX}deg)`; | |
}); | |
// Create side faces for an element to give it a 3D appearance | |
function createSideFaces(element, color) { | |
if (!SHOW_SIDES) { return } | |
const width = element.offsetWidth; | |
const height = element.offsetHeight; | |
const fragment = document.createDocumentFragment(); | |
// Helper function to create and style a face | |
const createFace = ({ width, height, transform, transformOrigin, top, left, right, bottom }) => { | |
const face = document.createElement('div'); | |
face.className = 'dom-3d-side-face'; | |
Object.assign(face.style, { | |
transformStyle: "preserve-3d", | |
backfaceVisibility: 'hidden', | |
position: 'absolute', | |
width: `${width}px`, | |
height: `${height}px`, | |
background: color, | |
transform, | |
transformOrigin, | |
overflow: 'hidden', | |
willChange: 'transform', | |
top, | |
left, | |
right, | |
bottom | |
}); | |
fragment.appendChild(face); | |
} | |
// Top face | |
createFace({ | |
width, | |
height: THICKNESS, | |
transform: `rotateX(-270deg) translateY(${-THICKNESS}px)`, | |
transformOrigin: 'top', | |
top: '0px', | |
left: '0px', | |
}); | |
// Right face | |
createFace({ | |
width: THICKNESS, | |
height, | |
transform: 'rotateY(90deg)', | |
transformOrigin: 'left', | |
top: '0px', | |
left: `${width}px` | |
}); | |
// Bottom face | |
createFace({ | |
width, | |
height: THICKNESS, | |
transform: `rotateX(-90deg) translateY(${THICKNESS}px)`, | |
transformOrigin: 'bottom', | |
bottom: '0px', | |
left: '0px' | |
}); | |
// Left face | |
createFace({ | |
width: THICKNESS, | |
height, | |
transform: `translateX(${-THICKNESS}px) rotateY(-90deg)`, | |
transformOrigin: 'right', | |
top: '0px', | |
left: '0px' | |
}); | |
element.appendChild(fragment); | |
} | |
// Recursive function to traverse child nodes, apply 3D styles, and create side faces | |
function traverseDOM(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('dom-3d-side-face'))) continue; | |
const color = COLOR_RANDOM ? getRandomColor() : getColorByDepth(depthLevel, COLOR_HUE, -5); | |
Object.assign(childNode.style, { | |
transform: `translateZ(${THICKNESS}px)`, | |
overflow: "visible", | |
backfaceVisibility: "hidden", | |
isolation: "auto", | |
transformStyle: "preserve-3d", | |
backgroundColor: COLOR_SURFACE ? color : getComputedStyle(childNode).backgroundColor, | |
willChange: 'transform', | |
}); | |
let updatedOffsetX = offsetX; | |
let updatedOffsetY = offsetY; | |
if (childNode.offsetParent === parentNode) { | |
updatedOffsetX += parentNode.offsetLeft; | |
updatedOffsetY += parentNode.offsetTop; | |
} | |
createSideFaces(childNode, color); | |
traverseDOM(childNode, depthLevel + 1, updatedOffsetX, updatedOffsetY); | |
} | |
} | |
})() |
This is amazing!
FYI you can make a bookmarklet in Firefox/Chrome and add this as the url to create a 1-click way of loading this into any site.
javascript:(()=>{function e(){return`hsl(${Math.floor(360*Math.random())}, ${50+Math.floor(30*Math.random())}%, ${40+Math.floor(30*Math.random())}%)`}let t=e=>[...e.children].reduce((e,n)=>Math.max(e,t(n)),0)+1,n=t(document.body),r=(e,t=0,r=0)=>`hsl(${t}, 75%, ${Math.min(10+e*(1+60/n),90)+r}%)`,o=document.body;o.style.overflow="visible",o.style.transformStyle="preserve-3d",o.style.perspective=1e4;let i=window.innerWidth/2,l=window.innerHeight/2;function s(e,t){}function f(e,t,n,o){for(let i=e.childNodes,l=i.length,$=0;$<l;$++){let a=i[$];if(!(1===a.nodeType&&!a.classList.contains("dom-3d-side-face")))continue;let d=r(t,190,-5);Object.assign(a.style,{transform:"translateZ(20px)",overflow:"visible",backfaceVisibility:"hidden",transformStyle:"preserve-3d",backgroundColor:d,willChange:"transform"});let c=n,m=o;a.offsetParent===e&&(c+=e.offsetLeft,m+=e.offsetTop),s(a,d),f(a,t+1,c,m)}}o.style.perspectiveOrigin=o.style.transformOrigin=`${i}px ${l}px`,f(o,0,0,0),document.addEventListener("mousemove",e=>{let t=180*(1-e.clientY/window.innerHeight)-90,n=180*e.clientX/window.innerWidth-90;o.style.transform=`rotateX(${t}deg) rotateY(${n}deg)`})})();
This is excellent. Waoh! Well done
FYI you can make a bookmarklet in Firefox/Chrome and add this as the url to create a 1-click way of loading this into any site.
javascript:(()=>{function e(){return`hsl(${Math.floor(360*Math.random())}, ${50+Math.floor(30*Math.random())}%, ${40+Math.floor(30*Math.random())}%)`}let t=e=>[...e.children].reduce((e,n)=>Math.max(e,t(n)),0)+1,n=t(document.body),r=(e,t=0,r=0)=>`hsl(${t}, 75%, ${Math.min(10+e*(1+60/n),90)+r}%)`,o=document.body;o.style.overflow="visible",o.style.transformStyle="preserve-3d",o.style.perspective=1e4;let i=window.innerWidth/2,l=window.innerHeight/2;function s(e,t){}function f(e,t,n,o){for(let i=e.childNodes,l=i.length,$=0;$<l;$++){let a=i[$];if(!(1===a.nodeType&&!a.classList.contains("dom-3d-side-face")))continue;let d=r(t,190,-5);Object.assign(a.style,{transform:"translateZ(20px)",overflow:"visible",backfaceVisibility:"hidden",transformStyle:"preserve-3d",backgroundColor:d,willChange:"transform"});let c=n,m=o;a.offsetParent===e&&(c+=e.offsetLeft,m+=e.offsetTop),s(a,d),f(a,t+1,c,m)}}o.style.perspectiveOrigin=o.style.transformOrigin=`${i}px ${l}px`,f(o,0,0,0),document.addEventListener("mousemove",e=>{let t=180*(1-e.clientY/window.innerHeight)-90,n=180*e.clientX/window.innerWidth-90;o.style.transform=`rotateX(${t}deg) rotateY(${n}deg)`})})();
This is great! Thanks!
Pardon my french, but what the fuck.
edit: the video isn't embedding. basically, it causes firefox to spaz out on twitter.
link (bright flashing lights warning): https://utfs.io/f/b6f07e7a-6460-4dd0-862a-de3d6bd85152-1yk8hb.webm
Very cool! Just commenting to add a link to your tweet that led me here: https://x.com/OrionReedOne/status/1772934478421188620
It's cool! Love it <3
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Revised the gist, should now work on many more websites which previously looked flat thanks to forcing
isolation: auto