| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8" /> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
| | <title>XQ Thối</title> |
| | <style> |
| | html, body { |
| | margin: 0; |
| | padding: 0; |
| | overflow: hidden; |
| | height: 100vh; |
| | width: 100vw; |
| | font-family: 'Arial Rounded MT Bold', sans-serif; |
| | position: relative; |
| | } |
| | |
| | |
| | #body-video-bg { |
| | position: fixed; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | object-fit: cover; |
| | z-index: -2; |
| | will-change: transform; |
| | transform: translateZ(0); |
| | backface-visibility: hidden; |
| | } |
| | |
| | |
| | body::before { |
| | content: ''; |
| | position: fixed; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background: linear-gradient(to bottom right, #000000, #222222); |
| | z-index: -3; |
| | } |
| | |
| | |
| | .container { |
| | position: absolute; |
| | border: 5px solid #8B4513; |
| | padding: 40px; |
| | border-radius: 20px; |
| | background-color: rgba(255, 255, 255, 0.3); |
| | box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); |
| | overflow: hidden; |
| | cursor: grab; |
| | user-select: none; |
| | } |
| | |
| | .container:active { |
| | cursor: grabbing; |
| | } |
| | |
| | |
| | .mini-box { |
| | position: absolute; |
| | border: 3px solid #8B4513; |
| | padding: 15px; |
| | border-radius: 12px; |
| | background-color: rgba(255, 255, 255, 0.25); |
| | box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); |
| | overflow: hidden; |
| | cursor: grab; |
| | user-select: none; |
| | width: 120px; |
| | height: 120px; |
| | } |
| | |
| | .mini-box:active { |
| | cursor: grabbing; |
| | } |
| | |
| | |
| | .video-background { |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | object-fit: cover; |
| | z-index: -1; |
| | border-radius: 15px; |
| | will-change: transform; |
| | transform: translateZ(0); |
| | backface-visibility: hidden; |
| | } |
| | |
| | h1 { |
| | font-size: 6rem; |
| | color: #000000; |
| | text-shadow: 0 0 25px #d2b48c, 0 0 40px #8B4513; |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | gap: 20px; |
| | margin: 0; |
| | position: relative; |
| | z-index: 1; |
| | } |
| | |
| | .letter { |
| | display: inline-block; |
| | animation: floaty 3s ease-in-out infinite; |
| | } |
| | |
| | @keyframes floaty { |
| | 0% { transform: translateY(0) rotate(0deg); } |
| | 25% { transform: translateY(-8px) rotate(1deg); } |
| | 50% { transform: translateY(0) rotate(0deg); } |
| | 75% { transform: translateY(8px) rotate(-1deg); } |
| | 100% { transform: translateY(0) rotate(0deg); } |
| | } |
| | |
| | .emoji { |
| | position: absolute; |
| | font-size: 26px; |
| | animation: pop 1s ease-out forwards; |
| | color: #8B4513; |
| | pointer-events: none; |
| | } |
| | |
| | @keyframes pop { |
| | 0% { transform: scale(1); opacity: 1; } |
| | 100% { transform: scale(2); opacity: 0; top: -40px; } |
| | } |
| | |
| | .spider { |
| | position: absolute; |
| | font-size: 40px; |
| | pointer-events: none; |
| | transition: transform 0.1s linear; |
| | } |
| | |
| | .baby-spider { |
| | position: absolute; |
| | font-size: 20px; |
| | pointer-events: none; |
| | transition: transform 0.1s linear; |
| | } |
| | |
| | |
| | .border-frame { |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | width: 100vw; |
| | height: 100vh; |
| | background-image: url('https://www.transparenttextures.com/patterns/circuit-board.png'); |
| | background-size: cover; |
| | z-index: -1; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| |
|
| | |
| | <video id="body-video-bg" autoplay loop muted playsinline preload="auto" loading="eager"> |
| | <source src="Background.mp4" type="video/mp4"> |
| | </video> |
| |
|
| | |
| | <div class="border-frame"></div> |
| |
|
| | |
| | <div class="container"> |
| | <video class="video-background" autoplay loop muted playsinline preload="auto" loading="eager"> |
| | <source src="video.mp4" type="video/mp4"> |
| | </video> |
| | <h1> |
| | <span class="letter">emem</span> |
| | <span class="letter">XQ</span> |
| | <span class="letter">💗</span> |
| | </h1> |
| | </div> |
| |
|
| | <div id="spider" class="spider">💗</div> |
| | <audio id="bg-music" src="music.mp3" preload="auto"></audio> |
| |
|
| | <script> |
| | const spider = document.getElementById('spider'); |
| | const music = document.getElementById('bg-music'); |
| | const container = document.querySelector('.container'); |
| | let mouseX = window.innerWidth / 2; |
| | let mouseY = window.innerHeight / 2; |
| | let spiderX = mouseX, spiderY = mouseY; |
| | |
| | |
| | const boxes = []; |
| | |
| | |
| | const mainBox = { |
| | element: container, |
| | x: window.innerWidth / 2, |
| | y: window.innerHeight / 2, |
| | velX: 0, |
| | velY: 0, |
| | isDragging: false, |
| | dragOffsetX: 0, |
| | dragOffsetY: 0 |
| | }; |
| | boxes.push(mainBox); |
| | |
| | |
| | const miniBoxCount = 8; |
| | for (let i = 0; i < miniBoxCount; i++) { |
| | const miniBox = document.createElement('div'); |
| | miniBox.className = 'mini-box'; |
| | |
| | |
| | const videoSrc = Math.random() > 0.5 ? 'video.mp4' : 'Background.mp4'; |
| | |
| | miniBox.innerHTML = ` |
| | <video class="video-background" autoplay loop muted playsinline preload="auto" loading="eager"> |
| | <source src="${videoSrc}" type="video/mp4"> |
| | </video> |
| | `; |
| | |
| | document.body.appendChild(miniBox); |
| | |
| | |
| | const boxData = { |
| | element: miniBox, |
| | x: Math.random() * (window.innerWidth - 150) + 75, |
| | y: Math.random() * (window.innerHeight - 150) + 75, |
| | velX: (Math.random() - 0.5) * 5, |
| | velY: (Math.random() - 0.5) * 5, |
| | isDragging: false, |
| | dragOffsetX: 0, |
| | dragOffsetY: 0 |
| | }; |
| | |
| | boxes.push(boxData); |
| | |
| | |
| | miniBox.addEventListener('mousedown', (e) => startDrag(e, boxData)); |
| | miniBox.addEventListener('touchstart', (e) => startDrag(e, boxData)); |
| | } |
| | |
| | const friction = 0.95; |
| | const elasticity = 0.7; |
| | const wallMargin = 50; |
| | |
| | |
| | mainBox.element.style.left = mainBox.x + 'px'; |
| | mainBox.element.style.top = mainBox.y + 'px'; |
| | mainBox.element.style.transform = 'translate(-50%, -50%)'; |
| | |
| | |
| | container.addEventListener('mousedown', (e) => startDrag(e, mainBox)); |
| | container.addEventListener('touchstart', (e) => startDrag(e, mainBox)); |
| | |
| | function startDrag(e, box) { |
| | box.isDragging = true; |
| | const touch = e.type === 'touchstart' ? e.touches[0] : e; |
| | box.dragOffsetX = touch.clientX - box.x; |
| | box.dragOffsetY = touch.clientY - box.y; |
| | box.velX = 0; |
| | box.velY = 0; |
| | e.preventDefault(); |
| | } |
| | |
| | document.addEventListener('mousemove', drag); |
| | document.addEventListener('touchmove', drag); |
| | |
| | function drag(e) { |
| | boxes.forEach(box => { |
| | if (box.isDragging) { |
| | const touch = e.type === 'touchmove' ? e.touches[0] : e; |
| | const newX = touch.clientX - box.dragOffsetX; |
| | const newY = touch.clientY - box.dragOffsetY; |
| | |
| | box.velX = (newX - box.x) * 0.5; |
| | box.velY = (newY - box.y) * 0.5; |
| | |
| | box.x = newX; |
| | box.y = newY; |
| | } |
| | }); |
| | } |
| | |
| | document.addEventListener('mouseup', stopDrag); |
| | document.addEventListener('touchend', stopDrag); |
| | |
| | function stopDrag() { |
| | boxes.forEach(box => { |
| | box.isDragging = false; |
| | }); |
| | } |
| | |
| | |
| | function updateBoxPhysics() { |
| | boxes.forEach(box => { |
| | if (!box.isDragging) { |
| | |
| | box.velX *= friction; |
| | box.velY *= friction; |
| | |
| | |
| | box.x += box.velX; |
| | box.y += box.velY; |
| | |
| | |
| | const rect = box.element.getBoundingClientRect(); |
| | const halfWidth = rect.width / 2; |
| | const halfHeight = rect.height / 2; |
| | |
| | |
| | if (box.x - halfWidth < wallMargin) { |
| | box.x = wallMargin + halfWidth; |
| | box.velX = Math.abs(box.velX) * elasticity; |
| | } |
| | |
| | if (box.x + halfWidth > window.innerWidth - wallMargin) { |
| | box.x = window.innerWidth - wallMargin - halfWidth; |
| | box.velX = -Math.abs(box.velX) * elasticity; |
| | } |
| | |
| | if (box.y - halfHeight < wallMargin) { |
| | box.y = wallMargin + halfHeight; |
| | box.velY = Math.abs(box.velY) * elasticity; |
| | } |
| | |
| | if (box.y + halfHeight > window.innerHeight - wallMargin) { |
| | box.y = window.innerHeight - wallMargin - halfHeight; |
| | box.velY = -Math.abs(box.velY) * elasticity; |
| | } |
| | } |
| | |
| | |
| | if (box.element === container) { |
| | box.element.style.left = box.x + 'px'; |
| | box.element.style.top = box.y + 'px'; |
| | } else { |
| | box.element.style.left = (box.x - 60) + 'px'; |
| | box.element.style.top = (box.y - 60) + 'px'; |
| | } |
| | }); |
| | |
| | requestAnimationFrame(updateBoxPhysics); |
| | } |
| | |
| | updateBoxPhysics(); |
| | |
| | |
| | const babySpiders = [ |
| | { x: 0, y: 0, speed: 0.02 }, |
| | { x: window.innerWidth, y: 0, speed: 0.03 }, |
| | { x: 0, y: window.innerHeight, speed: 0.04 }, |
| | { x: window.innerWidth, y: window.innerHeight, speed: 0.05 } |
| | ]; |
| | |
| | |
| | babySpiders.forEach(spiderData => { |
| | const babySpider = document.createElement('div'); |
| | babySpider.className = 'baby-spider'; |
| | babySpider.innerText = '💗'; |
| | document.body.appendChild(babySpider); |
| | moveSpiderToCursor(babySpider, spiderData.x, spiderData.y, spiderData.speed); |
| | }); |
| | |
| | |
| | function moveSpiderToCursor(spiderElement, x, y, speed) { |
| | let targetX = x; |
| | let targetY = y; |
| | function move() { |
| | const dx = mouseX - targetX; |
| | const dy = mouseY - targetY; |
| | targetX += dx * speed; |
| | targetY += dy * speed; |
| | spiderElement.style.transform = `translate(${targetX}px, ${targetY}px)`; |
| | requestAnimationFrame(move); |
| | } |
| | move(); |
| | } |
| | |
| | |
| | function getRandomEmoji() { |
| | const emojis = ['💗', '💗', '♥']; |
| | return emojis[Math.floor(Math.random() * emojis.length)]; |
| | } |
| | |
| | |
| | function createEmoji(x, y) { |
| | const emoji = document.createElement('div'); |
| | emoji.className = 'emoji'; |
| | emoji.innerText = getRandomEmoji(); |
| | emoji.style.left = `${x}px`; |
| | emoji.style.top = `${y}px`; |
| | document.body.appendChild(emoji); |
| | setTimeout(() => emoji.remove(), 1000); |
| | } |
| | |
| | document.addEventListener('mousemove', (e) => { |
| | mouseX = e.clientX; |
| | mouseY = e.clientY; |
| | |
| | |
| | const anyDragging = boxes.some(box => box.isDragging); |
| | if (!anyDragging) { |
| | createEmoji(e.clientX, e.clientY); |
| | } |
| | }); |
| | |
| | document.addEventListener('click', (e) => { |
| | createEmoji(e.clientX, e.clientY); |
| | spiderX = e.clientX; |
| | spiderY = e.clientY; |
| | }); |
| | |
| | document.addEventListener('touchmove', (e) => { |
| | const touch = e.touches[0]; |
| | mouseX = touch.clientX; |
| | mouseY = touch.clientY; |
| | createEmoji(mouseX, mouseY); |
| | }); |
| | |
| | document.addEventListener('touchstart', (e) => { |
| | const touch = e.touches[0]; |
| | createEmoji(touch.clientX, touch.clientY); |
| | spiderX = touch.clientX; |
| | spiderY = touch.clientY; |
| | }); |
| | |
| | |
| | let musicStarted = false; |
| | function startMusic() { |
| | if (!musicStarted) { |
| | music.loop = true; |
| | music.play().catch(e => console.log('Music autoplay blocked:', e)); |
| | musicStarted = true; |
| | } |
| | } |
| | |
| | document.addEventListener('click', startMusic, { once: true }); |
| | document.addEventListener('touchstart', startMusic, { once: true }); |
| | document.addEventListener('keydown', startMusic, { once: true }); |
| | |
| | |
| | function moveSpider() { |
| | const dx = mouseX - spiderX; |
| | const dy = mouseY - spiderY; |
| | spiderX += dx * 0.2; |
| | spiderY += dy * 0.2; |
| | spider.style.transform = `translate(${spiderX}px, ${spiderY}px)`; |
| | |
| | if (Math.abs(dx) < 1 && Math.abs(dy) < 1) { |
| | spiderX = window.innerWidth / 2; |
| | spiderY = window.innerHeight / 2; |
| | } |
| | |
| | requestAnimationFrame(moveSpider); |
| | } |
| | |
| | moveSpider(); |
| | </script> |
| | </body> |
| | </html> |