Files
web_template/web/dist/shop/index.html
admin 8b00f14099 feat: 跨境电商3套模板添加3D动态光影效果
- shop: 红色经典风格 - 鼠标红色光斑+卡片8度3D倾斜+玻璃高光+Hero视差
- shop2: 暗黑霓虹风格 - 霓虹紫青光斑+卡片10度3D倾斜+霓虹高光+12个浮动粒子
- shop3: 北欧极简风格 - 柔和暖色光斑+卡片6度3D倾斜+柔和高光+入场动画
2026-07-11 15:31:22 +08:00

907 lines
47 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlobalShop · 跨境电商平台</title>
<style>
/* ========== 主题变量 ========== */
:root {
--primary: #E63946;
--primary-dark: #D62828;
--primary-light: #FF6B6B;
--accent: #457B9D;
--dark: #1D3557;
--bg: #F8F9FA;
--surface: #FFFFFF;
--text: #1D3557;
--text-muted: #6C757D;
--border: #E9ECEF;
--shadow: 0 2px 8px rgba(0,0,0,0.06);
--shadow-lg: 0 8px 30px rgba(0,0,0,0.12);
--radius: 10px;
--radius-sm: 6px;
--radius-lg: 16px;
--transition: all 0.25s cubic-bezier(0.4,0,0.2,1);
}
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; background:var(--bg); color:var(--text); line-height:1.6; -webkit-font-smoothing:antialiased; }
a { text-decoration:none; color:inherit; }
button { cursor:pointer; border:none; font-family:inherit; }
input { font-family:inherit; }
img { max-width:100%; display:block; }
/* ========== 顶部公告栏 ========== */
.topbar { background:var(--dark); color:#fff; font-size:13px; padding:8px 0; text-align:center; }
.topbar span { margin:0 16px; opacity:0.9; }
.topbar a { color:var(--primary-light); }
/* ========== 导航栏 ========== */
.navbar { background:var(--surface); box-shadow:var(--shadow); position:sticky; top:0; z-index:100; }
.nav-container { max-width:1280px; margin:0 auto; padding:0 24px; display:flex; align-items:center; gap:32px; height:64px; }
.logo { font-size:24px; font-weight:800; color:var(--primary); display:flex; align-items:center; gap:8px; white-space:nowrap; }
.logo svg { width:28px; height:28px; }
.nav-links { display:flex; gap:4px; list-style:none; }
.nav-links a { padding:8px 16px; border-radius:var(--radius-sm); font-size:14px; font-weight:500; color:var(--text); transition:var(--transition); }
.nav-links a:hover, .nav-links a.active { background:var(--primary); color:#fff; }
.search-box { flex:1; max-width:400px; position:relative; }
.search-box input { width:100%; height:40px; padding:0 16px 0 40px; border:2px solid var(--border); border-radius:var(--radius); font-size:14px; outline:none; transition:var(--transition); }
.search-box input:focus { border-color:var(--primary); }
.search-box svg { position:absolute; left:12px; top:50%; transform:translateY(-50%); width:18px; height:18px; color:var(--text-muted); }
.nav-actions { display:flex; align-items:center; gap:8px; }
.nav-actions button { background:none; width:40px; height:40px; border-radius:50%; display:flex; align-items:center; justify-content:center; transition:var(--transition); position:relative; }
.nav-actions button:hover { background:var(--bg); }
.nav-actions svg { width:22px; height:22px; color:var(--text); }
.cart-badge { position:absolute; top:2px; right:2px; background:var(--primary); color:#fff; font-size:11px; font-weight:700; min-width:18px; height:18px; border-radius:9px; display:flex; align-items:center; justify-content:center; padding:0 4px; }
/* ========== Hero 轮播 ========== */
.hero { max-width:1280px; margin:24px auto; padding:0 24px; }
.hero-banner { background:linear-gradient(135deg, #E63946 0%, #D62828 50%, #1D3557 100%); border-radius:var(--radius-lg); padding:60px 48px; color:#fff; position:relative; overflow:hidden; min-height:280px; display:flex; align-items:center; }
.hero-banner::before { content:''; position:absolute; top:-50%; right:-10%; width:400px; height:400px; background:rgba(255,255,255,0.08); border-radius:50%; }
.hero-banner::after { content:''; position:absolute; bottom:-30%; right:20%; width:300px; height:300px; background:rgba(255,255,255,0.05); border-radius:50%; }
.hero-content { position:relative; z-index:1; max-width:560px; }
.hero-content h1 { font-size:42px; font-weight:800; line-height:1.2; margin-bottom:16px; }
.hero-content p { font-size:18px; opacity:0.9; margin-bottom:28px; }
.hero-btn { display:inline-flex; align-items:center; gap:8px; background:#fff; color:var(--primary); padding:14px 32px; border-radius:var(--radius); font-size:16px; font-weight:700; transition:var(--transition); }
.hero-btn:hover { transform:translateY(-2px); box-shadow:0 8px 24px rgba(0,0,0,0.2); }
/* ========== 优势条 ========== */
.features-strip { max-width:1280px; margin:24px auto; padding:0 24px; display:grid; grid-template-columns:repeat(4,1fr); gap:16px; }
.feature-item { background:var(--surface); border-radius:var(--radius); padding:20px; display:flex; align-items:center; gap:14px; box-shadow:var(--shadow); }
.feature-item svg { width:36px; height:36px; color:var(--primary); flex-shrink:0; }
.feature-item h4 { font-size:14px; font-weight:700; }
.feature-item p { font-size:12px; color:var(--text-muted); }
/* ========== 分类导航 ========== */
.section { max-width:1280px; margin:48px auto; padding:0 24px; }
.section-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:24px; }
.section-title { font-size:26px; font-weight:800; position:relative; padding-left:16px; }
.section-title::before { content:''; position:absolute; left:0; top:50%; transform:translateY(-50%); width:4px; height:28px; background:var(--primary); border-radius:2px; }
.section-more { font-size:14px; color:var(--primary); font-weight:600; }
.category-nav { display:flex; gap:12px; overflow-x:auto; padding-bottom:8px; margin-bottom:24px; }
.category-chip { padding:10px 24px; border-radius:var(--radius); background:var(--surface); border:2px solid var(--border); font-size:14px; font-weight:600; color:var(--text); white-space:nowrap; transition:var(--transition); }
.category-chip:hover { border-color:var(--primary); color:var(--primary); }
.category-chip.active { background:var(--primary); color:#fff; border-color:var(--primary); }
/* ========== 商品网格 ========== */
.products-layout { display:grid; grid-template-columns:220px 1fr; gap:24px; }
.filters-sidebar { background:var(--surface); border-radius:var(--radius); padding:20px; box-shadow:var(--shadow); height:fit-content; position:sticky; top:80px; }
.filters-sidebar h3 { font-size:16px; font-weight:700; margin-bottom:16px; padding-bottom:12px; border-bottom:1px solid var(--border); }
.filter-group { margin-bottom:20px; }
.filter-group h4 { font-size:13px; font-weight:700; margin-bottom:10px; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.5px; }
.filter-option { display:flex; align-items:center; gap:8px; padding:6px 0; cursor:pointer; font-size:14px; }
.filter-option input { accent-color:var(--primary); }
.filter-option label { cursor:pointer; }
.filter-option .count { margin-left:auto; font-size:12px; color:var(--text-muted); }
.products-main { min-width:0; }
.products-toolbar { display:flex; align-items:center; justify-content:space-between; margin-bottom:20px; flex-wrap:wrap; gap:12px; }
.products-count { font-size:14px; color:var(--text-muted); }
.sort-select { padding:8px 16px; border:2px solid var(--border); border-radius:var(--radius-sm); font-size:14px; outline:none; background:var(--surface); cursor:pointer; }
.sort-select:focus { border-color:var(--primary); }
.products-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:20px; }
.product-card { background:var(--surface); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); transition:var(--transition); cursor:pointer; position:relative; }
.product-card:hover { transform:translateY(-4px); box-shadow:var(--shadow-lg); }
.product-image { width:100%; aspect-ratio:1; background:linear-gradient(135deg,#f0f0f0,#e0e0e0); position:relative; overflow:hidden; }
.product-image-gradient { width:100%; height:100%; display:flex; align-items:center; justify-content:center; }
.product-image-gradient svg { width:50%; height:50%; opacity:0.3; }
.product-tags { position:absolute; top:10px; left:10px; display:flex; flex-direction:column; gap:4px; }
.product-tag { padding:3px 10px; border-radius:var(--radius-sm); font-size:11px; font-weight:700; color:#fff; }
.tag-hot { background:var(--primary); }
.tag-new { background:var(--accent); }
.tag-sale { background:#FF8C00; }
.product-discount { position:absolute; top:10px; right:10px; background:var(--primary); color:#fff; font-size:12px; font-weight:700; padding:4px 8px; border-radius:var(--radius-sm); }
.product-info { padding:14px; }
.product-name { font-size:14px; font-weight:600; line-height:1.4; margin-bottom:8px; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; min-height:39px; }
.product-price { display:flex; align-items:baseline; gap:8px; margin-bottom:8px; }
.price-current { font-size:20px; font-weight:800; color:var(--primary); }
.price-original { font-size:14px; color:var(--text-muted); text-decoration:line-through; }
.product-meta { display:flex; align-items:center; justify-content:space-between; font-size:12px; color:var(--text-muted); }
.product-rating { display:flex; align-items:center; gap:4px; }
.stars { color:#FFD700; letter-spacing:1px; }
.product-sales { }
.add-cart-btn { position:absolute; bottom:14px; right:14px; width:36px; height:36px; border-radius:50%; background:var(--primary); color:#fff; display:flex; align-items:center; justify-content:center; opacity:0; transform:translateY(10px); transition:var(--transition); }
.product-card:hover .add-cart-btn { opacity:1; transform:translateY(0); }
.add-cart-btn:hover { background:var(--primary-dark); transform:scale(1.1); }
.add-cart-btn svg { width:18px; height:18px; }
/* ========== 加载骨架 ========== */
.skeleton-card { background:var(--surface); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); }
.skeleton-img { aspect-ratio:1; background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%); background-size:200% 100%; animation:shimmer 1.5s infinite; }
.skeleton-line { height:14px; margin:12px 14px; border-radius:4px; background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%); background-size:200% 100%; animation:shimmer 1.5s infinite; }
@keyframes shimmer { 0%{background-position:200% 0;} 100%{background-position:-200% 0;} }
/* ========== 分页 ========== */
.pagination { display:flex; justify-content:center; gap:8px; margin-top:40px; }
.page-btn { min-width:40px; height:40px; border-radius:var(--radius-sm); background:var(--surface); border:2px solid var(--border); font-size:14px; font-weight:600; transition:var(--transition); }
.page-btn:hover:not(:disabled) { border-color:var(--primary); color:var(--primary); }
.page-btn.active { background:var(--primary); color:#fff; border-color:var(--primary); }
.page-btn:disabled { opacity:0.4; cursor:not-allowed; }
/* ========== 购物车抽屉 ========== */
.cart-overlay { position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:200; opacity:0; visibility:hidden; transition:var(--transition); }
.cart-overlay.open { opacity:1; visibility:visible; }
.cart-drawer { position:fixed; top:0; right:0; width:420px; max-width:90vw; height:100vh; background:var(--surface); z-index:201; transform:translateX(100%); transition:transform 0.3s cubic-bezier(0.4,0,0.2,1); display:flex; flex-direction:column; }
.cart-drawer.open { transform:translateX(0); }
.cart-header { padding:20px 24px; border-bottom:1px solid var(--border); display:flex; align-items:center; justify-content:space-between; }
.cart-header h3 { font-size:18px; font-weight:800; }
.cart-close { width:32px; height:32px; border-radius:50%; background:var(--bg); display:flex; align-items:center; justify-content:center; font-size:20px; color:var(--text-muted); }
.cart-items { flex:1; overflow-y:auto; padding:16px 24px; }
.cart-item { display:flex; gap:12px; padding:16px 0; border-bottom:1px solid var(--border); }
.cart-item-img { width:72px; height:72px; border-radius:var(--radius-sm); background:linear-gradient(135deg,#f0f0f0,#e0e0e0); flex-shrink:0; display:flex; align-items:center; justify-content:center; }
.cart-item-img svg { width:32px; height:32px; opacity:0.3; }
.cart-item-info { flex:1; min-width:0; }
.cart-item-name { font-size:14px; font-weight:600; margin-bottom:4px; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.cart-item-price { font-size:16px; font-weight:700; color:var(--primary); }
.cart-item-controls { display:flex; align-items:center; gap:12px; margin-top:8px; }
.qty-btn { width:28px; height:28px; border-radius:var(--radius-sm); background:var(--bg); display:flex; align-items:center; justify-content:center; font-size:16px; font-weight:700; }
.qty-btn:hover { background:var(--border); }
.qty-value { font-size:14px; font-weight:600; min-width:24px; text-align:center; }
.cart-item-remove { margin-left:auto; font-size:12px; color:var(--text-muted); }
.cart-item-remove:hover { color:var(--primary); }
.cart-empty { text-align:center; padding:60px 20px; color:var(--text-muted); }
.cart-empty svg { width:64px; height:64px; margin:0 auto 16px; opacity:0.3; }
.cart-footer { padding:20px 24px; border-top:1px solid var(--border); background:var(--bg); }
.cart-total { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
.cart-total-label { font-size:14px; color:var(--text-muted); }
.cart-total-value { font-size:24px; font-weight:800; color:var(--primary); }
.checkout-btn { width:100%; padding:14px; border-radius:var(--radius); background:var(--primary); color:#fff; font-size:16px; font-weight:700; transition:var(--transition); }
.checkout-btn:hover { background:var(--primary-dark); }
.checkout-btn:disabled { opacity:0.5; cursor:not-allowed; }
/* ========== 商品详情模态框 ========== */
.modal-overlay { position:fixed; inset:0; background:rgba(0,0,0,0.6); z-index:300; display:flex; align-items:center; justify-content:center; padding:20px; opacity:0; visibility:hidden; transition:var(--transition); }
.modal-overlay.open { opacity:1; visibility:visible; }
.modal-content { background:var(--surface); border-radius:var(--radius-lg); max-width:800px; width:100%; max-height:90vh; overflow-y:auto; transform:scale(0.9); transition:transform 0.3s; }
.modal-overlay.open .modal-content { transform:scale(1); }
.modal-close { position:absolute; top:16px; right:16px; width:36px; height:36px; border-radius:50%; background:rgba(0,0,0,0.1); display:flex; align-items:center; justify-content:center; font-size:24px; z-index:1; }
.modal-body { display:grid; grid-template-columns:1fr 1fr; gap:0; }
.modal-image { background:linear-gradient(135deg,#f0f0f0,#e0e0e0); aspect-ratio:1; display:flex; align-items:center; justify-content:center; }
.modal-image svg { width:40%; height:40%; opacity:0.2; }
.modal-details { padding:32px; position:relative; }
.modal-details h2 { font-size:22px; font-weight:700; margin-bottom:12px; line-height:1.3; }
.modal-price { display:flex; align-items:baseline; gap:12px; margin-bottom:16px; }
.modal-price .price-current { font-size:32px; }
.modal-tags { display:flex; gap:8px; margin-bottom:16px; }
.modal-desc { font-size:14px; color:var(--text-muted); line-height:1.7; margin-bottom:20px; }
.modal-meta { display:flex; gap:24px; padding:16px 0; border-top:1px solid var(--border); border-bottom:1px solid var(--border); margin-bottom:20px; font-size:13px; }
.modal-meta-item { display:flex; flex-direction:column; gap:2px; }
.modal-meta-item span:first-child { color:var(--text-muted); }
.modal-meta-item span:last-child { font-weight:600; }
.modal-add-cart { width:100%; padding:14px; border-radius:var(--radius); background:var(--primary); color:#fff; font-size:16px; font-weight:700; transition:var(--transition); }
.modal-add-cart:hover { background:var(--primary-dark); }
/* ========== 页脚 ========== */
.footer { background:var(--dark); color:#fff; margin-top:64px; }
.footer-content { max-width:1280px; margin:0 auto; padding:48px 24px 24px; display:grid; grid-template-columns:2fr 1fr 1fr 1fr; gap:40px; }
.footer-brand h3 { font-size:22px; font-weight:800; color:var(--primary-light); margin-bottom:12px; }
.footer-brand p { font-size:14px; opacity:0.7; line-height:1.7; }
.footer-col h4 { font-size:15px; font-weight:700; margin-bottom:16px; }
.footer-col ul { list-style:none; }
.footer-col li { margin-bottom:10px; }
.footer-col a { font-size:13px; opacity:0.7; transition:var(--transition); }
.footer-col a:hover { opacity:1; color:var(--primary-light); }
.footer-bottom { border-top:1px solid rgba(255,255,255,0.1); padding:20px 24px; text-align:center; font-size:13px; opacity:0.6; }
/* ========== Toast 提示 ========== */
.toast { position:fixed; bottom:30px; left:50%; transform:translateX(-50%) translateY(100px); background:var(--dark); color:#fff; padding:12px 24px; border-radius:var(--radius); font-size:14px; font-weight:600; z-index:400; opacity:0; transition:all 0.3s; display:flex; align-items:center; gap:8px; }
.toast.show { transform:translateX(-50%) translateY(0); opacity:1; }
.toast svg { width:20px; height:20px; color:#4ADE80; }
/* ========== 响应式 ========== */
@media (max-width:1024px) {
.products-grid { grid-template-columns:repeat(3,1fr); }
.features-strip { grid-template-columns:repeat(2,1fr); }
.footer-content { grid-template-columns:2fr 1fr 1fr; }
}
@media (max-width:768px) {
.nav-links { display:none; }
.search-box { max-width:200px; }
.hero-banner { padding:40px 24px; }
.hero-content h1 { font-size:28px; }
.hero-content p { font-size:15px; }
.products-layout { grid-template-columns:1fr; }
.filters-sidebar { position:static; }
.products-grid { grid-template-columns:repeat(2,1fr); }
.modal-body { grid-template-columns:1fr; }
.footer-content { grid-template-columns:1fr; gap:24px; }
}
@media (max-width:480px) {
.features-strip { grid-template-columns:1fr; }
.products-grid { grid-template-columns:1fr; }
.hero-content h1 { font-size:24px; }
}
/* ========== 3D 动态效果 & 鼠标光影(新增)========== */
/* 鼠标跟随光斑 */
.cursor-glow{position:fixed;width:600px;height:600px;border-radius:50%;background:radial-gradient(circle,rgba(230,57,70,0.12) 0%,transparent 70%);pointer-events:none;z-index:150;transform:translate(-50%,-50%);transition:opacity .3s;mix-blend-mode:multiply;opacity:0;left:0;top:0}
.cursor-glow.active{opacity:1}
/* 3D 卡片容器与倾斜 */
.products-grid{perspective:1000px}
.product-card{transform-style:preserve-3d;transition:transform .15s ease-out,box-shadow .25s}
.product-card .product-image{transform:translateZ(20px)}
.product-card .product-info{transform:translateZ(15px)}
/* 卡片表面光影反射(玻璃高光) */
.product-card::after{content:'';position:absolute;inset:0;border-radius:inherit;background:radial-gradient(circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,0.25) 0%,transparent 50%);opacity:0;transition:opacity .3s;pointer-events:none;z-index:2}
.product-card:hover::after{opacity:1}
/* 触摸设备隐藏光斑 */
@media (hover:none){.cursor-glow{display:none !important}}
</style>
</head>
<body>
<!-- 顶部公告栏 -->
<div class="topbar">
<span>🚀 全球直邮 · 满 $99 免运费</span>
<span>📦 7天无理由退换</span>
<span>🔒 安全支付 · <a href="#">查看详情</a></span>
</div>
<!-- 导航栏 -->
<nav class="navbar">
<div class="nav-container">
<div class="logo">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7v10l10 5 10-5V7L12 2z"/><path d="M2 7l10 5 10-5"/><path d="M12 22V12"/></svg>
GlobalShop
</div>
<ul class="nav-links">
<li><a href="#" class="active" onclick="resetPage();return false;">首页</a></li>
<li><a href="#products" onclick="scrollToProducts();return false;">所有商品</a></li>
<li><a href="#featured" onclick="loadFeatured();return false;">热卖推荐</a></li>
<li><a href="#deals" onclick="filterDeals();return false;">限时特惠</a></li>
</ul>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<input type="text" id="searchInput" placeholder="搜索商品..." oninput="handleSearch(event)">
</div>
<div class="nav-actions">
<button title="收藏">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
</button>
<button title="购物车" onclick="toggleCart()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
<span class="cart-badge" id="cartBadge" style="display:none">0</span>
</button>
</div>
</div>
</nav>
<!-- Hero Banner -->
<div class="hero">
<div class="hero-banner">
<div class="hero-content">
<h1>全球好物<br>跨境直达</h1>
<p>精选 18+ 品类好物,覆盖智能电子、家居生活、时尚运动,全球直邮,品质保障</p>
<a href="#" class="hero-btn" onclick="scrollToProducts();return false;">
立即选购
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:20px;height:20px"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
</div>
</div>
<!-- 优势条 -->
<div class="features-strip">
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="3" width="15" height="13"/><path d="M16 8h4l3 3v5h-7V8z"/><circle cx="5.5" cy="18.5" r="2.5"/><circle cx="18.5" cy="18.5" r="2.5"/></svg>
<div><h4>全球直邮</h4><p>满 $99 免运费</p></div>
</div>
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
<div><h4>正品保障</h4><p>品牌授权直供</p></div>
</div>
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
<div><h4>7天退换</h4><p>无理由退货</p></div>
</div>
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="4" width="22" height="16" rx="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg>
<div><h4>安全支付</h4><p>多渠道加密</p></div>
</div>
</div>
<!-- 商品列表区 -->
<div class="section" id="products">
<div class="section-header">
<div class="section-title">全部商品</div>
</div>
<div class="products-layout">
<!-- 侧边筛选 -->
<aside class="filters-sidebar">
<h3>筛选</h3>
<div class="filter-group">
<h4>分类</h4>
<div id="categoryFilters"></div>
</div>
<div class="filter-group">
<h4>价格区间</h4>
<div class="filter-option"><input type="radio" name="price" id="p0" value="0" checked onchange="applyFilters()"><label for="p0">全部</label></div>
<div class="filter-option"><input type="radio" name="price" id="p1" value="0-30" onchange="applyFilters()"><label for="p1">$0 - $30</label></div>
<div class="filter-option"><input type="radio" name="price" id="p2" value="30-60" onchange="applyFilters()"><label for="p2">$30 - $60</label></div>
<div class="filter-option"><input type="radio" name="price" id="p3" value="60-100" onchange="applyFilters()"><label for="p3">$60 - $100</label></div>
<div class="filter-option"><input type="radio" name="price" id="p4" value="100-9999" onchange="applyFilters()"><label for="p4">$100+</label></div>
</div>
<div class="filter-group">
<h4>排序</h4>
<div class="filter-option"><input type="radio" name="sort" id="s1" value="default" checked onchange="applyFilters()"><label for="s1">默认排序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s2" value="sales" onchange="applyFilters()"><label for="s2">销量优先</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s3" value="price_asc" onchange="applyFilters()"><label for="s3">价格升序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s4" value="price_desc" onchange="applyFilters()"><label for="s4">价格降序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s5" value="rating" onchange="applyFilters()"><label for="s5">评分最高</label></div>
</div>
</aside>
<!-- 商品网格 -->
<div class="products-main">
<div class="products-toolbar">
<div class="products-count" id="productsCount">加载中...</div>
<div class="category-nav" style="margin:0;padding:0;overflow:visible;border:none" id="categoryChips"></div>
</div>
<div class="products-grid" id="productsGrid">
<!-- 骨架屏 -->
</div>
<div class="pagination" id="pagination"></div>
</div>
</div>
</div>
<!-- 购物车遮罩 -->
<div class="cart-overlay" id="cartOverlay" onclick="toggleCart()"></div>
<!-- 购物车抽屉 -->
<div class="cart-drawer" id="cartDrawer">
<div class="cart-header">
<h3>购物车 (<span id="cartCount">0</span>)</h3>
<button class="cart-close" onclick="toggleCart()">&times;</button>
</div>
<div class="cart-items" id="cartItems"></div>
<div class="cart-footer">
<div class="cart-total">
<span class="cart-total-label">合计</span>
<span class="cart-total-value" id="cartTotal">$0.00</span>
</div>
<button class="checkout-btn" id="checkoutBtn" onclick="checkout()">去结算</button>
</div>
</div>
<!-- 商品详情模态框 -->
<div class="modal-overlay" id="productModal" onclick="closeModal(event)">
<div class="modal-content" id="modalContent"></div>
</div>
<!-- Toast -->
<div class="toast" id="toast">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
<span id="toastMsg">已加入购物车</span>
</div>
<!-- 页脚 -->
<footer class="footer">
<div class="footer-content">
<div class="footer-brand">
<h3>GlobalShop</h3>
<p>全球好物,跨境直达。我们精选全球优质商品,为您提供安全、便捷、愉悦的跨境购物体验。覆盖智能电子、家居生活、时尚服饰、运动户外、美妆个护等品类。</p>
</div>
<div class="footer-col">
<h4>购物指南</h4>
<ul>
<li><a href="#">购物流程</a></li>
<li><a href="#">会员介绍</a></li>
<li><a href="#">常见问题</a></li>
<li><a href="#">联系客服</a></li>
</ul>
</div>
<div class="footer-col">
<h4>配送与退换</h4>
<ul>
<li><a href="#">配送方式</a></li>
<li><a href="#">运费规则</a></li>
<li><a href="#">退换货政策</a></li>
<li><a href="#">退款说明</a></li>
</ul>
</div>
<div class="footer-col">
<h4>关于我们</h4>
<ul>
<li><a href="#">公司简介</a></li>
<li><a href="#">加入我们</a></li>
<li><a href="#">隐私政策</a></li>
<li><a href="#">用户协议</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
© 2026 GlobalShop. All rights reserved. 全球好物 · 跨境直达
</div>
</footer>
<script>
// ========== 全局状态 ==========
let allCategories = [];
let currentPage = 1;
const pageSize = 12;
let selectedCategory = 0;
let searchKeyword = '';
let cart = [];
// ========== 图标库 ==========
const iconColors = ['#E63946','#457B9D','#F4A261','#2A9D8F','#9D4EDD','#E76F51','#06A77D','#D62828'];
function getGradient(seed) {
const c = iconColors[seed % iconColors.length];
return `linear-gradient(135deg, ${c}22, ${c}44)`;
}
function productIconSVG(catId) {
const icons = {
1: '<path d="M4 4h16v12H4z"/><path d="M2 20h20"/><path d="M9 8h6"/>',
2: '<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/>',
3: '<path d="M20.38 3.46L16 2a4 4 0 0 1-8 0L3.62 3.46a2 2 0 0 0-1.34 2.23l.58 3.47a1 1 0 0 0 .99.84H6v10a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V10h2.15a1 1 0 0 0 .99-.84l.58-3.47a2 2 0 0 0-1.34-2.23z"/>',
4: '<path d="M6.5 6.5l11 11"/><path d="M21 21l-6-6m6 6v-4m0 4h-4"/><circle cx="12" cy="12" r="10"/><path d="M2 12h20"/>',
5: '<path d="M12 2v20M2 12h20"/><circle cx="12" cy="12" r="4"/>',
};
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">${icons[catId] || icons[1]}</svg>`;
}
// ========== API 调用 ==========
async function fetchJSON(url) {
const res = await fetch(url);
const json = await res.json();
if (json.code !== 0) throw new Error(json.message || '请求失败');
return json.data;
}
async function loadCategories() {
try {
allCategories = await fetchJSON('/api/v1/shop/categories');
renderCategoryFilters();
renderCategoryChips();
} catch(e) { console.error('分类加载失败:', e); }
}
async function loadProducts() {
const grid = document.getElementById('productsGrid');
grid.innerHTML = Array(8).fill(0).map(() => `
<div class="skeleton-card">
<div class="skeleton-img"></div>
<div class="skeleton-line"></div>
<div class="skeleton-line" style="width:60%"></div>
<div class="skeleton-line" style="width:40%"></div>
</div>
`).join('');
try {
let url = `/api/v1/shop/products?page=${currentPage}&page_size=${pageSize}`;
const sortRadio = document.querySelector('input[name="sort"]:checked');
if (sortRadio && sortRadio.value !== 'default') url += `&sort=${sortRadio.value}`;
if (selectedCategory > 0) url += `&category_id=${selectedCategory}`;
if (searchKeyword) url += `&keyword=${encodeURIComponent(searchKeyword)}`;
const data = await fetchJSON(url);
renderProducts(data.list || [], data.total || 0);
renderPagination(data.total || 0);
} catch(e) {
grid.innerHTML = `<div style="grid-column:1/-1;text-align:center;padding:60px;color:var(--text-muted)">
<p>商品加载失败,请稍后重试</p></div>`;
}
}
// ========== 渲染 ==========
function renderCategoryFilters() {
const container = document.getElementById('categoryFilters');
container.innerHTML = `
<div class="filter-option">
<input type="radio" name="cat-filter" id="cat0" value="0" checked onchange="selectCategory(0)">
<label for="cat0">全部分类</label>
</div>
` + allCategories.map(c => `
<div class="filter-option">
<input type="radio" name="cat-filter" id="cat${c.id}" value="${c.id}" onchange="selectCategory(${c.id})">
<label for="cat${c.id}">${c.name}</label>
</div>
`).join('');
}
function renderCategoryChips() {
const container = document.getElementById('categoryChips');
container.innerHTML = `<span class="category-chip ${selectedCategory === 0 ? 'active' : ''}" onclick="selectCategory(0)">全部</span>` +
allCategories.map(c => `<span class="category-chip ${selectedCategory === c.id ? 'active' : ''}" onclick="selectCategory(${c.id})">${c.name}</span>`).join('');
}
function renderProducts(products, total) {
const grid = document.getElementById('productsGrid');
document.getElementById('productsCount').textContent = `共 ${total} 件商品`;
if (products.length === 0) {
grid.innerHTML = `<div style="grid-column:1/-1;text-align:center;padding:60px;color:var(--text-muted)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:48px;height:48px;margin:0 auto 12px;opacity:0.3"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<p>未找到匹配的商品</p></div>`;
return;
}
grid.innerHTML = products.map((p, i) => {
const discount = p.original_price > p.price ? Math.round((1 - p.price / p.original_price) * 100) : 0;
let tags = [];
try { tags = JSON.parse(p.tags || '[]'); } catch(e) {}
const tagHtml = tags.slice(0, 2).map(t => {
const cls = t.includes('热') || t.includes('爆') ? 'tag-hot' : t.includes('新') ? 'tag-new' : 'tag-sale';
return `<span class="product-tag ${cls}">${t}</span>`;
}).join('');
return `
<div class="product-card" onclick="showProductDetail(${p.id})">
<div class="product-image">
${p.image ? `<img src="${p.image}" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" onerror="this.style.display='none';this.nextElementSibling.style.display='flex'">` : ''}
<div class="product-image-gradient" style="background:${getGradient(p.category_id)};${p.image ? 'display:none' : 'display:flex'}">
${productIconSVG(p.category_id)}
</div>
<div class="product-tags">${tagHtml}</div>
${discount > 0 ? `<div class="product-discount">-${discount}%</div>` : ''}
</div>
<div class="product-info">
<div class="product-name">${p.name}</div>
<div class="product-price">
<span class="price-current">$${p.price.toFixed(2)}</span>
${p.original_price > p.price ? `<span class="price-original">$${p.original_price.toFixed(2)}</span>` : ''}
</div>
<div class="product-meta">
<div class="product-rating">
<span class="stars">${'★'.repeat(Math.round(p.rating))}</span>
<span>${p.rating.toFixed(1)}</span>
</div>
<span class="product-sales">已售 ${formatSales(p.sales)}</span>
</div>
</div>
<button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${p.id})" title="加入购物车">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
</button>
</div>`;
}).join('');
}
function renderPagination(total) {
const pages = Math.ceil(total / pageSize);
if (pages <= 1) { document.getElementById('pagination').innerHTML = ''; return; }
let html = `<button class="page-btn" ${currentPage === 1 ? 'disabled' : ''} onclick="goToPage(${currentPage - 1})">&lt;</button>`;
const maxVisible = 5;
let start = Math.max(1, currentPage - 2);
let end = Math.min(pages, start + maxVisible - 1);
if (end - start < maxVisible - 1) start = Math.max(1, end - maxVisible + 1);
if (start > 1) { html += `<button class="page-btn" onclick="goToPage(1)">1</button>`; if (start > 2) html += `<span style="padding:0 4px;color:var(--text-muted)">...</span>`; }
for (let i = start; i <= end; i++) {
html += `<button class="page-btn ${i === currentPage ? 'active' : ''}" onclick="goToPage(${i})">${i}</button>`;
}
if (end < pages) { if (end < pages - 1) html += `<span style="padding:0 4px;color:var(--text-muted)">...</span>`; html += `<button class="page-btn" onclick="goToPage(${pages})">${pages}</button>`; }
html += `<button class="page-btn" ${currentPage === pages ? 'disabled' : ''} onclick="goToPage(${currentPage + 1})">&gt;</button>`;
document.getElementById('pagination').innerHTML = html;
}
// ========== 交互函数 ==========
function selectCategory(catId) {
selectedCategory = catId;
currentPage = 1;
document.querySelectorAll('input[name="cat-filter"]').forEach(r => { r.checked = parseInt(r.value) === catId; });
document.querySelectorAll('.category-chip').forEach((c, i) => {
c.classList.toggle('active', (i === 0 && catId === 0) || (i > 0 && allCategories[i - 1] && allCategories[i - 1].id === catId));
});
loadProducts();
}
function applyFilters() {
// 侧边栏排序与价格筛选
const priceRadio = document.querySelector('input[name="price"]:checked');
const sortRadio = document.querySelector('input[name="sort"]:checked');
// 同步排序到主工具栏的 sort
currentPage = 1;
loadProducts();
}
function handleSearch(e) {
searchKeyword = e.target.value.trim();
currentPage = 1;
clearTimeout(window._searchTimer);
window._searchTimer = setTimeout(loadProducts, 400);
}
function goToPage(page) {
currentPage = page;
loadProducts();
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
}
function scrollToProducts() {
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
}
function resetPage() {
selectedCategory = 0;
searchKeyword = '';
currentPage = 1;
document.getElementById('searchInput').value = '';
document.querySelectorAll('input[name="price"]').forEach(r => r.checked = r.value === '0');
document.querySelectorAll('input[name="sort"]').forEach(r => r.checked = r.value === 'default');
selectCategory(0);
}
function filterDeals() {
// 筛选有折扣的商品(通过搜索价格降序再筛选不太精确,这里简单跳转)
scrollToProducts();
showToast('正在筛选限时特惠商品...');
}
async function loadFeatured() {
try {
const products = await fetchJSON('/api/v1/shop/featured?limit=8');
renderProducts(products, products.length);
document.getElementById('pagination').innerHTML = '';
document.getElementById('productsCount').textContent = `热卖推荐 · ${products.length} 件商品`;
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
} catch(e) { showToast('加载推荐失败'); }
}
// ========== 商品详情 ==========
async function showProductDetail(id) {
const modal = document.getElementById('productModal');
const content = document.getElementById('modalContent');
content.innerHTML = '<div style="padding:60px;text-align:center;color:var(--text-muted)">加载中...</div>';
modal.classList.add('open');
try {
const p = await fetchJSON(`/api/v1/shop/products/${id}`);
let tags = [];
try { tags = JSON.parse(p.tags || '[]'); } catch(e) {}
let imgList = [];
try { imgList = JSON.parse(p.images || '[]'); } catch(e) {}
if (!Array.isArray(imgList)) imgList = [];
if (p.image) imgList = [p.image, ...imgList.filter(img => img !== p.image)];
const discount = p.original_price > p.price ? Math.round((1 - p.price / p.original_price) * 100) : 0;
content.innerHTML = `
<button class="modal-close" onclick="closeModal()">&times;</button>
<div class="modal-body">
<div class="modal-image" style="background:${getGradient(p.category_id)};position:relative;overflow:hidden">
${imgList.length > 0 ? `<img id="modalMainImg" src="${imgList[0]}" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" onerror="this.style.display='none';this.nextElementSibling.style.display=''">` : ''}
<div style="${imgList.length > 0 ? 'display:none' : ''}">${productIconSVG(p.category_id)}</div>
${imgList.length > 1 ? `<div style="position:absolute;bottom:8px;left:0;right:0;display:flex;gap:6px;justify-content:center;z-index:5">${imgList.map((img,idx) => `<img src="${img}" onclick="document.getElementById('modalMainImg').src=this.src" style="width:44px;height:44px;border-radius:6px;object-fit:cover;cursor:pointer;border:2px solid ${idx===0?'#fff':'rgba(255,255,255,0.5)'}" onerror="this.remove()">`).join('')}</div>` : ''}
</div>
<div class="modal-details">
<h2>${p.name}</h2>
<div class="modal-price">
<span class="price-current" style="color:var(--primary);font-size:32px;font-weight:800">$${p.price.toFixed(2)}</span>
${p.original_price > p.price ? `<span class="price-original" style="font-size:16px;color:var(--text-muted);text-decoration:line-through">$${p.original_price.toFixed(2)}</span>` : ''}
${discount > 0 ? `<span style="background:var(--primary);color:#fff;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:700">省 ${discount}%</span>` : ''}
</div>
<div class="modal-tags">${tags.map(t => `<span class="product-tag ${t.includes('热') ? 'tag-hot' : t.includes('新') ? 'tag-new' : 'tag-sale'}">${t}</span>`).join('')}</div>
<p class="modal-desc">${p.description}</p>
<div class="modal-meta">
<div class="modal-meta-item"><span>评分</span><span>${p.rating.toFixed(1)} ★</span></div>
<div class="modal-meta-item"><span>销量</span><span>${formatSales(p.sales)}</span></div>
<div class="modal-meta-item"><span>库存</span><span>${p.stock} 件</span></div>
</div>
<button class="modal-add-cart" onclick="addToCart(${p.id});closeModal()">加入购物车</button>
</div>
</div>
`;
} catch(e) {
content.innerHTML = '<div style="padding:60px;text-align:center;color:var(--text-muted)">商品信息加载失败</div>';
}
}
function closeModal(e) {
if (e && e.target !== e.currentTarget) return;
document.getElementById('productModal').classList.remove('open');
}
// ========== 购物车 ==========
let productCache = {};
async function addToCart(productId) {
let product = productCache[productId];
if (!product) {
try {
product = await fetchJSON(`/api/v1/shop/products/${productId}`);
productCache[productId] = product;
} catch(e) { showToast('添加失败'); return; }
}
const existing = cart.find(i => i.id === productId);
if (existing) {
existing.qty++;
} else {
cart.push({ id: productId, name: product.name, price: product.price, qty: 1, category_id: product.category_id, image: product.image || '' });
}
updateCartUI();
showToast(`已加入购物车: ${product.name.substring(0, 15)}...`);
}
function removeFromCart(productId) {
cart = cart.filter(i => i.id !== productId);
updateCartUI();
}
function changeQty(productId, delta) {
const item = cart.find(i => i.id === productId);
if (!item) return;
item.qty += delta;
if (item.qty <= 0) removeFromCart(productId);
else updateCartUI();
}
function updateCartUI() {
const count = cart.reduce((s, i) => s + i.qty, 0);
const total = cart.reduce((s, i) => s + i.price * i.qty, 0);
const badge = document.getElementById('cartBadge');
badge.textContent = count;
badge.style.display = count > 0 ? 'flex' : 'none';
document.getElementById('cartCount').textContent = count;
document.getElementById('cartTotal').textContent = `$${total.toFixed(2)}`;
document.getElementById('checkoutBtn').disabled = count === 0;
const container = document.getElementById('cartItems');
if (cart.length === 0) {
container.innerHTML = `
<div class="cart-empty">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
<p>购物车是空的</p>
<p style="font-size:13px;margin-top:8px">快去挑选心仪的商品吧</p>
</div>`;
return;
}
container.innerHTML = cart.map(item => `
<div class="cart-item">
<div class="cart-item-img" style="background:${getGradient(item.category_id)};position:relative;overflow:hidden">${item.image ? `<img src="${item.image}" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" onerror="this.remove()">` : ''}${productIconSVG(item.category_id)}</div>
<div class="cart-item-info">
<div class="cart-item-name">${item.name}</div>
<div class="cart-item-price">$${item.price.toFixed(2)}</div>
<div class="cart-item-controls">
<button class="qty-btn" onclick="changeQty(${item.id}, -1)">-</button>
<span class="qty-value">${item.qty}</span>
<button class="qty-btn" onclick="changeQty(${item.id}, 1)">+</button>
<span class="cart-item-remove" onclick="removeFromCart(${item.id})">删除</span>
</div>
</div>
</div>
`).join('');
}
function toggleCart() {
document.getElementById('cartOverlay').classList.toggle('open');
document.getElementById('cartDrawer').classList.toggle('open');
}
function checkout() {
if (cart.length === 0) return;
const total = cart.reduce((s, i) => s + i.price * i.qty, 0);
showToast(`订单已提交! 合计 $${total.toFixed(2)}`);
cart = [];
updateCartUI();
toggleCart();
}
// ========== 工具函数 ==========
function formatSales(n) {
if (n >= 10000) return (n / 10000).toFixed(1) + '万';
return n.toString();
}
function showToast(msg) {
const toast = document.getElementById('toast');
document.getElementById('toastMsg').textContent = msg;
toast.classList.add('show');
clearTimeout(window._toastTimer);
window._toastTimer = setTimeout(() => toast.classList.remove('show'), 2500);
}
// ========== 初始化 ==========
async function init() {
await loadCategories();
await loadProducts();
updateCartUI();
}
init();
// ========== 3D 动态效果 & 鼠标光影(新增)==========
(function(){
var isTouch = window.matchMedia('(hover: none)').matches;
/* ----- 鼠标跟随光斑 ----- */
var cursorGlow = document.createElement('div');
cursorGlow.className = 'cursor-glow';
document.body.appendChild(cursorGlow);
if (!isTouch) {
var glowOn = false;
document.addEventListener('mousemove', function(e){
cursorGlow.style.left = e.clientX + 'px';
cursorGlow.style.top = e.clientY + 'px';
if (!glowOn) { cursorGlow.classList.add('active'); glowOn = true; }
});
document.addEventListener('mouseleave', function(){
cursorGlow.classList.remove('active'); glowOn = false;
});
}
/* ----- 产品卡片 3D 倾斜 + 表面高光 ----- */
if (!isTouch) {
var tiltRaf = null, tiltEvt = null;
function tiltUpdate(){
tiltRaf = null;
var e = tiltEvt;
if (!e) return;
var cards = document.querySelectorAll('.product-card');
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
var rect = card.getBoundingClientRect();
if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) continue;
var cx = rect.left + rect.width / 2;
var cy = rect.top + rect.height / 2;
var rx = ((e.clientY - cy) / rect.height) * -8;
var ry = ((e.clientX - cx) / rect.width) * 8;
card.style.transform = 'perspective(1000px) rotateX(' + rx + 'deg) rotateY(' + ry + 'deg) translateY(-4px)';
card.style.setProperty('--mx', ((e.clientX - rect.left) / rect.width * 100) + '%');
card.style.setProperty('--my', ((e.clientY - rect.top) / rect.height * 100) + '%');
}
}
document.addEventListener('mousemove', function(e){
tiltEvt = e;
if (tiltRaf === null) tiltRaf = requestAnimationFrame(tiltUpdate);
});
// 鼠标真正离开卡片时恢复(relatedTarget 仍在卡片内则保持倾斜,避免子元素间移动闪烁)
document.addEventListener('mouseout', function(e){
var t = e.target;
if (t && t.classList && t.classList.contains('product-card')) {
var r = e.relatedTarget;
if (r && (r === t || t.contains(r))) return;
t.style.transform = '';
}
});
}
/* ----- Hero 区域 3D 视差 ----- */
if (!isTouch) {
var heroContent = document.querySelector('.hero-content');
var heroRaf = null, heroEvt = null;
function heroUpdate(){
heroRaf = null;
var e = heroEvt;
if (!e || !heroContent) return;
var x = (e.clientX / window.innerWidth - 0.5) * 2;
var y = (e.clientY / window.innerHeight - 0.5) * 2;
heroContent.style.transform = 'translate(' + (x * 10) + 'px,' + (y * 10) + 'px)';
}
document.addEventListener('mousemove', function(e){
heroEvt = e;
if (heroRaf === null) heroRaf = requestAnimationFrame(heroUpdate);
});
}
})();
</script>
</body>
</html>