-
-
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 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); | |
} | |
} | |
})() |
@malikalimoekhamedov it is being plugin-ified here here
@malikalimoekhamedov it is being plugin-ified here here
Beautiful
I've added some of the few major issues to the extension repo and if anyone can fix any of them I will buy you a coffee or drink of your choice!
Specifically, there are 3 issues that are causing the majority of problems:
Revised the gist, should now work on many more websites which previously looked flat thanks to forcing isolation: auto
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
I have a updated version without the blue color:
https://gist.github.com/swnck/7aa80d2968a115ada8d920d772823cc8