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
981
zegarkidawida
Abrir en el editor
Publica tu código
Recomendado
22 June 2025
Plantilla de blog Tailwind CSS: modo oscuro, adaptable
18 October 2024
Plantilla de sitio web de cartera HTML CSS
14 May 2025
sistema de donaciones con múltiples montos
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('') }