WEBLEB
Inicio
Editora de código
Iniciar sesión
Pro
Español
English
Français
Español
Português
Deutsch
Italiano
हिंदी
HTML
CSS
JS
bands
cells
size
speed
texture
↗
↗
↗
borders
@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 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 => `
${chunk(state.cells, j => `
`)}
`) } function chunk(howMany, mapFn) { return Array .from({ length: howMany }, (_, i) => mapFn(i)) .join('') }
Validating your code, please wait...
HTML
CSS
JS
bands
cells
size
speed
texture
↗
↗
↗
borders
@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 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 => `
${chunk(state.cells, j => `
`)}
`) } function chunk(howMany, mapFn) { return Array .from({ length: howMany }, (_, i) => mapFn(i)) .join('') }