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
631
zegarkidawida
Abrir en el editor
Publica tu código
Recomendado
5 March 2024
Animación de texto CSS
30 March 2025
Un código de ytr3d3
25 August 2024
Animación Onscroll: desplazamiento dinámico de contenido con ScrollMagic
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('') }