1
This commit is contained in:
173
test/customization-3d-copy.js
Normal file
173
test/customization-3d-copy.js
Normal file
@ -0,0 +1,173 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
if (!document.querySelector('.customization-3d-wrapper')) return;
|
||||
|
||||
const get3DViewer = () => window.Customization3DViewer;
|
||||
|
||||
const load3DModel = (modelUrl, productId) => {
|
||||
const viewer = get3DViewer();
|
||||
return viewer ? viewer.loadModel(modelUrl, productId) : Promise.resolve(false);
|
||||
};
|
||||
|
||||
const clear3DModel = () => {
|
||||
const viewer = get3DViewer();
|
||||
return viewer ? viewer.clearModel() : Promise.resolve();
|
||||
};
|
||||
|
||||
const show3DEmpty = () => {
|
||||
get3DViewer()?.showEmpty();
|
||||
};
|
||||
|
||||
const get3DModelUrl = (productId, variantId, wrapper) => {
|
||||
const viewer = get3DViewer();
|
||||
return viewer ? viewer.getModelUrl(productId, variantId, wrapper) : Promise.resolve(null);
|
||||
};
|
||||
|
||||
const get3DHotspots = () => {
|
||||
const hotspots = window.CUSTOMIZATION_3D_HOTSPOTS;
|
||||
if (!Array.isArray(hotspots)) return [];
|
||||
return hotspots
|
||||
.filter((item) => item && typeof item === 'object')
|
||||
.map((item) => {
|
||||
const meshName = String(item.meshName || '').trim();
|
||||
if (!meshName) return null;
|
||||
|
||||
let offset = [0, 0, 0];
|
||||
if (Array.isArray(item.offset) && item.offset.length >= 3) {
|
||||
const parsed = item.offset.slice(0, 3).map((v) => Number(v));
|
||||
if (parsed.every(Number.isFinite)) {
|
||||
const maxAbs = Math.max(...parsed.map((v) => Math.abs(v)));
|
||||
offset = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
const next = {
|
||||
id: String(item.id || meshName),
|
||||
name: String(item.name || item.id || meshName),
|
||||
meshName,
|
||||
offset,
|
||||
};
|
||||
|
||||
const color = String(item.color || '').trim();
|
||||
if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color)) next.color = color;
|
||||
|
||||
const r = Number(item.radius);
|
||||
if (Number.isFinite(r) && r > 0) {
|
||||
next.radius = Math.min(Math.max(r, 0.5), 30);
|
||||
} else {
|
||||
const defaultRadius = Number(window.CUSTOMIZATION_3D_HOTSPOT_RADIUS_DEFAULT);
|
||||
next.radius = Number.isFinite(defaultRadius) && defaultRadius > 0
|
||||
? Math.min(30, defaultRadius)
|
||||
: 18;
|
||||
}
|
||||
|
||||
const icon = String(item.icon || '').trim();
|
||||
if (icon && (/^https?:\/\//i.test(icon) || icon.startsWith('//'))) {
|
||||
next.icon = icon;
|
||||
}
|
||||
|
||||
if (item.payload && typeof item.payload === 'object' && !Array.isArray(item.payload)) {
|
||||
next.payload = item.payload;
|
||||
}
|
||||
|
||||
return next;
|
||||
})
|
||||
.filter(Boolean);
|
||||
};
|
||||
|
||||
const getHotspotActionConfig = (detail = {}) => {
|
||||
const all = window.CUSTOMIZATION_3D_HOTSPOT_ACTIONS;
|
||||
if (!all || typeof all !== 'object') return null;
|
||||
return all[detail.id] || all[detail.name] || null;
|
||||
};
|
||||
|
||||
const handle3DHotspotClick = (event) => {
|
||||
const detail = event?.detail || {};
|
||||
const viewer = get3DViewer();
|
||||
if (!viewer) return;
|
||||
|
||||
if (typeof window.CUSTOMIZATION_3D_ON_HOTSPOT_CLICK === 'function') {
|
||||
try {
|
||||
window.CUSTOMIZATION_3D_ON_HOTSPOT_CLICK(detail, viewer);
|
||||
} catch (err) {
|
||||
console.warn('[Customization] CUSTOMIZATION_3D_ON_HOTSPOT_CLICK failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
const action = getHotspotActionConfig(detail);
|
||||
if (action?.door) {
|
||||
viewer.door?.toggle(action.door);
|
||||
}
|
||||
if (action?.clipping) {
|
||||
const clip = action.clipping;
|
||||
if (typeof clip.height === 'number') {
|
||||
viewer.clipping?.setY(
|
||||
clip.height,
|
||||
clip.keepBelow !== false,
|
||||
Array.isArray(clip.meshNames) ? clip.meshNames : []
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setup3DEventBridge = () => {
|
||||
if (document.documentElement.dataset.customization3dEventsBoundCopy === '1') return;
|
||||
document.documentElement.dataset.customization3dEventsBoundCopy = '1';
|
||||
|
||||
let hotspotRenderTimer = null;
|
||||
const renderConfiguredHotspots = () => {
|
||||
if (hotspotRenderTimer) clearTimeout(hotspotRenderTimer);
|
||||
hotspotRenderTimer = setTimeout(() => {
|
||||
hotspotRenderTimer = null;
|
||||
const hotspots = get3DHotspots();
|
||||
const viewer = get3DViewer();
|
||||
viewer?.hotspot?.clear?.();
|
||||
if (!hotspots.length) return;
|
||||
viewer?.hotspot?.render(hotspots);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
document.addEventListener('3d:scene:ready', renderConfiguredHotspots);
|
||||
document.addEventListener('3d:hotspots:update', renderConfiguredHotspots);
|
||||
document.addEventListener('3d:hotspot:click', handle3DHotspotClick);
|
||||
};
|
||||
|
||||
const setupWheelScrollLockOn3DContainer = () => {
|
||||
const container = document.querySelector('[data-3d-container]');
|
||||
if (!container || container.dataset.customization3dWheelLockCopy === '1') return;
|
||||
container.dataset.customization3dWheelLockCopy = '1';
|
||||
container.addEventListener(
|
||||
'wheel',
|
||||
(e) => {
|
||||
if (!container.contains(e.target)) return;
|
||||
e.preventDefault();
|
||||
},
|
||||
{ capture: true, passive: false }
|
||||
);
|
||||
};
|
||||
|
||||
const refreshHotspots = () => {
|
||||
document.dispatchEvent(new CustomEvent('3d:hotspots:update', { bubbles: true }));
|
||||
};
|
||||
|
||||
window.Customization3DInteractions = {
|
||||
load3DModel,
|
||||
clear3DModel,
|
||||
show3DEmpty,
|
||||
get3DModelUrl,
|
||||
get3DHotspots,
|
||||
refreshHotspots,
|
||||
};
|
||||
|
||||
const init3DInteractions = () => {
|
||||
setup3DEventBridge();
|
||||
setupWheelScrollLockOn3DContainer();
|
||||
};
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init3DInteractions);
|
||||
} else {
|
||||
init3DInteractions();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user