Last active
May 12, 2024 07:56
-
Star
1,409
You must be signed in to star a gist -
Fork
108
You must be signed in to fork a gist
-
-
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); | |
} | |
} | |
})() |
@ylluminate ^ absolutely. we can push improvement from that repo into this single-function gist and reference those issues/changes here.
This should be a browser plugin.
I have a updated version without the blue color:
https://gist.github.com/swnck/7aa80d2968a115ada8d920d772823cc8
@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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Everyone: since @OrionReed is now developing browser extensions (https://github.com/OrionReed/dom3d), it might be best to file issues at https://github.com/OrionReed/dom3d/issues - even if they are referenced here.
I think it might make it easier to track and resolve specific things.