// ========= Customization ========= // Add any GIFs you want to show in order. const GIF_URLS = [ "https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExM3hlZGx4dmdubzgyeG5sN3l4ZHVpY3htcW9pZHY0Yzhoc3JzaDczayZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/IVK6xNBpEAHYyOdghk/giphy.gif", "https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExMWI4N3JuZDY2ZHlzdGF4eG9zbmV6c215em1ndHFzYnY0Mm01YWliaSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/5NzRr5INmnXiG9xrFy/giphy.gif", "https://media4.giphy.com/media/v1.Y2lkPTc5MGI3NjExcTQyM2RqZnpwa2NwaDNnZzJzZnpxN25kMThpajl1cjYwODh0dHJ5dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/964WRtxBbYHzln3uN5/giphy.gif", // FLowers in hand "https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExYnhlczQ2OW4zcGE1NGFwdjJmdDk0bHpwejg0cjdtYmN6OW4wOTJpdyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/lfa5Bq6Am8Pf3a5AWU/giphy.gif", // YOu know I love you "https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExM3dram11MHdpMjM1dWF5dnlkZ3E0bjQyOHc5cHZ0dXNhdG1rNmF6dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/9KI8vyCCAOqHbcMYd2/giphy.gif", // Mexican "https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExeDN5c3BxcDA4OW83Y2RzNmcxcms1cGwxdnpjaXF3aGpxZ2U2aWtmcSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/zEtQ9FrbqIt3BVKY3i/giphy.gif" ]; // ================================= const gifImg = document.getElementById("gifImg"); gifImg.src = GIF_URLS[0]; const yesBtn = document.getElementById("yesBtn"); const noBtn = document.getElementById("noBtn"); const subtitle = document.getElementById("subtitle"); const btnRow = document.getElementById("btnRow"); const askScreen = document.getElementById("askScreen"); const successScreen = document.getElementById("successScreen"); // 5 stages of "No" presses const noStages = [ "I think you accidentally pressed no", "Are you sure?", "Really sure?", "Please? Pretty please?", "Last chance... you sure you want to say no?", ]; let noCount = 0; function clamp(v, min, max) { return Math.max(min, Math.min(max, v)); } function setButtonWidths() { // Adjust width instead of transform scaling to avoid jumpy layout on phones. const stage = Math.min(noCount, 4); const rowWidth = btnRow.clientWidth || window.innerWidth * 0.9; const rowStyle = getComputedStyle(btnRow); const gap = Number.parseFloat(rowStyle.columnGap || rowStyle.gap || "0") || 0; const perButtonMax = Math.max(120, (rowWidth - gap) / 2); const baseWidth = Math.min(172, perButtonMax * 0.92); const yesWidth = clamp(baseWidth + stage * 18, 110, perButtonMax); const noWidth = clamp(baseWidth - stage * 14, 84, perButtonMax); document.documentElement.style.setProperty("--yes-width", `${Math.round(yesWidth)}px`); document.documentElement.style.setProperty("--no-width", `${Math.round(noWidth)}px`); } function showSuccess() { askScreen.style.display = "none"; successScreen.style.display = "block"; } yesBtn.addEventListener("click", () => showSuccess()); function advanceNoStage() { if (noCount < 5) noCount++; // Update the GIF based on the current "no" count gifImg.src = GIF_URLS[noCount]; console.log(noCount); const stageIndex = Math.min(noCount - 1, 4); subtitle.textContent = noStages[stageIndex] || ""; setButtonWidths(); // Enter final stage after 5th "no" attempt if (noCount >= 5) enableNoEvadeMode(); } noBtn.addEventListener("click", () => { if (noCount < 5) { advanceNoStage(); } else { // In evade mode, clicking should be basically impossible, but just in case: teleportNoButton(); } }); // Final stage: make it impossible to click No by moving away on hover and pointerdown let evadeEnabled = false; function getViewportBounds() { const vv = window.visualViewport; if (vv) { return { left: vv.offsetLeft, top: vv.offsetTop, width: vv.width, height: vv.height }; } return { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight }; } function enableNoEvadeMode() { if (evadeEnabled) return; evadeEnabled = true; noBtn.textContent = "No"; subtitle.textContent = noStages[4]; // Make the No button position fixed so it can teleport around the viewport noBtn.style.position = "fixed"; noBtn.style.left = ""; noBtn.style.top = ""; // Start it near the bottom center const bounds = getViewportBounds(); const startX = Math.round(bounds.left + bounds.width * 0.5); const startY = Math.round(bounds.top + bounds.height * 0.72); placeNoButton(startX, startY); // Evade on hover and on press noBtn.addEventListener("pointerenter", teleportNoButton, { passive: true }); noBtn.addEventListener("pointerdown", teleportNoButton, { passive: true }); // If the viewport changes, keep it clamped inside the visible phone screen. window.addEventListener("resize", keepNoButtonOnScreen, { passive: true }); if (window.visualViewport) { window.visualViewport.addEventListener("resize", keepNoButtonOnScreen, { passive: true }); window.visualViewport.addEventListener("scroll", keepNoButtonOnScreen, { passive: true }); } } function placeNoButton(centerX, centerY) { const rect = noBtn.getBoundingClientRect(); const pad = 12; const bounds = getViewportBounds(); const minX = bounds.left + pad; const minY = bounds.top + pad; const maxX = Math.max(minX, bounds.left + bounds.width - rect.width - pad); const maxY = Math.max(minY, bounds.top + bounds.height - rect.height - pad); const x = clamp(centerX - rect.width / 2, minX, maxX); const y = clamp(centerY - rect.height / 2, minY, maxY); noBtn.style.left = Math.round(x) + "px"; noBtn.style.top = Math.round(y) + "px"; } function keepNoButtonOnScreen() { if (!evadeEnabled) return; const rect = noBtn.getBoundingClientRect(); placeNoButton(rect.left + rect.width / 2, rect.top + rect.height / 2); } function teleportNoButton() { if (!evadeEnabled) return; // "Far away" point each time: jump to a different viewport region const bounds = getViewportBounds(); const left = bounds.left; const top = bounds.top; const w = bounds.width; const h = bounds.height; const candidates = [ { x: left + w * 0.16, y: top + h * 0.22 }, { x: left + w * 0.84, y: top + h * 0.25 }, { x: left + w * 0.18, y: top + h * 0.78 }, { x: left + w * 0.82, y: top + h * 0.78 }, { x: left + w * 0.5, y: top + h * 0.18 } ]; const pick = candidates[Math.floor(Math.random() * candidates.length)]; placeNoButton(pick.x, pick.y); } // Start with neutral stage setButtonWidths(); window.addEventListener("resize", setButtonWidths, { passive: true }); // ========= Sakura falling leaves (canvas) ========= const canvas = document.getElementById("sakura"); const ctx = canvas.getContext("2d"); function resizeCanvas() { const dpr = Math.max(1, Math.min(2, window.devicePixelRatio || 1)); canvas.width = Math.floor(window.innerWidth * dpr); canvas.height = Math.floor(window.innerHeight * dpr); canvas.style.width = window.innerWidth + "px"; canvas.style.height = window.innerHeight + "px"; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } resizeCanvas(); window.addEventListener("resize", resizeCanvas, { passive: true }); const petals = []; const PETAL_COUNT = Math.round(Math.min(90, Math.max(40, window.innerWidth / 10))); function rand(min, max) { return min + Math.random() * (max - min); } function makePetal() { const size = rand(6, 14); return { x: rand(0, window.innerWidth), y: rand(-window.innerHeight, 0), vx: rand(-0.35, 0.55), vy: rand(0.8, 1.8), rot: rand(0, Math.PI * 2), vr: rand(-0.02, 0.02), wobble: rand(0.8, 1.8), size, hue: rand(330, 350), // pink range alpha: rand(0.55, 0.9) }; } for (let i = 0; i < PETAL_COUNT; i++) petals.push(makePetal()); function drawPetal(p) { ctx.save(); ctx.translate(p.x, p.y); ctx.rotate(p.rot); // Sakura-like petal shape const s = p.size; ctx.beginPath(); ctx.moveTo(0, -s * 0.9); ctx.bezierCurveTo(s * 0.9, -s * 0.9, s * 0.95, s * 0.2, 0, s); ctx.bezierCurveTo(-s * 0.95, s * 0.2, -s * 0.9, -s * 0.9, 0, -s * 0.9); ctx.closePath(); ctx.fillStyle = `hsla(${p.hue}, 90%, 82%, ${p.alpha})`; ctx.fill(); // soft highlight ctx.globalAlpha = p.alpha * 0.45; ctx.strokeStyle = "rgba(255,255,255,0.55)"; ctx.lineWidth = 1; ctx.stroke(); ctx.restore(); } let lastT = performance.now(); function tick(t) { const dt = Math.min(33, t - lastT); lastT = t; ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); for (const p of petals) { p.x += p.vx * dt * 0.06 + Math.sin((p.y / 40) * p.wobble) * 0.2; p.y += p.vy * dt * 0.06; p.rot += p.vr * dt; if (p.y > window.innerHeight + 30 || p.x < -40 || p.x > window.innerWidth + 40) { // respawn from top p.x = rand(0, window.innerWidth); p.y = rand(-120, -20); } drawPetal(p); } requestAnimationFrame(tick); } requestAnimationFrame(tick);