Spaces:
Running
Running
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>心动连接 - 相遇从这里开始</title> | |
| <!-- 引入 FontAwesome 图标库 --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| --primary-gradient: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%); | |
| --secondary-gradient: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%); | |
| --bg-color: #f3f4f6; | |
| --text-dark: #1f2937; | |
| --text-gray: #6b7280; | |
| --white: #ffffff; | |
| --shadow-sm: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| --glass-bg: rgba(255, 255, 255, 0.85); | |
| --glass-border: rgba(255, 255, 255, 0.3); | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| -webkit-tap-highlight-color: transparent; | |
| } | |
| body { | |
| font-family: 'PingFang SC', 'Microsoft YaHei', -apple-system, BlinkMacSystemFont, sans-serif; | |
| background-color: #e5e7eb; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| overflow: hidden; /* 防止背景滚动 */ | |
| } | |
| /* 手机容器模拟 */ | |
| .app-container { | |
| width: 100%; | |
| max-width: 414px; /* iPhone Max 宽度 */ | |
| height: 100vh; | |
| max-height: 896px; | |
| background-color: var(--white); | |
| position: relative; | |
| overflow: hidden; | |
| display: flex; | |
| flex-direction: column; | |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); | |
| } | |
| @media (min-width: 450px) { | |
| .app-container { | |
| height: 90vh; | |
| border-radius: 40px; | |
| border: 8px solid #fff; | |
| } | |
| } | |
| /* 顶部 Header */ | |
| header { | |
| padding: 15px 20px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| z-index: 10; | |
| background: rgba(255,255,255,0.9); | |
| backdrop-filter: blur(10px); | |
| } | |
| .logo { | |
| font-size: 1.5rem; | |
| font-weight: 800; | |
| background: var(--primary-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .header-actions button { | |
| background: none; | |
| border: none; | |
| font-size: 1.2rem; | |
| color: var(--text-gray); | |
| margin-left: 15px; | |
| cursor: pointer; | |
| transition: transform 0.2s; | |
| } | |
| .header-actions button:active { | |
| transform: scale(0.9); | |
| } | |
| /* 主内容区域 - 卡片堆叠 */ | |
| main { | |
| flex: 1; | |
| position: relative; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| padding: 10px; | |
| overflow: hidden; | |
| } | |
| .card-stack { | |
| position: relative; | |
| width: 100%; | |
| height: 100%; | |
| max-height: 600px; | |
| } | |
| .card { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| border-radius: 24px; | |
| background-size: cover; | |
| background-position: center; | |
| box-shadow: var(--shadow-lg); | |
| overflow: hidden; | |
| transform-origin: 50% 100%; | |
| transition: transform 0.1s linear; /* 拖拽时不需要过渡,JS控制,松开时加上 */ | |
| cursor: grab; | |
| user-select: none; | |
| } | |
| .card:active { | |
| cursor: grabbing; | |
| } | |
| /* 卡片上的渐变遮罩,保证文字清晰 */ | |
| .card-content { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| width: 100%; | |
| padding: 80px 20px 20px; | |
| background: linear-gradient(to top, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0) 60%); | |
| color: white; | |
| pointer-events: none; | |
| } | |
| .card-name { | |
| font-size: 2rem; | |
| font-weight: 700; | |
| margin-bottom: 5px; | |
| display: flex; | |
| align-items: baseline; | |
| gap: 10px; | |
| } | |
| .card-age { | |
| font-size: 1.2rem; | |
| font-weight: 400; | |
| background: rgba(255,255,255,0.2); | |
| padding: 2px 8px; | |
| border-radius: 12px; | |
| } | |
| .card-job { | |
| font-size: 1rem; | |
| opacity: 0.9; | |
| margin-bottom: 8px; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .card-bio { | |
| font-size: 0.9rem; | |
| opacity: 0.8; | |
| line-height: 1.4; | |
| display: -webkit-box; | |
| -webkit-line-clamp: 2; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| } | |
| /* 状态标签 (喜欢/不喜欢) */ | |
| .status-badge { | |
| position: absolute; | |
| top: 40px; | |
| padding: 5px 15px; | |
| border: 4px solid; | |
| border-radius: 8px; | |
| font-size: 2rem; | |
| font-weight: 800; | |
| text-transform: uppercase; | |
| letter-spacing: 2px; | |
| opacity: 0; | |
| transform: rotate(-15deg); | |
| z-index: 10; | |
| } | |
| .status-like { | |
| left: 40px; | |
| color: #4ade80; | |
| border-color: #4ade80; | |
| transform: rotate(-15deg); | |
| } | |
| .status-nope { | |
| right: 40px; | |
| color: #f87171; | |
| border-color: #f87171; | |
| transform: rotate(15deg); | |
| } | |
| /* 底部操作按钮区 */ | |
| .action-area { | |
| height: 100px; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 20px; | |
| padding-bottom: 10px; | |
| } | |
| .action-btn { | |
| width: 60px; | |
| height: 60px; | |
| border-radius: 50%; | |
| background: white; | |
| border: none; | |
| box-shadow: var(--shadow-sm); | |
| font-size: 1.5rem; | |
| cursor: pointer; | |
| transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .action-btn:hover { | |
| transform: scale(1.1); | |
| } | |
| .action-btn:active { | |
| transform: scale(0.9); | |
| } | |
| .btn-nope { color: #f87171; } | |
| .btn-super { color: #3b82f6; width: 50px; height: 50px; font-size: 1.2rem; } | |
| .btn-like { color: #4ade80; } | |
| .btn-info { color: #fbbf24; width: 50px; height: 50px; font-size: 1.2rem; } | |
| /* 底部导航栏 */ | |
| .bottom-nav { | |
| height: 70px; | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(12px); | |
| border-top: 1px solid var(--glass-border); | |
| display: flex; | |
| justify-content: space-around; | |
| align-items: center; | |
| padding-bottom: 10px; /* 适配 iPhone X 底部 */ | |
| } | |
| .nav-item { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--text-gray); | |
| text-decoration: none; | |
| font-size: 0.75rem; | |
| gap: 4px; | |
| transition: color 0.3s; | |
| cursor: pointer; | |
| } | |
| .nav-item i { | |
| font-size: 1.5rem; | |
| transition: transform 0.2s; | |
| } | |
| .nav-item.active { | |
| color: #ff6b6b; | |
| } | |
| .nav-item.active i { | |
| transform: translateY(-2px); | |
| } | |
| /* 模态框 (匹配成功 & 详情) */ | |
| .modal-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.6); | |
| backdrop-filter: blur(5px); | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 100; | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: opacity 0.3s ease; | |
| } | |
| .modal-overlay.active { | |
| opacity: 1; | |
| pointer-events: auto; | |
| } | |
| .match-modal { | |
| background: white; | |
| width: 85%; | |
| border-radius: 30px; | |
| padding: 30px; | |
| text-align: center; | |
| transform: scale(0.8); | |
| transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); | |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); | |
| } | |
| .modal-overlay.active .match-modal { | |
| transform: scale(1); | |
| } | |
| .match-title { | |
| font-family: 'Brush Script MT', cursive; | |
| font-size: 3rem; | |
| color: #4ade80; | |
| margin-bottom: 20px; | |
| transform: rotate(-5deg); | |
| } | |
| .match-avatars { | |
| display: flex; | |
| justify-content: center; | |
| gap: 20px; | |
| margin-bottom: 30px; | |
| } | |
| .avatar { | |
| width: 80px; | |
| height: 80px; | |
| border-radius: 50%; | |
| border: 4px solid white; | |
| box-shadow: var(--shadow-sm); | |
| object-fit: cover; | |
| } | |
| .match-btn { | |
| width: 100%; | |
| padding: 15px; | |
| border-radius: 30px; | |
| border: none; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| margin-bottom: 10px; | |
| cursor: pointer; | |
| transition: opacity 0.2s; | |
| } | |
| .btn-primary { | |
| background: var(--primary-gradient); | |
| color: white; | |
| } | |
| .btn-secondary { | |
| background: #f3f4f6; | |
| color: var(--text-dark); | |
| } | |
| /* 详情模态框样式 */ | |
| .detail-modal { | |
| background: white; | |
| width: 90%; | |
| max-height: 80%; | |
| border-radius: 20px; | |
| overflow-y: auto; | |
| position: relative; | |
| } | |
| .detail-header-img { | |
| width: 100%; | |
| height: 250px; | |
| object-fit: cover; | |
| } | |
| .detail-content { | |
| padding: 20px; | |
| } | |
| .tag-container { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| margin: 15px 0; | |
| } | |
| .tag { | |
| background: #f3f4f6; | |
| padding: 5px 12px; | |
| border-radius: 15px; | |
| font-size: 0.85rem; | |
| color: var(--text-gray); | |
| } | |
| .close-modal-btn { | |
| position: absolute; | |
| top: 15px; | |
| right: 15px; | |
| background: rgba(0,0,0,0.5); | |
| color: white; | |
| border: none; | |
| width: 32px; | |
| height: 32px; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| z-index: 2; | |
| } | |
| /* Built with anycoder link */ | |
| .anycoder-link { | |
| font-size: 0.7rem; | |
| color: #9ca3af; | |
| text-decoration: none; | |
| margin-top: 5px; | |
| display: block; | |
| text-align: center; | |
| } | |
| .anycoder-link:hover { | |
| color: #ff6b6b; | |
| } | |
| /* 空状态 */ | |
| .empty-state { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| text-align: center; | |
| color: var(--text-gray); | |
| width: 80%; | |
| display: none; | |
| } | |
| .empty-state i { | |
| font-size: 3rem; | |
| margin-bottom: 15px; | |
| color: #d1d5db; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <!-- 头部 --> | |
| <header> | |
| <div class="logo"> | |
| <i class="fa-solid fa-heart"></i> 心动连接 | |
| </div> | |
| <div class="header-actions"> | |
| <button aria-label="筛选"><i class="fa-solid fa-sliders"></i></button> | |
| <button aria-label="聊天"><i class="fa-solid fa-comment-dots"></i></button> | |
| </div> | |
| </header> | |
| <!-- 主内容区:卡片 --> | |
| <main> | |
| <div class="empty-state" id="emptyState"> | |
| <i class="fa-regular fa-face-sad-tear"></i> | |
| <h3>暂时没有更多推荐</h3> | |
| <p>请稍后再试或调整筛选条件</p> | |
| <button class="match-btn btn-primary" style="margin-top: 20px; width: auto; padding: 10px 30px;" onclick="location.reload()">重新加载</button> | |
| </div> | |
| <div class="card-stack" id="cardStack"> | |
| <!-- 卡片将通过 JS 动态生成 --> | |
| </div> | |
| </main> | |
| <!-- 底部操作按钮 --> | |
| <div class="action-area"> | |
| <button class="action-btn btn-nope" id="btnNope" aria-label="跳过"> | |
| <i class="fa-solid fa-xmark"></i> | |
| </button> | |
| <button class="action-btn btn-info" id="btnInfo" aria-label="详情"> | |
| <i class="fa-solid fa-circle-info"></i> | |
| </button> | |
| <button class="action-btn btn-like" id="btnLike" aria-label="喜欢"> | |
| <i class="fa-solid fa-heart"></i> | |
| </button> | |
| </div> | |
| <!-- 底部导航 --> | |
| <nav class="bottom-nav"> | |
| <a href="#" class="nav-item active"> | |
| <i class="fa-solid fa-layer-group"></i> | |
| <span>推荐</span> | |
| </a> | |
| <a href="#" class="nav-item"> | |
| <i class="fa-solid fa-compass"></i> | |
| <span>发现</span> | |
| </a> | |
| <a href="#" class="nav-item"> | |
| <i class="fa-solid fa-heart-circle-bolt"></i> | |
| <span>匹配</span> | |
| </a> | |
| <a href="#" class="nav-item"> | |
| <i class="fa-regular fa-user"></i> | |
| <span>我的</span> | |
| </a> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link"> | |
| Built with anycoder | |
| </a> | |
| </nav> | |
| <!-- 匹配成功弹窗 --> | |
| <div class="modal-overlay" id="matchModal"> | |
| <div class="match-modal"> | |
| <div class="match-title">It's a Match!</div> | |
| <p style="color: #6b7280; margin-bottom: 20px;">你和 <span id="matchName">她</span> 互相喜欢了对方</p> | |
| <div class="match-avatars"> | |
| <img src="https://picsum.photos/seed/me/200/200" alt="My Avatar" class="avatar"> | |
| <img id="matchAvatar" src="" alt="Match Avatar" class="avatar"> | |
| </div> | |
| <button class="match-btn btn-primary" onclick="closeModal('matchModal')">发起聊天</button> | |
| <button class="match-btn btn-secondary" onclick="closeModal('matchModal')">继续浏览</button> | |
| </div> | |
| </div> | |
| <!-- 详情弹窗 --> | |
| <div class="modal-overlay" id="detailModal"> | |
| <div class="detail-modal"> | |
| <button class="close-modal-btn" onclick="closeModal('detailModal')"><i class="fa-solid fa-xmark"></i></button> | |
| <img id="detailImg" src="" class="detail-header-img" alt="Detail"> | |
| <div class="detail-content"> | |
| <div class="card-name" style="color: var(--text-dark); justify-content: flex-start;"> | |
| <span id="detailName">Name</span> | |
| <span id="detailAge" class="card-age" style="background: #ff6b6b; color: white;">Age</span> | |
| </div> | |
| <div class="card-job" style="color: var(--text-gray);"> | |
| <i class="fa-solid fa-briefcase"></i> <span id="detailJob">Job</span> | |
| </div> | |
| <div class="card-job" style="color: var(--text-gray);"> | |
| <i class="fa-solid fa-location-dot"></i> <span id="detailLoc">Location</span> | |
| </div> | |
| <h4 style="margin-top: 20px; margin-bottom: 10px;">关于我</h4> | |
| <p id="detailBio" style="color: #4b5563; line-height: 1.6;">Bio info...</p> | |
| <h4 style="margin-top: 20px; margin-bottom: 10px;">兴趣标签</h4> | |
| <div class="tag-container" id="detailTags"> | |
| <!-- Tags generated by JS --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // 模拟用户数据 | |
| const profiles = [ | |
| { | |
| id: 1, | |
| name: '林婉儿', | |
| age: 24, | |
| job: 'UI 设计师', | |
| location: '上海 · 徐汇', | |
| bio: '喜欢猫、咖啡和周末的探店。寻找一个有趣灵魂一起探索这座城市。', | |
| tags: ['摄影', '旅行', '猫奴', '极简主义'], | |
| img: 'https://picsum.photos/seed/lin/600/900' | |
| }, | |
| { | |
| id: 2, | |
| name: '苏晓', | |
| age: 26, | |
| job: '产品经理', | |
| location: '杭州 · 西湖', | |
| bio: '工作很忙,但生活不能只有工作。热爱户外运动,偶尔也会宅在家里看书。', | |
| tags: ['健身', '阅读', '烹饪', '电影'], | |
| img: 'https://picsum.photos/seed/su/600/900' | |
| }, | |
| { | |
| id: 3, | |
| name: '陈思思', | |
| age: 23, | |
| job: '插画师', | |
| location: '成都 · 高新', | |
| bio: '用画笔记录生活。希望你是一个有耐心、有幽默感的人。', | |
| tags: ['画画', '动漫', '甜食', '音乐节'], | |
| img: 'https://picsum.photos/seed/chen/600/900' | |
| }, | |
| { | |
| id: 4, | |
| name: '张雨薇', | |
| age: 25, | |
| job: '教师', | |
| location: '北京 · 朝阳', | |
| bio: '温柔是外衣,坚强是内核。喜欢逛博物馆和看展。', | |
| tags: ['历史', '展览', '瑜伽', '茶道'], | |
| img: 'https://picsum.photos/seed/zhang/600/900' | |
| }, | |
| { | |
| id: 5, | |
| name: '赵露', | |
| age: 27, | |
| job: '自由撰稿人', | |
| location: '深圳 · 南山', | |
| bio: '文字工作者,喜欢深夜的灵感和清晨的阳光。', | |
| tags: ['写作', '夜跑', '咖啡', '独立音乐'], | |
| img: 'https://picsum.photos/seed/zhao/600/900' | |
| } | |
| ]; | |
| let currentProfileIndex = profiles.length - 1; // 从最后一张开始渲染,利用堆叠顺序 | |
| const cardStack = document.getElementById('cardStack'); | |
| const emptyState = document.getElementById('emptyState'); | |
| // 初始化渲染 | |
| function initCards() { | |
| cardStack.innerHTML = ''; | |
| profiles.forEach((profile, index) => { | |
| const card = createCardElement(profile, index); | |
| cardStack.appendChild(card); | |
| }); | |
| updateZIndex(); | |
| attachDragEvents(); | |
| } | |
| // 创建卡片 DOM | |
| function createCardElement(profile, index) { | |
| const el = document.createElement('div'); | |
| el.className = 'card'; | |
| el.style.backgroundImage = `url(${profile.img})`; | |
| el.dataset.index = index; | |
| el.innerHTML = ` | |
| <div class="status-badge status-like">LIKE</div> | |
| <div class="status-badge status-nope">NOPE</div> | |
| <div class="card-content"> | |
| <div class="card-name">${profile.name} <span class="card-age">${profile.age}</span></div> | |
| <div class="card-job"><i class="fa-solid fa-briefcase"></i> ${profile.job}</div> | |
| <div class="card-bio">${profile.bio}</div> | |
| </div> | |
| `; | |
| return el; | |
| } | |
| // 更新堆叠顺序 | |
| function updateZIndex() { | |
| const cards = document.querySelectorAll('.card'); | |
| cards.forEach((card, index) => { | |
| // 确保当前的卡片在最上面 | |
| // index 0 是数组第一个(最底下),index length-1 是最上面 | |
| // 但为了拖拽逻辑,我们实际上是在操作 DOM 顺序或 transform | |
| // 这里我们用 z-index 确保视觉正确 | |
| card.style.zIndex = index; | |
| // 微微缩放后面的卡片,制造层次感 | |
| const offset = (cards.length - 1 - index) * 10; | |
| if (index < cards.length - 1) { | |
| card.style.transform = `scale(${1 - (cards.length - 1 - index) * 0.05}) translateY(${offset}px)`; | |
| card.style.opacity = 1 - (cards.length - 1 - index) * 0.2; | |
| card.style.pointerEvents = 'none'; // 只有最上面的卡片可以交互 | |
| } else { | |
| card.style.transform = 'scale(1) translateY(0)'; | |
| card.style.opacity = 1; | |
| card.style.pointerEvents = 'auto'; | |
| } | |
| }); | |
| } | |
| // 绑定拖拽事件 | |
| function attachDragEvents() { | |
| const cards = document.querySelectorAll('.card'); | |
| // 只给最上面的卡片绑定事件 | |
| const topCard = cards[cards.length - 1]; | |
| if (!topCard) return; | |
| let isDragging = false; | |
| let startX = 0; | |
| let currentX = 0; | |
| let startY = 0; | |
| let currentY = 0; | |
| const likeBadge = topCard.querySelector('.status-like'); | |
| const nopeBadge = topCard.querySelector('.status-nope'); | |
| const onStart = (e) => { | |
| isDragging = true; | |
| startX = e.clientX || e.touches[0].clientX; | |
| startY = e.clientY || e.touches[0].clientY; | |
| topCard.style.transition = 'none'; // 拖拽时移除过渡,实现跟手 | |
| }; | |
| const onMove = (e) => { | |
| if (!isDragging) return; | |
| const x = e.clientX || e.touches[0].clientX; | |
| const y = e.clientY || e.touches[0].clientY; | |
| currentX = x - startX; | |
| currentY = y - startY; | |
| // 旋转角度 | |
| const rotate = currentX * 0.1; | |
| topCard.style.transform = `translate(${currentX}px, ${currentY}px) rotate(${rotate}deg)`; | |
| // 显示状态标签 | |
| if (currentX > 0) { | |
| likeBadge.style.opacity = Math.min(currentX / 100, 1); | |
| nopeBadge.style.opacity = 0; | |
| } else { | |
| nopeBadge.style.opacity = Math.min(Math.abs(currentX) / 100, 1); | |
| likeBadge.style.opacity = 0; | |
| } | |
| }; | |
| const onEnd = () => { | |
| if (!isDragging) return; | |
| isDragging = false; | |
| const threshold = 100; // 滑动阈值 | |
| if (currentX > threshold) { | |
| swipeCard('right'); | |
| } else if (currentX < -threshold) { | |
| swipeCard('left'); | |
| } else { | |
| // 回弹 | |
| topCard.style.transition = 'transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)'; | |
| topCard.style.transform = 'translate(0, 0) rotate(0)'; | |
| likeBadge.style.opacity = 0; | |
| nopeBadge.style.opacity = 0; | |
| } | |
| }; | |
| // 鼠标事件 | |
| topCard.addEventListener('mousedown', onStart); | |
| document.addEventListener('mousemove', onMove); | |
| document.addEventListener('mouseup', onEnd); | |
| // 触摸事件 | |
| topCard.addEventListener('touchstart', onStart); | |
| document.addEventListener('touchmove', onMove); | |
| document.addEventListener('touchend', onEnd); | |
| } | |
| // 执行滑动逻辑 | |
| function swipeCard(direction) { | |
| const cards = document.querySelectorAll('.card'); | |
| const topCard = cards[cards.length - 1]; | |
| if (!topCard) return; | |
| const profile = profiles[topCard.dataset.index]; | |
| // 飞出动画 | |
| topCard.style.transition = 'transform 0.5s ease-out'; | |
| const endX = direction === 'right' ? window.innerWidth + 200 : -window.innerWidth - 200; | |
| const rotate = direction === 'right' ? 30 : -30; | |
| topCard.style.transform = `translate(${endX}px, 50px) rotate(${rotate}deg)`; | |
| // 移除 DOM | |
| setTimeout(() => { | |
| topCard.remove(); | |
| updateZIndex(); | |
| attachDragEvents(); // 重新绑定给新的顶卡 | |
| // 检查是否匹配 | |
| if (direction === 'right') { | |
| checkMatch(profile); | |
| } | |
| // 检查是否空了 | |
| if (document.querySelectorAll('.card').length === 0) { | |
| emptyState.style.display = 'block'; | |
| } | |
| }, 300); | |
| } | |
| // 底部按钮逻辑 | |
| document.getElementById('btnNope').addEventListener('click', () => { | |
| const cards = document.querySelectorAll('.card'); | |
| if (cards.length > 0) { | |
| swipeCard('left'); | |
| } | |
| }); | |
| document.getElementById('btnLike').addEventListener('click', () => { | |
| const cards = document.querySelectorAll('.card'); | |
| if (cards.length > 0) { | |
| swipeCard('right'); | |
| } | |
| }); | |
| document.getElementById('btnInfo').addEventListener('click', () => { | |
| const cards = document.querySelectorAll('.card'); | |
| if (cards.length > 0) { | |
| const topCard = cards[cards.length - 1]; | |
| const profile = profiles[topCard.dataset.index]; | |
| showDetail(profile); | |
| } | |
| }); | |
| // 匹配逻辑 | |
| function checkMatch(profile) { | |
| // 30% 几率匹配成功 | |
| if (Math.random() < 0.3) { | |
| document.getElementById('matchName').innerText = profile.name; | |
| document.getElementById('matchAvatar').src = profile.img; | |
| openModal('matchModal'); | |
| } | |
| } | |
| // 详情逻辑 | |
| function showDetail(profile) { | |
| document.getElementById('detailImg').src = profile.img; | |
| document.getElementById('detailName').innerText = profile.name; | |
| document.getElementById('detailAge').innerText = profile.age; | |
| document.getElementById('detailJob').innerText = profile.job; | |
| document.getElementById('detailLoc').innerText = profile.location; | |
| document.getElementById('detailBio').innerText = profile.bio; | |
| const tagsContainer = document.getElementById('detailTags'); | |
| tagsContainer.innerHTML = profile.tags.map(tag => `<span class="tag">#${tag}</span>`).join(''); | |
| openModal('detailModal'); | |
| } | |
| // 模态框通用 | |
| function openModal(id) { | |
| document.getElementById(id).classList.add('active'); | |
| } | |
| function closeModal(id) { | |
| document.getElementById(id).classList.remove('active'); | |
| } | |
| // 启动 | |
| initCards(); | |
| </script> | |
| </body> | |
| </html> |