importmap-starter.html 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. <!DOCTYPE html>
  2. <!--
  3. threejs-ops import-map starter — complete no-bundler three.js app skeleton.
  4. Serve over HTTP (any static server; ES modules don't load from file://):
  5. npx serve . # or: python -m http.server
  6. ADAPT: [1] pin your three version (BOTH entries, same version)
  7. [2] swap the demo scene for your own
  8. [3] delete OrbitControls if you don't need it
  9. -->
  10. <html lang="en">
  11. <head>
  12. <meta charset="utf-8" />
  13. <meta name="viewport" content="width=device-width, initial-scale=1" />
  14. <title>three.js app</title>
  15. <style>
  16. html, body { margin: 0; height: 100%; overflow: hidden; }
  17. canvas { display: block; }
  18. </style>
  19. <!-- Import map MUST come before any type="module" script.
  20. [1] Pin the SAME exact version in both entries — mixing versions loads
  21. two copies of three and breaks instanceof checks across modules. -->
  22. <script type="importmap">
  23. {
  24. "imports": {
  25. "three": "https://cdn.jsdelivr.net/npm/three@0.185.1/build/three.module.js",
  26. "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.185.1/examples/jsm/"
  27. }
  28. }
  29. </script>
  30. </head>
  31. <body>
  32. <script type="module">
  33. import * as THREE from 'three';
  34. import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  35. // --- renderer / scene / camera -----------------------------------------
  36. const renderer = new THREE.WebGLRenderer({ antialias: true });
  37. renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  38. renderer.setSize(window.innerWidth, window.innerHeight);
  39. document.body.appendChild(renderer.domElement);
  40. const scene = new THREE.Scene();
  41. scene.background = new THREE.Color(0x10141c);
  42. const camera = new THREE.PerspectiveCamera(
  43. 60, window.innerWidth / window.innerHeight, 0.1, 200);
  44. camera.position.set(3, 2, 5);
  45. const controls = new OrbitControls(camera, renderer.domElement);
  46. controls.enableDamping = true;
  47. // --- [2] demo scene — replace ------------------------------------------
  48. scene.add(new THREE.AmbientLight(0xffffff, 0.4));
  49. const sun = new THREE.DirectionalLight(0xffffff, 2.5);
  50. sun.position.set(5, 10, 5);
  51. scene.add(sun);
  52. const cube = new THREE.Mesh(
  53. new THREE.BoxGeometry(1, 1, 1),
  54. new THREE.MeshStandardMaterial({ color: 0x4488ff, roughness: 0.4 }));
  55. cube.position.y = 0.5;
  56. scene.add(cube);
  57. scene.add(new THREE.Mesh(
  58. new THREE.PlaneGeometry(20, 20).rotateX(-Math.PI / 2),
  59. new THREE.MeshStandardMaterial({ color: 0x222831 })));
  60. // --- resize --------------------------------------------------------------
  61. window.addEventListener('resize', () => {
  62. camera.aspect = window.innerWidth / window.innerHeight;
  63. camera.updateProjectionMatrix();
  64. renderer.setSize(window.innerWidth, window.innerHeight);
  65. });
  66. // --- loop: render dt + clamped, fixed-step-ready (see game-loop.md) ------
  67. let last = 0;
  68. renderer.setAnimationLoop((now) => {
  69. const dt = Math.min((now - last) / 1000, 0.1); // clamp tab-switch spike
  70. last = now;
  71. cube.rotation.y += dt; // dt-scaled, never per-frame constants
  72. controls.update();
  73. renderer.render(scene, camera);
  74. });
  75. </script>
  76. </body>
  77. </html>