Add packaging design and 3D render
This commit is contained in:
857
3d.html
Normal file
857
3d.html
Normal file
@@ -0,0 +1,857 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>卡特光醒狮系列 - 包装3D渲染展示</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
background: linear-gradient(135deg, #0D0D0D 0%, #1a1a1a 50%, #0D0D0D 100%);
|
||||
color: #F5F5F0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#canvas-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ui-layer {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ui-interactive {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 24px 40px;
|
||||
background: linear-gradient(180deg, rgba(13,13,13,0.95) 0%, rgba(13,13,13,0.7) 60%, transparent 100%);
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 14px;
|
||||
color: #D4A853;
|
||||
letter-spacing: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #D4A853 0%, #F5F5F0 50%, #D4A853 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
color: rgba(245,245,240,0.6);
|
||||
margin-top: 4px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.product-selector {
|
||||
bottom: 40px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
padding: 20px;
|
||||
background: rgba(13,13,13,0.85);
|
||||
border: 1px solid rgba(212,168,83,0.3);
|
||||
border-radius: 16px;
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.product-btn {
|
||||
width: 80px;
|
||||
height: 100px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
background: rgba(255,255,255,0.05);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.product-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0.1;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.product-btn:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: rgba(212,168,83,0.5);
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.product-btn.active {
|
||||
border-color: #D4A853;
|
||||
background: rgba(212,168,83,0.15);
|
||||
}
|
||||
|
||||
.product-btn.active::before {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.product-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.view-controls {
|
||||
right: 40px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.view-btn {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 1px solid rgba(212,168,83,0.3);
|
||||
border-radius: 12px;
|
||||
background: rgba(13,13,13,0.8);
|
||||
color: #F5F5F0;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.view-btn:hover {
|
||||
border-color: #D4A853;
|
||||
background: rgba(212,168,83,0.15);
|
||||
}
|
||||
|
||||
.view-btn.active {
|
||||
background: linear-gradient(135deg, rgba(212,168,83,0.3), rgba(212,168,83,0.1));
|
||||
border-color: #D4A853;
|
||||
}
|
||||
|
||||
.view-btn svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
stroke: currentColor;
|
||||
fill: none;
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
top: 40px;
|
||||
right: 40px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 12px 24px;
|
||||
border: 1px solid rgba(212,168,83,0.4);
|
||||
border-radius: 8px;
|
||||
background: rgba(13,13,13,0.8);
|
||||
color: #D4A853;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: rgba(212,168,83,0.2);
|
||||
border-color: #D4A853;
|
||||
}
|
||||
|
||||
.info-panel {
|
||||
bottom: 180px;
|
||||
left: 40px;
|
||||
max-width: 280px;
|
||||
padding: 24px;
|
||||
background: rgba(13,13,13,0.85);
|
||||
border: 1px solid rgba(212,168,83,0.2);
|
||||
border-radius: 16px;
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.info-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #D4A853;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.info-desc {
|
||||
font-size: 13px;
|
||||
color: rgba(245,245,240,0.7);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.color-indicator {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.color-dot {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #0D0D0D;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 2px solid rgba(212,168,83,0.2);
|
||||
border-top-color: #D4A853;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 20px;
|
||||
color: #D4A853;
|
||||
font-size: 14px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.lens-glow {
|
||||
filter: drop-shadow(0 0 20px currentColor);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Loading Overlay -->
|
||||
<div class="loading-overlay" id="loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<div class="loading-text">正在加载3D渲染...</div>
|
||||
</div>
|
||||
|
||||
<!-- 3D Canvas -->
|
||||
<div id="canvas-container"></div>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="ui-layer header">
|
||||
<div class="logo">KART LIGHTING</div>
|
||||
<div class="title">醒狮系列透镜</div>
|
||||
<div class="subtitle">XINGSHI LENS COLLECTION</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="ui-layer action-buttons ui-interactive">
|
||||
<button class="action-btn" onclick="toggleBoxOpen()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M5 15l7-7 7 7"/>
|
||||
</svg>
|
||||
<span id="open-btn-text">打开包装</span>
|
||||
</button>
|
||||
<button class="action-btn" onclick="takeScreenshot()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="6" width="18" height="12" rx="2"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
截图
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Info Panel -->
|
||||
<div class="ui-layer info-panel" id="info-panel">
|
||||
<div class="info-title" id="product-title">醒狮·瞳</div>
|
||||
<div class="info-desc" id="product-desc">
|
||||
狮头设计,象征洞察与智慧。帝王金配色彰显尊贵气质,为驾驶者带来清晰明亮的视野体验。
|
||||
</div>
|
||||
<div class="color-indicator" id="color-indicator">
|
||||
<div class="color-dot" style="background: #FFD700;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View Controls -->
|
||||
<div class="ui-layer view-controls ui-interactive">
|
||||
<button class="view-btn active" onclick="setView('front')" title="正面视角">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
||||
<path d="M12 8v8M8 12h8"/>
|
||||
</svg>
|
||||
<span>正面</span>
|
||||
</button>
|
||||
<button class="view-btn" onclick="setView('angle')" title="45度视角">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M3 7l9-4 9 4v10l-9 4-9-4V7z"/>
|
||||
<path d="M12 3v18"/>
|
||||
</svg>
|
||||
<span>45°</span>
|
||||
</button>
|
||||
<button class="view-btn" onclick="setView('top')" title="顶面视角">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<rect x="4" y="4" width="16" height="16" rx="2"/>
|
||||
<circle cx="12" cy="12" r="4"/>
|
||||
</svg>
|
||||
<span>顶面</span>
|
||||
</button>
|
||||
<button class="view-btn" onclick="setView('free')" title="自由视角">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M12 2v4M12 18v4M2 12h4M18 12h4"/>
|
||||
</svg>
|
||||
<span>自由</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Product Selector -->
|
||||
<div class="ui-layer product-selector ui-interactive">
|
||||
<div class="product-btn active" onclick="selectProduct(0)" data-product="0">
|
||||
<div class="product-icon" style="background: linear-gradient(135deg, #FFD700, #D4A853); color: #0D0D0D;">瞳</div>
|
||||
<div class="product-name">醒狮·瞳</div>
|
||||
</div>
|
||||
<div class="product-btn" onclick="selectProduct(1)" data-product="1">
|
||||
<div class="product-icon" style="background: linear-gradient(135deg, #FF6B35, #C41E3A); color: #fff;">威</div>
|
||||
<div class="product-name">醒狮·威</div>
|
||||
</div>
|
||||
<div class="product-btn" onclick="selectProduct(2)" data-product="2">
|
||||
<div class="product-icon" style="background: linear-gradient(135deg, #8B7355, #5C4A3A); color: #fff;">鼓</div>
|
||||
<div class="product-name">醒狮·鼓</div>
|
||||
</div>
|
||||
<div class="product-btn" onclick="selectProduct(3)" data-product="3">
|
||||
<div class="product-icon" style="background: linear-gradient(135deg, #6B4EE6, #9B7BF7); color: #fff;">潮</div>
|
||||
<div class="product-name">醒狮·潮</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Product Data
|
||||
const products = [
|
||||
{
|
||||
name: '醒狮·瞳',
|
||||
subtitle: 'XINGSHI TONG',
|
||||
color: '#FFD700',
|
||||
accentColor: '#D4A853',
|
||||
description: '狮头设计,象征洞察与智慧。帝王金配色彰显尊贵气质,为驾驶者带来清晰明亮的视野体验。',
|
||||
pattern: 'lion_head'
|
||||
},
|
||||
{
|
||||
name: '醒狮·威',
|
||||
subtitle: 'XINGSHI WEI',
|
||||
color: '#FF6B35',
|
||||
accentColor: '#C41E3A',
|
||||
description: '狮尾设计,象征力量与威严。烈焰橙配色展现澎湃动力,为车辆注入强劲光芒。',
|
||||
pattern: 'lion_tail'
|
||||
},
|
||||
{
|
||||
name: '醒狮·鼓',
|
||||
subtitle: 'XINGSHI GU',
|
||||
color: '#8B7355',
|
||||
accentColor: '#D4A853',
|
||||
description: '鼓手设计,象征节奏与韵律。古铜褐配色传递沉稳底蕴,为行车带来和谐节奏。',
|
||||
pattern: 'drummer'
|
||||
},
|
||||
{
|
||||
name: '醒狮·潮',
|
||||
subtitle: 'XINGSHI CHAO',
|
||||
color: '#6B4EE6',
|
||||
accentColor: '#9B7BF7',
|
||||
description: '少年设计,象征活力与创新。电光紫配色诠释年轻态度,为爱车增添时尚魅力。',
|
||||
pattern: 'youth'
|
||||
}
|
||||
];
|
||||
|
||||
let currentProduct = 0;
|
||||
let isBoxOpen = false;
|
||||
let scene, camera, renderer, controls;
|
||||
let boxGroup, lidGroup, lensMesh;
|
||||
let animationId;
|
||||
|
||||
// Initialize Three.js
|
||||
function init() {
|
||||
const container = document.getElementById('canvas-container');
|
||||
|
||||
// Scene
|
||||
scene = new THREE.Scene();
|
||||
scene.fog = new THREE.FogExp2(0x0D0D0D, 0.02);
|
||||
|
||||
// Camera
|
||||
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
camera.position.set(0, 0, 12);
|
||||
|
||||
// Renderer
|
||||
renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
preserveDrawingBuffer: true
|
||||
});
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
renderer.shadowMap.enabled = true;
|
||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
renderer.toneMappingExposure = 1.2;
|
||||
container.appendChild(renderer.domElement);
|
||||
|
||||
// Controls
|
||||
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
controls.dampingFactor = 0.05;
|
||||
controls.minDistance = 5;
|
||||
controls.maxDistance = 20;
|
||||
controls.maxPolarAngle = Math.PI * 0.8;
|
||||
|
||||
// Lighting
|
||||
setupLighting();
|
||||
|
||||
// Create Box
|
||||
createPackagingBox();
|
||||
|
||||
// Start Animation
|
||||
animate();
|
||||
|
||||
// Hide Loading
|
||||
setTimeout(() => {
|
||||
document.getElementById('loading').classList.add('hidden');
|
||||
}, 1000);
|
||||
|
||||
// Resize Handler
|
||||
window.addEventListener('resize', onWindowResize);
|
||||
}
|
||||
|
||||
function setupLighting() {
|
||||
// Ambient
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
|
||||
scene.add(ambientLight);
|
||||
|
||||
// Key Light (warm gold)
|
||||
const keyLight = new THREE.SpotLight(0xD4A853, 1.5);
|
||||
keyLight.position.set(8, 10, 8);
|
||||
keyLight.castShadow = true;
|
||||
keyLight.shadow.mapSize.width = 2048;
|
||||
keyLight.shadow.mapSize.height = 2048;
|
||||
keyLight.shadow.bias = -0.0001;
|
||||
scene.add(keyLight);
|
||||
|
||||
// Fill Light (cool)
|
||||
const fillLight = new THREE.DirectionalLight(0x6B4EE6, 0.4);
|
||||
fillLight.position.set(-8, 5, 5);
|
||||
scene.add(fillLight);
|
||||
|
||||
// Rim Light
|
||||
const rimLight = new THREE.SpotLight(0xFFD700, 0.8);
|
||||
rimLight.position.set(0, 8, -8);
|
||||
scene.add(rimLight);
|
||||
|
||||
// Bottom reflection
|
||||
const bottomLight = new THREE.DirectionalLight(0xffffff, 0.2);
|
||||
bottomLight.position.set(0, -5, 0);
|
||||
scene.add(bottomLight);
|
||||
}
|
||||
|
||||
function createPackagingBox() {
|
||||
if (boxGroup) scene.remove(boxGroup);
|
||||
|
||||
boxGroup = new THREE.Group();
|
||||
|
||||
const product = products[currentProduct];
|
||||
const boxWidth = 5.3;
|
||||
const boxHeight = 4.1;
|
||||
const boxDepth = 2.0;
|
||||
|
||||
// Black card paper material
|
||||
const boxMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0x0D0D0D,
|
||||
roughness: 0.9,
|
||||
metalness: 0.1,
|
||||
bumpScale: 0.02
|
||||
});
|
||||
|
||||
// Gold foil material for patterns
|
||||
const goldMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0xD4A853,
|
||||
roughness: 0.2,
|
||||
metalness: 0.9,
|
||||
emissive: 0xD4A853,
|
||||
emissiveIntensity: 0.1
|
||||
});
|
||||
|
||||
// Product accent material
|
||||
const accentMaterial = new THREE.MeshStandardMaterial({
|
||||
color: new THREE.Color(product.color),
|
||||
roughness: 0.3,
|
||||
metalness: 0.7,
|
||||
emissive: new THREE.Color(product.color),
|
||||
emissiveIntensity: 0.2
|
||||
});
|
||||
|
||||
// Main box body
|
||||
const bodyGeometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
|
||||
const bodyMesh = new THREE.Mesh(bodyGeometry, boxMaterial);
|
||||
bodyMesh.castShadow = true;
|
||||
bodyMesh.receiveShadow = true;
|
||||
boxGroup.add(bodyMesh);
|
||||
|
||||
// Front face design - decorative frame
|
||||
const frameGeometry = new THREE.PlaneGeometry(boxWidth * 0.85, boxHeight * 0.85);
|
||||
const frameMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0xD4A853,
|
||||
roughness: 0.3,
|
||||
metalness: 0.8,
|
||||
transparent: true,
|
||||
opacity: 0.9,
|
||||
side: THREE.DoubleSide
|
||||
});
|
||||
const frameMesh = new THREE.Mesh(frameGeometry, frameMaterial);
|
||||
frameMesh.position.set(0, 0, boxDepth/2 + 0.01);
|
||||
boxGroup.add(frameMesh);
|
||||
|
||||
// Inner frame
|
||||
const innerFrameGeometry = new THREE.PlaneGeometry(boxWidth * 0.75, boxHeight * 0.75);
|
||||
const innerFrameMesh = new THREE.Mesh(innerFrameGeometry, boxMaterial);
|
||||
innerFrameMesh.position.set(0, 0, boxDepth/2 + 0.02);
|
||||
boxGroup.add(innerFrameMesh);
|
||||
|
||||
// Lion pattern area
|
||||
const patternGeometry = new THREE.PlaneGeometry(boxWidth * 0.5, boxWidth * 0.5);
|
||||
const patternMaterial = new THREE.MeshStandardMaterial({
|
||||
color: new THREE.Color(product.color),
|
||||
roughness: 0.4,
|
||||
metalness: 0.6,
|
||||
emissive: new THREE.Color(product.color),
|
||||
emissiveIntensity: 0.15
|
||||
});
|
||||
const patternMesh = new THREE.Mesh(patternGeometry, patternMaterial);
|
||||
patternMesh.position.set(0, 0.3, boxDepth/2 + 0.03);
|
||||
boxGroup.add(patternMesh);
|
||||
|
||||
// Product name text area
|
||||
const nameBgGeometry = new THREE.PlaneGeometry(boxWidth * 0.6, 0.8);
|
||||
const nameBgMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0x0D0D0D,
|
||||
roughness: 0.9,
|
||||
metalness: 0.1
|
||||
});
|
||||
const nameBgMesh = new THREE.Mesh(nameBgGeometry, nameBgMaterial);
|
||||
nameBgMesh.position.set(0, -1.2, boxDepth/2 + 0.03);
|
||||
boxGroup.add(nameBgMesh);
|
||||
|
||||
// Brand logo strip
|
||||
const logoStripGeometry = new THREE.PlaneGeometry(boxWidth, 0.4);
|
||||
const logoStripMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0xD4A853,
|
||||
roughness: 0.2,
|
||||
metalness: 0.9
|
||||
});
|
||||
const logoStripMesh = new THREE.Mesh(logoStripGeometry, logoStripMaterial);
|
||||
logoStripMesh.position.set(0, 1.6, boxDepth/2 + 0.03);
|
||||
boxGroup.add(logoStripMesh);
|
||||
|
||||
// Top surface design
|
||||
const topDesignGeometry = new THREE.PlaneGeometry(boxWidth * 0.7, boxDepth * 0.7);
|
||||
const topDesignMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0xD4A853,
|
||||
roughness: 0.3,
|
||||
metalness: 0.8
|
||||
});
|
||||
const topDesignMesh = new THREE.Mesh(topDesignGeometry, topDesignMaterial);
|
||||
topDesignMesh.rotation.x = -Math.PI / 2;
|
||||
topDesignMesh.position.set(0, boxHeight/2 + 0.01, 0);
|
||||
boxGroup.add(topDesignMesh);
|
||||
|
||||
// Side decorations
|
||||
const sideDecoGeometry = new THREE.PlaneGeometry(boxDepth * 0.6, boxHeight * 0.6);
|
||||
const sideDecoMaterial = new THREE.MeshStandardMaterial({
|
||||
color: new THREE.Color(product.accentColor),
|
||||
roughness: 0.4,
|
||||
metalness: 0.6
|
||||
});
|
||||
|
||||
// Left side
|
||||
const leftDeco = new THREE.Mesh(sideDecoGeometry, sideDecoMaterial);
|
||||
leftDeco.rotation.y = -Math.PI / 2;
|
||||
leftDeco.position.set(-boxWidth/2 - 0.01, 0, 0);
|
||||
boxGroup.add(leftDeco);
|
||||
|
||||
// Right side
|
||||
const rightDeco = new THREE.Mesh(sideDecoGeometry, sideDecoMaterial);
|
||||
rightDeco.rotation.y = Math.PI / 2;
|
||||
rightDeco.position.set(boxWidth/2 + 0.01, 0, 0);
|
||||
boxGroup.add(rightDeco);
|
||||
|
||||
// Lid group (for opening animation)
|
||||
lidGroup = new THREE.Group();
|
||||
lidGroup.position.set(0, boxHeight/2, -boxDepth/2);
|
||||
|
||||
// Lid mesh
|
||||
const lidGeometry = new THREE.BoxGeometry(boxWidth, 0.15, boxDepth);
|
||||
const lidMesh = new THREE.Mesh(lidGeometry, boxMaterial);
|
||||
lidMesh.position.set(0, 0.075, boxDepth/2);
|
||||
lidMesh.castShadow = true;
|
||||
lidGroup.add(lidMesh);
|
||||
|
||||
// Lid top design
|
||||
const lidTopGeometry = new THREE.PlaneGeometry(boxWidth * 0.8, boxDepth * 0.8);
|
||||
const lidTopMesh = new THREE.Mesh(lidTopGeometry, goldMaterial);
|
||||
lidTopMesh.rotation.x = -Math.PI / 2;
|
||||
lidTopMesh.position.set(0, 0.16, boxDepth/2);
|
||||
lidGroup.add(lidTopMesh);
|
||||
|
||||
boxGroup.add(lidGroup);
|
||||
|
||||
// Lens product (inside)
|
||||
createLensProduct();
|
||||
|
||||
scene.add(boxGroup);
|
||||
}
|
||||
|
||||
function createLensProduct() {
|
||||
const product = products[currentProduct];
|
||||
|
||||
// Lens body
|
||||
const lensGeometry = new THREE.CylinderGeometry(1.2, 1.2, 0.4, 32);
|
||||
const lensMaterial = new THREE.MeshPhysicalMaterial({
|
||||
color: 0x1a1a1a,
|
||||
metalness: 0.8,
|
||||
roughness: 0.2,
|
||||
clearcoat: 1.0,
|
||||
clearcoatRoughness: 0.1
|
||||
});
|
||||
lensMesh = new THREE.Mesh(lensGeometry, lensMaterial);
|
||||
lensMesh.position.set(0, -0.5, 0);
|
||||
lensMesh.rotation.x = Math.PI / 2;
|
||||
lensMesh.visible = false;
|
||||
boxGroup.add(lensMesh);
|
||||
|
||||
// Lens glass
|
||||
const glassGeometry = new THREE.CylinderGeometry(1.0, 1.0, 0.1, 32);
|
||||
const glassMaterial = new THREE.MeshPhysicalMaterial({
|
||||
color: new THREE.Color(product.color),
|
||||
metalness: 0.1,
|
||||
roughness: 0.0,
|
||||
transmission: 0.9,
|
||||
thickness: 0.5,
|
||||
emissive: new THREE.Color(product.color),
|
||||
emissiveIntensity: 0.3
|
||||
});
|
||||
const glassMesh = new THREE.Mesh(glassGeometry, glassMaterial);
|
||||
glassMesh.position.set(0, -0.5, 0.25);
|
||||
glassMesh.rotation.x = Math.PI / 2;
|
||||
glassMesh.visible = false;
|
||||
glassMesh.name = 'lensGlass';
|
||||
boxGroup.add(glassMesh);
|
||||
|
||||
// LED chips ring
|
||||
const ledGeometry = new THREE.RingGeometry(0.6, 0.9, 32);
|
||||
const ledMaterial = new THREE.MeshBasicMaterial({
|
||||
color: new THREE.Color(product.color),
|
||||
side: THREE.DoubleSide
|
||||
});
|
||||
const ledMesh = new THREE.Mesh(ledGeometry, ledMaterial);
|
||||
ledMesh.position.set(0, -0.5, 0.26);
|
||||
ledMesh.visible = false;
|
||||
ledMesh.name = 'ledRing';
|
||||
boxGroup.add(ledMesh);
|
||||
}
|
||||
|
||||
function selectProduct(index) {
|
||||
currentProduct = index;
|
||||
|
||||
// Update UI
|
||||
document.querySelectorAll('.product-btn').forEach((btn, i) => {
|
||||
btn.classList.toggle('active', i === index);
|
||||
});
|
||||
|
||||
const product = products[index];
|
||||
document.getElementById('product-title').textContent = product.name;
|
||||
document.getElementById('product-desc').textContent = product.description;
|
||||
document.getElementById('color-indicator').innerHTML =
|
||||
`<div class="color-dot" style="background: ${product.color};"></div>`;
|
||||
|
||||
// Recreate box with new product
|
||||
createPackagingBox();
|
||||
|
||||
// Maintain open state if was open
|
||||
if (isBoxOpen) {
|
||||
setTimeout(() => toggleBoxOpen(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
function setView(view) {
|
||||
// Update UI
|
||||
document.querySelectorAll('.view-btn').forEach(btn => btn.classList.remove('active'));
|
||||
event.target.closest('.view-btn').classList.add('active');
|
||||
|
||||
const duration = 1000;
|
||||
const startPos = camera.position.clone();
|
||||
let targetPos;
|
||||
|
||||
switch(view) {
|
||||
case 'front':
|
||||
targetPos = new THREE.Vector3(0, 0, 12);
|
||||
break;
|
||||
case 'angle':
|
||||
targetPos = new THREE.Vector3(8, 6, 8);
|
||||
break;
|
||||
case 'top':
|
||||
targetPos = new THREE.Vector3(0, 12, 0.1);
|
||||
break;
|
||||
case 'free':
|
||||
targetPos = new THREE.Vector3(6, 4, 10);
|
||||
break;
|
||||
}
|
||||
|
||||
// Animate camera
|
||||
const startTime = Date.now();
|
||||
function animateCamera() {
|
||||
const elapsed = Date.now() - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
const ease = 1 - Math.pow(1 - progress, 3);
|
||||
|
||||
camera.position.lerpVectors(startPos, targetPos, ease);
|
||||
camera.lookAt(0, 0, 0);
|
||||
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(animateCamera);
|
||||
}
|
||||
}
|
||||
animateCamera();
|
||||
}
|
||||
|
||||
function toggleBoxOpen() {
|
||||
isBoxOpen = !isBoxOpen;
|
||||
document.getElementById('open-btn-text').textContent = isBoxOpen ? '关闭包装' : '打开包装';
|
||||
|
||||
const targetRotation = isBoxOpen ? -Math.PI * 0.7 : 0;
|
||||
const startRotation = lidGroup.rotation.x;
|
||||
const duration = 800;
|
||||
const startTime = Date.now();
|
||||
|
||||
// Show/hide lens
|
||||
const lensParts = ['lensGlass', 'ledRing'];
|
||||
lensParts.forEach(name => {
|
||||
const part = boxGroup.getObjectByName(name);
|
||||
if (part) part.visible = isBoxOpen;
|
||||
});
|
||||
if (lensMesh) lensMesh.visible = isBoxOpen;
|
||||
|
||||
function animateLid() {
|
||||
const elapsed = Date.now() - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
const ease = 1 - Math.pow(1 - progress, 3);
|
||||
|
||||
lidGroup.rotation.x = startRotation + (targetRotation - startRotation) * ease;
|
||||
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(animateLid);
|
||||
}
|
||||
}
|
||||
animateLid();
|
||||
}
|
||||
|
||||
function takeScreenshot() {
|
||||
renderer.render(scene, camera);
|
||||
const dataURL = renderer.domElement.toDataURL('image/png');
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.download = `醒狮包装_${products[currentProduct].name}_${new Date().getTime()}.png`;
|
||||
link.href = dataURL;
|
||||
link.click();
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
animationId = requestAnimationFrame(animate);
|
||||
controls.update();
|
||||
|
||||
// Subtle floating animation
|
||||
if (boxGroup) {
|
||||
boxGroup.position.y = Math.sin(Date.now() * 0.001) * 0.1;
|
||||
boxGroup.rotation.y += 0.001;
|
||||
}
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
// Initialize
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1246
index.html
Normal file
1246
index.html
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user