WEBLEB
Home
Editor
Accedi
Pro
Italiano
English
Français
Español
Português
Deutsch
Italiano
हिंदी
1759
Andev.web
Apri nell'Editor
Pubblica il Tuo Codice
Consigliato
13 June 2025
Un codice di Metehan
17 May 2025
emoji danzante
HTML
Copy
Andev Web
Christ the
Redeemer
Rio de Janeiro
Iconic symbol of Brazil
Pyramids of
Teotihuacan
Mexico
Architectural wonder of ancient Mesoamerica
Machu Picchu
Peru
Lost city of the Incas
Loading...
CSS
Copy
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@500;600;700;800&display=swap"); :root { --card-width: 200px; --card-height: 300px; --card-transition-duration: 800ms; --card-transition-easing: ease; } * { box-sizing: border-box; margin: 0; padding: 0; } body { width: 100%; height: 100vh; display: flex; justify-content: center; align-items: center; background: rgba(0, 0, 0, 0.787); overflow: hidden; } button { border: none; background: none; cursor: pointer; } button:focus { outline: none; border: none; } .app { position: relative; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } .app__bg { position: absolute; width: 100%; height: 100%; z-index: -5; filter: blur(8px); pointer-events: none; user-select: none; overflow: hidden; } .app__bg::before { content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: #000; z-index: 1; opacity: 0.8; } .app__bg__image { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) translateX(var(--image-translate-offset, 0)); width: 180%; height: 180%; transition: transform 1000ms ease, opacity 1000ms ease; overflow: hidden; } .app__bg__image img { width: 100%; height: 100%; object-fit: cover; } .app__bg__image.current--image { opacity: 1; --image-translate-offset: 0; } .app__bg__image.previous--image, .app__bg__image.next--image { opacity: 0; } .app__bg__image.previous--image { --image-translate-offset: -25%; } .app__bg__image.next--image { --image-translate-offset: 25%; } .cardList { position: absolute; width: calc(3 * var(--card-width)); height: auto; } .cardList__btn { --btn-size: 35px; width: var(--btn-size); height: var(--btn-size); position: absolute; top: 50%; transform: translateY(-50%); z-index: 100; } .cardList__btn.btn--left { left: -5%; } .cardList__btn.btn--right { right: -5%; } .cardList__btn .icon { width: 100%; height: 100%; } .cardList__btn .icon svg { width: 100%; height: 100%; } .cardList .cards__wrapper { position: relative; width: 100%; height: 100%; perspective: 1000px; } .card { --card-translateY-offset: 100vh; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) translateX(var(--card-translateX-offset)) translateY(var(--card-translateY-offset)) rotateY(var(--card-rotation-offset)) scale(var(--card-scale-offset)); display: inline-block; width: var(--card-width); height: var(--card-height); transition: transform var(--card-transition-duration) var(--card-transition-easing); user-select: none; } .card::before { content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: #000; z-index: 1; transition: opacity var(--card-transition-duration) var(--card-transition-easing); opacity: calc(1 - var(--opacity)); } /*Andev Web*/ .card__image { position: relative; width: 100%; height: 100%; } .card__image img { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; } .card.current--card { --current-card-rotation-offset: 0; --card-translateX-offset: 0; --card-rotation-offset: var(--current-card-rotation-offset); --card-scale-offset: 1.2; --opacity: 0.8; } .card.previous--card { --card-translateX-offset: calc(-1 * var(--card-width) * 1.1); --card-rotation-offset: 25deg; } .card.next--card { --card-translateX-offset: calc(var(--card-width) * 1.1); --card-rotation-offset: -25deg; } .card.previous--card, .card.next--card { --card-scale-offset: 0.9; --opacity: 0.4; } .infoList { position: absolute; width: calc(3 * var(--card-width)); height: var(--card-height); pointer-events: none; } .infoList .info__wrapper { position: relative; width: 100%; height: 100%; display: flex; justify-content: flex-start; align-items: flex-end; perspective: 1000px; transform-style: preserve-3d; } .info { margin-bottom: calc(var(--card-height) / 8); margin-left: calc(var(--card-width) / 1.5); transform: translateZ(2rem); transition: transform var(--card-transition-duration) var(--card-transition-easing); } .info .text { position: relative; font-family: "Montserrat"; font-size: calc(var(--card-width) * var(--text-size-offset, 0.2)); white-space: nowrap; color: #fff; width: fit-content; } .info .name, .info .location { text-transform: uppercase; } .info .location { font-weight: 800; } .info .location { --mg-left: 40px; --text-size-offset: 0.12; font-weight: 600; margin-left: var(--mg-left); margin-bottom: calc(var(--mg-left) / 2); padding-bottom: 0.8rem; } .info .location::before, .info .location::after { content: ""; position: absolute; background: #fff; left: 0%; transform: translate(calc(-1 * var(--mg-left)), -50%); } .info .location::before { top: 50%; width: 20px; height: 5px; } .info .location::after { bottom: 0; width: 60px; height: 2px; } .info .description { --text-size-offset: 0.065; font-weight: 500; } .info.current--info { opacity: 1; display: block; } .info.previous--info, .info.next--info { opacity: 0; display: none; } .loading__wrapper { position: fixed; left: 0; top: 0; width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; background: #000; z-index: 200; } .loading__wrapper .loader--text { color: #fff; font-family: "Montserrat"; font-weight: 500; margin-bottom: 1.4rem; } .loading__wrapper .loader { position: relative; width: 200px; height: 2px; background: rgba(255, 255, 255, 0.25); } .loading__wrapper .loader span { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: red; transform: scaleX(0); transform-origin: left; } @media only screen and (min-width: 800px) { :root { --card-width: 250px; --card-height: 400px; } } .support { position: absolute; right: 10px; bottom: 10px; padding: 10px; display: flex; } .support a { margin: 0 10px; color: #fff; font-size: 1.8rem; backface-visibility: hidden; transition: all 150ms ease; } .support a:hover { transform: scale(1.1); }
JS
Copy
console.clear(); const { gsap, imagesLoaded } = window; const buttons = { prev: document.querySelector(".btn--left"), next: document.querySelector(".btn--right"), }; const cardsContainerEl = document.querySelector(".cards__wrapper"); const appBgContainerEl = document.querySelector(".app__bg"); const cardInfosContainerEl = document.querySelector(".info__wrapper"); buttons.next.addEventListener("click", () => swapCards("right")); buttons.prev.addEventListener("click", () => swapCards("left")); function swapCards(direction) { const currentCardEl = cardsContainerEl.querySelector(".current--card"); const previousCardEl = cardsContainerEl.querySelector(".previous--card"); const nextCardEl = cardsContainerEl.querySelector(".next--card"); const currentBgImageEl = appBgContainerEl.querySelector(".current--image"); const previousBgImageEl = appBgContainerEl.querySelector(".previous--image"); const nextBgImageEl = appBgContainerEl.querySelector(".next--image"); changeInfo(direction); swapCardsClass(); removeCardEvents(currentCardEl); function swapCardsClass() { currentCardEl.classList.remove("current--card"); previousCardEl.classList.remove("previous--card"); nextCardEl.classList.remove("next--card"); currentBgImageEl.classList.remove("current--image"); previousBgImageEl.classList.remove("previous--image"); nextBgImageEl.classList.remove("next--image"); currentCardEl.style.zIndex = "50"; currentBgImageEl.style.zIndex = "-2"; if (direction === "right") { previousCardEl.style.zIndex = "20"; nextCardEl.style.zIndex = "30"; nextBgImageEl.style.zIndex = "-1"; currentCardEl.classList.add("previous--card"); previousCardEl.classList.add("next--card"); nextCardEl.classList.add("current--card"); currentBgImageEl.classList.add("previous--image"); previousBgImageEl.classList.add("next--image"); nextBgImageEl.classList.add("current--image"); } else if (direction === "left") { previousCardEl.style.zIndex = "30"; nextCardEl.style.zIndex = "20"; previousBgImageEl.style.zIndex = "-1"; currentCardEl.classList.add("next--card"); previousCardEl.classList.add("current--card"); nextCardEl.classList.add("previous--card"); currentBgImageEl.classList.add("next--image"); previousBgImageEl.classList.add("current--image"); nextBgImageEl.classList.add("previous--image"); } } } function changeInfo(direction) { let currentInfoEl = cardInfosContainerEl.querySelector(".current--info"); let previousInfoEl = cardInfosContainerEl.querySelector(".previous--info"); let nextInfoEl = cardInfosContainerEl.querySelector(".next--info"); gsap.timeline() .to([buttons.prev, buttons.next], { duration: 0.2, opacity: 0.5, pointerEvents: "none", }) .to( currentInfoEl.querySelectorAll(".text"), { duration: 0.4, stagger: 0.1, translateY: "-120px", opacity: 0, }, "-=" ) .call(() => { swapInfosClass(direction); }) .call(() => initCardEvents()) .fromTo( direction === "right" ? nextInfoEl.querySelectorAll(".text") : previousInfoEl.querySelectorAll(".text"), { opacity: 0, translateY: "40px", }, { duration: 0.4, stagger: 0.1, translateY: "0px", opacity: 1, } ) .to([buttons.prev, buttons.next], { duration: 0.2, opacity: 1, pointerEvents: "all", }); function swapInfosClass() { currentInfoEl.classList.remove("current--info"); previousInfoEl.classList.remove("previous--info"); nextInfoEl.classList.remove("next--info"); if (direction === "right") { currentInfoEl.classList.add("previous--info"); nextInfoEl.classList.add("current--info"); previousInfoEl.classList.add("next--info"); } else if (direction === "left") { currentInfoEl.classList.add("next--info"); nextInfoEl.classList.add("previous--info"); previousInfoEl.classList.add("current--info"); } } } function updateCard(e) { const card = e.currentTarget; const box = card.getBoundingClientRect(); const centerPosition = { x: box.left + box.width / 2, y: box.top + box.height / 2, }; let angle = Math.atan2(e.pageX - centerPosition.x, 0) * (35 / Math.PI); gsap.set(card, { "--current-card-rotation-offset": `${angle}deg`, }); const currentInfoEl = cardInfosContainerEl.querySelector(".current--info"); gsap.set(currentInfoEl, { rotateY: `${angle}deg`, }); } function resetCardTransforms(e) { const card = e.currentTarget; const currentInfoEl = cardInfosContainerEl.querySelector(".current--info"); gsap.set(card, { "--current-card-rotation-offset": 0, }); gsap.set(currentInfoEl, { rotateY: 0, }); } function initCardEvents() { const currentCardEl = cardsContainerEl.querySelector(".current--card"); currentCardEl.addEventListener("pointermove", updateCard); currentCardEl.addEventListener("pointerout", (e) => { resetCardTransforms(e); }); } initCardEvents(); function removeCardEvents(card) { card.removeEventListener("pointermove", updateCard); } function init() { let tl = gsap.timeline(); tl.to(cardsContainerEl.children, { delay: 0.15, duration: 0.5, stagger: { ease: "power4.inOut", from: "right", amount: 0.1, }, "--card-translateY-offset": "0%", }) .to(cardInfosContainerEl.querySelector(".current--info").querySelectorAll(".text"), { delay: 0.5, duration: 0.4, stagger: 0.1, opacity: 1, translateY: 0, }) .to( [buttons.prev, buttons.next], { duration: 0.4, opacity: 1, pointerEvents: "all", }, "-=0.4" ); } const waitForImages = () => { const images = [...document.querySelectorAll("img")]; const totalImages = images.length; let loadedImages = 0; const loaderEl = document.querySelector(".loader span"); gsap.set(cardsContainerEl.children, { "--card-translateY-offset": "100vh", }); gsap.set(cardInfosContainerEl.querySelector(".current--info").querySelectorAll(".text"), { translateY: "40px", opacity: 0, }); gsap.set([buttons.prev, buttons.next], { pointerEvents: "none", opacity: "0", }); images.forEach((image) => { imagesLoaded(image, (instance) => { if (instance.isComplete) { loadedImages++; let loadProgress = loadedImages / totalImages; gsap.to(loaderEl, { duration: 1, scaleX: loadProgress, backgroundColor: `hsl(${loadProgress * 120}, 100%, 50%`, }); if (totalImages == loadedImages) { gsap.timeline() .to(".loading__wrapper", { duration: 0.8, opacity: 0, pointerEvents: "none", }) .call(() => init()); } } }); }); }; waitForImages();