WEBLEB
Inicio
Editora de código
Iniciar sesión
Pro
Español
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Esfera CSS 3D falsa con textura
1190
zegarkidawida
Abrir en el editor
Publica tu código
Recomendado
30 June 2025
Formulario de registro HTML con correo electrónico y contraseña
27 September 2025
Fondo animado CSS con formas y texto
30 March 2025
Un código de ytr3d3
HTML
Copy
bands
cells
size
speed
texture
↗
↗
↗
borders
CSS
Copy
@layer cosmetics, utilities, demo; @layer demo { body { height: 100dvh; display: grid; place-content: center; } .world { --bands: 130; --cells: 60; --_size: 80; --size: calc(var(--_size) * 1dvmin); --_speed: 15; --speed: calc((20.5 - var(--_speed)) * 1s); --image: url(https://assets.codepen.io/25387/Gall_Stereographic_projection_SW_centered.jpg); display: flex; align-items: center; flex-direction: column; clip-path: circle(); } .band { --latitude: calc(sin(PI / var(--bands) * var(--i)) * 1.2); --width: calc(var(--size) * var(--latitude)); width: var(--width); height: calc(var(--size) / var(--bands) * var(--latitude)); display: flex; justify-content: center; } .cell { --longitude: calc(sin(PI / var(--cells) * var(--j)) * 1.2); --bg-width: calc(var(--width) * var(--longitude) * 1.25); --bg-height: calc(var(--size) * 0.9); height: calc(var(--size) * var(--longitude)); width: calc(var(--width) / var(--cells) * var(--longitude)); background: var(--image) 50% fixed; background-size: var(--bg-width) var(--bg-height); animation: spin var(--speed) linear infinite; } .with-borders .cell { box-shadow: 0 0 0 1px chartreuse; } @keyframes spin { 100% { background-position-x: calc(50% + var(--bg-width)); } } } @layer cosmetics { body { overflow: hidden; margin: 0; background: cornsilk; } .world::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: cornsilk; opacity: .5; mix-blend-mode: color; } } @layer utilities { .utilities { position: absolute; top: 0; right: 0; z-index: 1; padding: 1em; } .utilities .control { display: flex; justify-content: center; align-items: center; min-height: 24px; gap: .5em; font-size: 1.1em; font-family: monospace; font-variant: small-caps; letter-spacing: .05em; } .utilities #image:has(:focus) { border: 2px solid royalblue; } .utilities #image input { position: absolute; top: -100dvh; left: -100dvw; opacity: 0; } .utilities .image-wrapper { --color-1: #ddd; --color-2: royalblue; position: relative; width: max(10dvmin, 48px); height: max(10dvmin, 48px); color: var(--color-1); } .utilities .image-wrapper a { display: none; position: absolute; bottom: 0; right: 0; translate: 30% 30%; width: 30%; height: 30%; justify-content: center; align-items: center; font-size: 1.5em; text-decoration: none; background: var(--color-1); color: var(--color-2); border-radius: 50%; } .utilities .image-wrapper img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; border: .5dvmin solid var(--color-1); } .utilities #image :checked + .image-wrapper { --color-1: royalblue; --color-2: #ddd; } .utilities #image :checked + .image-wrapper a { display: inline-flex; } }
JS
Copy
/* * JS just handles the playground of parameters * and inserts the repeated markup. */ const UI = { bands: document.querySelector('#bands'), cells: document.querySelector('#cells'), size: document.querySelector('#size'), speed: document.querySelector('#speed'), image: document.querySelectorAll('#image input'), borders: document.querySelector('#with-border'), world: document.querySelector('.world') } const state = { bands: +UI.bands.value, cells: +UI.cells.value, size: +UI.size.value, speed: +UI.speed.value, image: UI.image[0].value, borders: UI.borders.checked } UI.bands.addEventListener('input', (e) => { state.bands = +e.target.value render() }) UI.cells.addEventListener('input', (e) => { state.cells = +e.target.value render() }) UI.size.addEventListener('input', (e) => { state.size = +e.target.value render() }) UI.speed.addEventListener('input', (e) => { state.speed = +e.target.value render() }) Array.from(UI.image).forEach(input => { input.addEventListener('change', () => { state.image = input.value render() }) }) UI.borders.addEventListener('input', (e) => { state.borders = e.target.checked render() }) render() function render() { UI.world.style.setProperty('--bands', state.bands) UI.world.style.setProperty('--cells', state.cells) UI.world.style.setProperty('--_size', state.size) UI.world.style.setProperty('--_speed', state.speed) UI.world.style.setProperty('--image', state.image) UI.world.classList.toggle('with-borders', state.borders) UI.world.innerHTML = chunk(state.bands, i => ` <div class="band" style="--i: ${i}"> ${chunk(state.cells, j => ` <div class="cell" style="--j: ${j}"></div> `)} </div> `) } function chunk(howMany, mapFn) { return Array .from({ length: howMany }, (_, i) => mapFn(i)) .join('') }