Files
zhengte.babylonjs-sdk/examples/demo-global.html.backup
2026-05-22 13:33:13 +08:00

912 lines
35 KiB
Plaintext
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>3D Model Showcase SDK - TS</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
overflow: hidden;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
}
#app {
width: 100vw;
height: 100vh;
display: flex;
position: relative;
}
#canvas-container {
flex: 1;
position: relative;
}
#renderDom {
width: 100%;
height: 100%;
display: block;
}
#config-panel {
width: 320px;
background: rgba(30, 30, 45, 0.95);
backdrop-filter: blur(10px);
overflow-y: auto;
padding: 20px;
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.3);
}
#config-panel::-webkit-scrollbar {
width: 6px;
}
#config-panel::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
}
#config-panel::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 3px;
}
.config-title {
color: #fff;
font-size: 18px;
font-weight: bold;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
}
.click-info {
background: rgba(76, 175, 80, 0.2);
border: 1px solid rgba(76, 175, 80, 0.5);
border-radius: 8px;
padding: 12px;
margin-bottom: 15px;
color: #fff;
font-size: 13px;
line-height: 1.6;
}
.click-info-title {
font-weight: bold;
color: #4caf50;
margin-bottom: 8px;
font-size: 14px;
}
.click-info-item {
margin-bottom: 4px;
display: flex;
gap: 8px;
}
.click-info-label {
color: rgba(255, 255, 255, 0.7);
min-width: 70px;
}
.click-info-value {
color: #fff;
word-break: break-all;
}
.config-category {
margin-bottom: 15px;
border-radius: 8px;
overflow: hidden;
background: rgba(255, 255, 255, 0.05);
}
.category-header {
padding: 12px 15px;
background: rgba(255, 255, 255, 0.1);
color: #fff;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
transition: background 0.3s;
user-select: none;
}
.category-header:hover {
background: rgba(255, 255, 255, 0.15);
}
.category-header.active {
background: rgba(76, 175, 80, 0.3);
}
.category-title {
font-size: 14px;
font-weight: 600;
}
.category-arrow {
transition: transform 0.3s;
font-size: 12px;
}
.category-arrow.expanded {
transform: rotate(180deg);
}
.category-content {
display: grid;
grid-template-rows: 0fr;
overflow: hidden;
transition: grid-template-rows 0.3s ease, padding 0.3s ease;
padding: 0 15px;
}
.category-content.expanded {
grid-template-rows: 1fr;
padding: 15px;
}
.category-content>* {
min-height: 0;
}
.option-item {
margin-bottom: 12px;
}
.option-label {
color: rgba(255, 255, 255, 0.8);
font-size: 13px;
margin-bottom: 8px;
display: block;
}
.option-group {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.option-btn {
padding: 8px 16px;
border: 1px solid rgba(255, 255, 255, 0.3);
background: rgba(255, 255, 255, 0.05);
color: #fff;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s;
}
.option-btn:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.5);
}
.option-btn.selected {
background: rgba(76, 175, 80, 0.6);
border-color: #4CAF50;
}
.option-checkbox {
display: flex;
align-items: center;
padding: 8px;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s;
}
.option-checkbox:hover {
background: rgba(255, 255, 255, 0.05);
}
.option-checkbox input[type="checkbox"] {
margin-right: 8px;
cursor: pointer;
}
.option-checkbox label {
color: rgba(255, 255, 255, 0.8);
font-size: 12px;
cursor: pointer;
}
/* 进度条样式 */
#progress-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
max-width: 500px;
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
padding: 10px;
z-index: 1000;
}
#progress-bar {
width: 0%;
height: 10px;
background: linear-gradient(90deg, #4CAF50, #45a049);
border-radius: 5px;
transition: width 0.1s ease;
}
#progress-text {
color: white;
text-align: center;
margin-top: 5px;
font-size: 14px;
}
</style>
</head>
<body>
<div id="app">
<!-- 画布区域 -->
<div id="canvas-container">
<canvas id="renderDom"></canvas>
<div id="progress-container" style="display: none;">
<div id="progress-bar"></div>
<div id="progress-text">0%</div>
</div>
<!-- 生成放置区域按钮 -->
<button id="dropzone-btn" style="
position: absolute;
top: 20px;
left: 20px;
padding: 10px 20px;
background: #21c7ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
z-index: 100;
">生成放置区域</button>
</div>
<!-- 配置面板 -->
<div id="config-panel">
<div class="config-title">选装选配</div>
<!-- 点击信息显示区域 -->
<div id="click-info" class="click-info" style="display: none;">
<div class="click-info-title">点击信息</div>
<div id="click-info-content"></div>
</div>
<!-- 棚子尺寸 -->
<div class="config-category">
<div class="category-header" data-category="size">
<span class="category-title">棚子尺寸</span>
<span class="category-arrow">▼</span>
</div>
<div class="category-content">
<div class="option-group">
<button class="option-btn" data-option="size-1">3*3</button>
<button class="option-btn" data-option="size-2">3x6</button>
<button class="option-btn" data-option="size-3">10x13EM星空篷</button>
<button class="option-btn" data-option="size-4">全铁3x6</button>
<button class="option-btn" data-option="size-1">10x12</button>
<button class="option-btn" data-option="size-2">10x10星空篷</button>
<button class="option-btn" data-option="size-3">10x13星空篷</button>
<button class="option-btn" data-option="size-4">10x20星空篷</button>
</div>
</div>
</div>
<!-- 棚子类型 -->
<div class="config-category">
<div class="category-header" data-category="type">
<span class="category-title">棚子类型</span>
<span class="category-arrow">▼</span>
</div>
<div class="category-content">
<div class="option-group">
<button class="option-btn" data-option="type-1">平顶</button>
<button class="option-btn" data-option="type-2">尖顶</button>
<button class="option-btn" data-option="type-3">弧形</button>
<button class="option-btn" data-option="type-4">异形</button>
</div>
</div>
</div>
<!-- 百叶 (单选) -->
<div class="config-category">
<div class="category-header" data-category="louver">
<span class="category-title">百叶</span>
<span class="category-arrow">▼</span>
</div>
<div class="category-content">
<div class="option-group">
<button class="option-btn" data-option="louver-1">SPFPDS13FTW</button>
<button class="option-btn" data-option="louver-2">SPFPDS13FTC</button>
<button class="option-btn" data-option="louver-3">3m下拉帘</button>
<button class="option-btn" data-option="louver-4">SPFSW13FTW</button>
<button class="option-btn" data-option="louver-4">SPFSW10FTW</button>
<button class="option-btn" data-option="louver-4">SPFPDS10FTC</button>
<button class="option-btn" data-option="louver-4">SPFPDS10FTW</button>
</div>
</div>
</div>
<!-- 配色 -->
<div class="config-category">
<div class="category-header" data-category="color">
<span class="category-title">配色</span>
<span class="category-arrow">▼</span>
</div>
<div class="category-content">
<div class="option-group">
13
<button class="option-btn" data-option="color-1">SPF111S1013W</button>
<button class="option-btn" data-option="color-2">SPF111S1013C</button>
<button class="option-btn" data-option="color-3">SPF111S1013TA</button>
10
<button class="option-btn" data-option="color-1">SPF111S1010W</button>
<button class="option-btn" data-option="color-2">SPF111S1010C</button>
<button class="option-btn" data-option="color-3">SPF111S1010TA</button>
</div>
</div>
</div>
<button id="hotspot-btn">生成热点</button>
<button id="prevent-btn">生成防止区域</button>
</div>
</div>
<!-- 模型信息框用于2D转3D显示 -->
<div id="model-info-box" style="display: none;">
<div style="
background: rgba(255, 255, 255, 0.95);
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
font-size: 14px;
color: #333;
min-width: 200px;
">
<div style="font-weight: bold; margin-bottom: 8px; color: #4CAF50;">模型信息</div>
<div id="info-name" style="margin-bottom: 5px;">名称: -</div>
<div id="info-position" style="margin-bottom: 10px; font-size: 12px; color: #666;">坐标: -</div>
<!-- 颜色按钮 -->
<div id="color-buttons" style="display: none; gap: 8px; margin-bottom: 8px;">
<button id="color-btn-1" style="
flex: 1;
padding: 8px;
background: #FFFFFF;
color: #333;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
">白色</button>
<button id="color-btn-2" style="
flex: 1;
padding: 8px;
background: #000000;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
">黑色</button>
</div>
<!-- 旋转按钮 -->
<div id="rotation-buttons" style="display: none; gap: 8px; margin-bottom: 8px;">
<button id="rotation-btn-90" style="
flex: 1;
padding: 8px;
background: #2196F3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
">旋转90°</button>
<button id="rotation-btn-180" style="
flex: 1;
padding: 8px;
background: #2196F3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
">旋转180°</button>
</div>
<div style="display: flex; gap: 8px;">
<button id="remove-model-btn" style="
flex: 1;
padding: 8px;
background: #f44336;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
">移除</button>
<button id="close-info-btn" style="
flex: 1;
padding: 8px;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
">关闭</button>
</div>
</div>
</div>
<!-- 全局脚本方式引入 SDK -->
<script src="https://sdk.zguiy.com/zt/assets/index.global.js"></script>
<!-- 业务逻辑脚本 -->
<script src="./app-global.js"></script>
<script>
// 从全局对象获取 SDK kernel
var kernel = window.faceSDK && window.faceSDK.kernel;
if (!kernel) {
console.error('faceSDK kernel 不可用,请确认 index.global.js 已正确加载');
alert('SDK 加载失败,请检查网络连接');
} else {
// 注入 kernel 实例到业务逻辑
window.AppLogic.initApp(kernel);
(async function () {
await window.AppLogic.init();
await window.AppLogic.getAutoLoadModelList();
kernel.on('model:load:progress', (data) => {
console.log('模型加载事件', data);
});
kernel.on('model:loaded', (data) => {
console.log('模型加载完成', data);
// 隐藏进度条
const progressContainer = document.getElementById('progress-container');
if (progressContainer) {
progressContainer.style.display = 'none';
}
});
kernel.on('all:ready', (data) => {
console.log('所有模块加载完,', data);
kernel.material.apply({
target: 'Material__2',
attribute: 'alpha',
value: 0.5,
});
});
// ========== UI 交互逻辑 ==========
// 折叠面板切换
document.querySelectorAll('.category-header').forEach(function (header) {
header.addEventListener('click', function () {
var content = this.nextElementSibling;
var arrow = this.querySelector('.category-arrow');
// 切换展开/收起状态
content.classList.toggle('expanded');
arrow.classList.toggle('expanded');
this.classList.toggle('active');
});
});
var sku = ""
// 单选按钮逻辑
document.querySelectorAll('.option-btn').forEach(function (btn) {
btn.addEventListener('click', async function () {
var optionGroup = this.parentElement;
var category = this.closest('.config-category');
var categoryName = category.querySelector('.category-header').dataset.category;
// 同一组内取消其他选中状态
optionGroup.querySelectorAll('.option-btn').forEach(function (b) {
b.classList.remove('selected');
});
// 选中当前按钮
this.classList.add('selected');
// 触发自定义事件
var event = new CustomEvent('config:change', {
detail: {
category: categoryName,
value: this.dataset.option,
text: this.textContent
}
});
document.dispatchEvent(event);
console.log('配置变更:', {
category: categoryName,
value: this.dataset.option,
text: this.textContent
});
var currentText = this.textContent;
sku = currentText;
await window.AppLogic.getProductConfig(currentText)
});
});
document.querySelector('#hotspot-btn').addEventListener('click', async function () {
await window.AppLogic.getHotspot();
})
// 监听热点点击事件
window.addEventListener('hotspot:click', function (event) {
console.log('热点被点击:', event.detail);
var id = event.detail.id;
var name = event.detail.name;
var payload = event.detail.payload;
var clickInfoDiv = document.getElementById('click-info');
var clickInfoContent = document.getElementById('click-info-content');
var html = '<div class="click-info-item">' +
'<span class="click-info-label">类型:</span>' +
'<span class="click-info-value">热点</span>' +
'</div>' +
'<div class="click-info-item">' +
'<span class="click-info-label">名称:</span>' +
'<span class="click-info-value">' + name + '</span>' +
'</div>';
if (payload && payload.skus && payload.skus.length > 0) {
html += '<div class="click-info-item">' +
'<span class="click-info-label">关联SKU:</span>' +
'<span class="click-info-value">' + payload.skus.join(', ') + '</span>' +
'</div>';
} else {
html += '<div class="click-info-item">' +
'<span class="click-info-label">关联SKU:</span>' +
'<span class="click-info-value">无</span>' +
'</div>';
}
clickInfoContent.innerHTML = html;
clickInfoDiv.style.display = 'block';
});
// 监听模型点击事件
window.addEventListener('model:click', function (event) {
console.log('模型被点击:', event.detail);
var meshName = event.detail.meshName;
var materialName = event.detail.materialName;
var modelControlType = event.detail.modelControlType;
var clickInfoDiv = document.getElementById('click-info');
var clickInfoContent = document.getElementById('click-info-content');
var html = '<div class="click-info-item">' +
'<span class="click-info-label">类型:</span>' +
'<span class="click-info-value">模型</span>' +
'</div>' +
'<div class="click-info-item">' +
'<span class="click-info-label">网格名称:</span>' +
'<span class="click-info-value">' + meshName + '</span>' +
'</div>';
if (materialName) {
html += '<div class="click-info-item">' +
'<span class="click-info-label">材质名称:</span>' +
'<span class="click-info-value">' + materialName + '</span>' +
'</div>';
}
if (modelControlType) {
html += '<div class="click-info-item">' +
'<span class="click-info-label">控制类型:</span>' +
'<span class="click-info-value">' + modelControlType + '</span>' +
'</div>';
}
clickInfoContent.innerHTML = html;
clickInfoDiv.style.display = 'block';
});
// 多选复选框逻辑
document.querySelectorAll('.option-checkbox input[type="checkbox"]').forEach(function (checkbox) {
checkbox.addEventListener('change', async function () {
var category = this.closest('.config-category');
var categoryName = category.querySelector('.category-header').dataset.category;
var optionGroup = this.closest('.option-group');
var checked = this.checked;
// 获取当前组所有选中的值
var selectedValues = Array.from(
optionGroup.querySelectorAll('input[type="checkbox"]:checked')
).map(function (cb) {
return {
value: cb.dataset.option,
text: cb.nextElementSibling.textContent
};
});
// 触发自定义事件
var event = new CustomEvent('config:change', {
detail: {
category: categoryName,
values: selectedValues,
checked: this.checked,
currentValue: this.dataset.option
}
});
document.dispatchEvent(event);
console.log('配置变更(多选):', {
category: categoryName,
selectedValues: selectedValues,
checked: this.checked,
currentValue: this.dataset.option
});
});
});
// 监听配置变更事件(供外部使用)
document.addEventListener('config:change', function (e) {
// 这里可以根据配置变更来操作 3D 模型
// 例如:
// if (e.detail.category === 'size') {
// kernel.model.replace({ modelId: 'shed', modelUrl: `/models/shed-${e.detail.value}.glb`, modelControlType: 'rotation' });
// }
// if (e.detail.category === 'color') {
// kernel.material.apply({
// target: 'ShedMaterial',
// attribute: 'baseColor',
// value: getColorValue(e.detail.value)
// });
// }
});
// ========== 模型信息框按钮事件 ==========
// 关闭按钮事件
document.getElementById('close-info-btn').addEventListener('click', function () {
kernel.domTo3D.detach('model-info');
});
// 白色按钮事件
document.getElementById('color-btn-1').addEventListener('click', function () {
var materialName = window.getCurrentMaterialName();
if (materialName) {
console.log('切换为白色,材质名:', materialName);
kernel.material.apply({
target: materialName,
albedoColor: '#FFFFFF',
});
} else {
console.log('没有选中材质');
}
});
// 黑色按钮事件
document.getElementById('color-btn-2').addEventListener('click', function () {
var materialName = window.getCurrentMaterialName();
if (materialName) {
console.log('切换为黑色,材质名:', materialName);
kernel.material.apply({
target: materialName,
albedoColor: '#000000',
});
} else {
console.log('没有选中材质');
}
});
// 旋转90度按钮事件
document.getElementById('rotation-btn-90').addEventListener('click', function () {
var pickedMesh = window.getCurrentPickedMesh();
if (pickedMesh) {
var modelName = kernel.model.findModelNameByMesh?.(pickedMesh);
if (modelName) {
console.log('旋转90度模型名:', modelName);
kernel.transform.rotation({
modelId: modelName,
vector3: { x: 0, y: 90, z: 0 }
});
} else {
console.log('未找到模型名称');
}
} else {
console.log('没有选中的网格');
}
});
// 旋转180度按钮事件
document.getElementById('rotation-btn-180').addEventListener('click', function () {
var pickedMesh = window.getCurrentPickedMesh();
if (pickedMesh) {
var modelName = kernel.model.findModelNameByMesh?.(pickedMesh);
if (modelName) {
console.log('旋转180度模型名:', modelName);
kernel.transform.rotation({
modelId: modelName,
vector3: { x: 0, y: 30, z: 0 }
});
} else {
console.log('未找到模型名称');
}
} else {
console.log('没有选中的网格');
}
});
// 移除按钮事件
document.getElementById('remove-model-btn').addEventListener('click', () => {
const pickedMesh = window.getCurrentPickedMesh();
if (pickedMesh) {
const meshName = pickedMesh.name;
const modelName = kernel.model.findModelNameByMesh(pickedMesh);
const success = kernel.model.removeByName(modelName);
if (success) {
console.log('模型已移除');
// 关闭信息框
kernel.domTo3D.detach('model-info');
} else {
console.log('移除失败:未找到该网格所属的模型');
}
} else {
console.log('没有选中的网格');
}
});
// 生成放置区域按钮事件
var dropZoneVisible = false;
document.getElementById('dropzone-btn').addEventListener('click', function () {
if (!dropZoneVisible) {
// 更新按钮文字
document.getElementById('dropzone-btn').textContent = '隐藏放置区域';
console.log('已生成并显示放置区域');
} else {
// 隐藏放置区域
kernel.dropZone.hideAll();
dropZoneVisible = false;
// 更新按钮文字
document.getElementById('dropzone-btn').textContent = '生成放置区域';
console.log('已隐藏放置区域');
}
});
// 监听放置区域点击事件
kernel.on('dropzone:click', async function (dropzone_data) {
window.AppLogic.getEvent(dropzone_data, sku)
});
// 存储当前选中的材质名和网格
var currentMaterialName = '';
var currentPickedMesh = null;
kernel.on('model:click', function (data) {
console.log('模型点击事件', data);
console.log('模型控制类型:', data.modelControlType);
switch (data.modelControlType) {
case "color":
// DOM 2D转3D 示例:点击模型时显示信息框
if (data.pickedMesh && data.pickedPoint) {
var meshName = data.pickedMesh.name;
var position = data.pickedPoint; // 使用点击位置的坐标
currentMaterialName = data.materialName || ''; // 保存材质名
currentPickedMesh = data.pickedMesh; // 保存网格对象
// 获取已创建的DOM元素
var infoDiv = document.getElementById('model-info-box');
// 更新信息内容
document.getElementById('info-name').textContent = '名称: ' + meshName;
document.getElementById('info-position').textContent = '坐标: [' + position.x.toFixed(2) + ', ' + position.y.toFixed(2) + ', ' + position.z.toFixed(2) + ']';
// 显示颜色按钮,隐藏旋转按钮
document.getElementById('color-buttons').style.display = 'flex';
document.getElementById('rotation-buttons').style.display = 'none';
// 将DOM附加到点击的3D坐标会自动显示
kernel.domTo3D.attach('model-info', infoDiv, [position.x, position.y, position.z], { x: -2, y: -2 });
}
break;
case "rotation":
// 显示旋转控制UI
if (data.pickedMesh && data.pickedPoint) {
var meshName = data.pickedMesh.name;
var position = data.pickedPoint;
currentPickedMesh = data.pickedMesh; // 保存网格对象
// 获取已创建的DOM元素
var infoDiv = document.getElementById('model-info-box');
// 更新信息内容
document.getElementById('info-name').textContent = '名称: ' + meshName;
document.getElementById('info-position').textContent = '坐标: [' + position.x.toFixed(2) + ', ' + position.y.toFixed(2) + ', ' + position.z.toFixed(2) + ']';
// 显示旋转按钮,隐藏颜色按钮
document.getElementById('rotation-buttons').style.display = 'flex';
document.getElementById('color-buttons').style.display = 'none';
// 将DOM附加到点击的3D坐标会自动显示
kernel.domTo3D.attach('model-info', infoDiv, [position.x, position.y, position.z], { x: -2, y: -2 });
}
break;
default:
break;
}
});
// 暴露到全局,供 index.html 使用
window.getCurrentMaterialName = function () { return currentMaterialName; };
window.getCurrentPickedMesh = function () { return currentPickedMesh; };
// 暴露 kernel 到全局,方便调试
kernel.on('hotspot:click', function (event) {
console.log('热点被点击:', event);
var id = event.id;
var name = event.name;
var payload = event.payload;
if (payload && payload.skus && payload.skus.length > 0) {
console.log('热点关联的SKU列表:', payload.skus);
// 这里可以根据 SKU 列表做进一步处理,比如显示产品信息
} else {
console.log('该热点没有关联SKU');
}
// if (name === "卷帘门") {
// kernel.door.toggle({ upY: 28, downY: 0, speed: 12 });
// // Y轴剖切只作用于卷帘门网格保留下方剖掉上方
// var clipHeight = 28; // 调整这个值找到合适的剖切高度
// console.log('设置剖切:', clipHeight);
// kernel.clipping.setY(clipHeight, true, ['Box005.001', 'Box006.001']);
// }
});
})();
}
</script>
</body>
</html>