我有一个包含 Iframe 的模态,它利用 Three.js 加载 3d 门。
Iframe 在桌面上运行良好,但是,当我们在移动设备上与它交互时,当我们关闭模式时,它会崩溃或重新加载页面。在 safari 上,页面会重新加载,但在第二次重新加载时,页面会崩溃
页面加载 Iframe
<iframe loading="lazy" id="door-viewer" src="<?= "{$site_url}/3d-spinner.php?sku={$unique_skus[0]}"; ?>" sandbox></iframe>
生成iframe的文件
<?php
$product_sku = isset($_GET['sku']) ? strtoupper($_GET['sku']) : null;
$proto = $_SERVER['HTTP_X_FORWARDED_PROTO'];
$name = $_SERVER['SERVER_NAME'];
$site_url = "{$proto}://{$name}";
?>
<?php
header("Content-Security-Policy: frame-ancestors 'self' ".file_get_contents(getcwd()."/3d-spinner-assets/allowed-domains.txt").";");
?>
<html>
<head>
<!-- Three JS CDN Scripts -->
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
}
}
</script>
<!-- IFRAME STYLES -->
<style>
body {
margin: 0;
box-sizing: border-box;
}
canvas {
object-fit: cover;
width: 100% !important;
height: 100% !important;
aspect-ratio: 1;
background-image: radial-gradient(at 50% 50%, rgb(245, 245, 245) 20%, rgb(206, 206, 206) 90%);
}
.lpd-360-image-icon {
position: absolute;
top: 2rem;
left: 2rem;
}
#door-viewer {
position: relative;
}
noscript {
display: flex;
align-items: center;
justify-self: center;
font-size: 2rem;
height: 100vw;
width: 100vw;
}
</style>
</head>
<body>
<noscript>
Please enable JavaScript and reload your browser
</noscript>
<?php if($product_sku) : ?>
<div id="door-viewer">
<!-- Canvas is injected via Three.js -->
</div>
<?php else : ?>
<div>
<p>Product not found, check your sku</p>
</div>
<?php endif; ?>
<script type="module">
const sku = "<?= preg_replace('/[^-a-zA-Z0-9_]/', '', $product_sku ); ?>"
const site_url = "<?= $site_url; ?>"
import initSpinner from "./3d-spinner-assets/assets/scripts/main.js"
initSpinner(sku, site_url);
</script>
</body>
</html>
主.js文件
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
function initSpinner(sku, site_url) {
const doorAssetPath = `${site_url}/3d-spinner-assets/door/${sku}/model.gltf"`;
const doorViewerCanvas = document.getElementById("door-viewer");
// Scene
const scene = new THREE.Scene();
// Camera
const camera = new THREE.PerspectiveCamera(75, 500 / 500, 0.1, 800);
camera.position.z = 10;
camera.position.y = 0;
camera.position.x = 0;
// Renderer
const renderer = new THREE.WebGLRenderer({
antialias: true,
devicePixelRatio: 1,
alpha: true,
});
renderer.setSize(1200, 1200); // Set Scene Size
renderer.shadowMap.enabled = true;
renderer.setClearColor(0xffffff, 0); // Make scene transparent
renderer.setPixelRatio(window.devicePixelRatio); // Set Pixel Ratio in line with device
// Scene Lighting
const ambientLight = new THREE.AmbientLight(0x404040, 90);
scene.add(ambientLight);
// Controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // Add damping for smoother motion
controls.dampingFactor = 0.05; // Adjust the damping factor
controls.rotateSpeed = 0.5; // Adjust rotation speed
controls.zoomSpeed = 1.2; // Adjust zoom speed
// Load Door
const loader = new GLTFLoader();
loader.load(doorAssetPath, (gltfScene) => {
const loadedDoor = gltfScene.scene;
// Traverse the loadedDoor to set transparency for the windows
const windowMaterials = []; // Add the window materials to this array
// Inside the load callback function
loadedDoor.traverse((child) => {
if (
(child.isMesh && child.name === "glass") ||
child.name === "Glass"
) {
const windowMaterial = child.material;
windowMaterial.transparent = true;
windowMaterial.opacity = 0; // Adjust opacity value
windowMaterials.push(windowMaterial);
}
});
const scaleFactor = 0.003; // Adjust this value as needed
loadedDoor.scale.set(scaleFactor, scaleFactor, scaleFactor); // Adjust door's scale
loadedDoor.position.setY(-3); // Offset door's position
scene.add(loadedDoor); // Add the door to the scene
});
doorViewerCanvas.appendChild(renderer.domElement); // Mount canvas to the DOM
function animate() {
requestAnimationFrame(animate);
controls.update(); // Update the controls
renderer.render(scene, camera);
}
animate();
}
export default initSpinner;
我寻找过内存泄漏,但找不到任何东西
我找到了解决方案,画布在移动设备上加载时宽度约为 3000px。我必须更新 html 以包含
<meta name="viewport" content="width=device-width, initial-scale=1.0">
并更新 main.js 文件以获取窗口的宽度和高度
// Camera
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
800
);
camera.position.z = 5;
camera.position.y = 0;
camera.position.x = 0;
// Renderer
const renderer = new THREE.WebGLRenderer({
antialias: true,
devicePixelRatio: window.devicePixelRatio,
alpha: true,
});