Instantly share code, notes, and snippets.
Last active
May 30, 2025 08:54
-
Star
0
(0)
You must be signed in to star a gist -
Fork
1
(1)
You must be signed in to fork a gist
-
-
Save ShaneBrumback/b2d918bd8fa9b45473f1822cf351aa68 to your computer and use it in GitHub Desktop.
Three.js Examples How to Program a 3D Menu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!--//////////////////////////////////////////////////////////////////////////////////////// | |
| /// /// | |
| /// Example Using Three.js Library, HTML, CSS & JavaScript /// | |
| // 3D Interactive Web Apps & Games 2021-2024 /// | |
| /// Contact Shane Brumback https://www.shanebrumback.com /// | |
| /// Send a message if you have questions about this code /// | |
| /// I am a freelance developer. I develop any and all web. /// | |
| /// Apps Websites 3D 2D CMS Systems etc. Contact me anytime :) /// | |
| /// /// | |
| ////////////////////////////////////////////////////////////////////////////////////////////--> | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Three.js Examples - Interactive 3D Menu</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Include Three.js library --> | |
| <script src="https://cdn.jsdelivr.net/npm/three@latest/build/three.min.js"></script> | |
| <!-- Include OrbitControls module --> | |
| <script src="https://cdn.jsdelivr.net/npm/three@latest/examples/js/controls/OrbitControls.js"></script> | |
| <script type="module"> | |
| // Define the JSON data containing images and links | |
| var jsonData = [ | |
| { | |
| "image": "https://www.shanebrumback.com/images/threejs-examples-exploring-interactive-visual-particle-systems.jpg", | |
| "link": "https://www.shanebrumback.com/blog/threejs-examples-exploring-interactive-visual-particle-systems.html" | |
| }, | |
| { | |
| "image": "https://www.shanebrumback.com/images/super-soldier-battl-game-demo.jpg", | |
| "link": "https://www.supersoldierbattle.com" | |
| }, | |
| { | |
| "image": "https://www.shanebrumback.com/images/threejs-examples-interactive-image-particle-system.jpg", | |
| "link": "http://www.shanebrumback.com/blog/fun-interactive-particle-system-programming-using-threejs.html" | |
| }, | |
| { | |
| "image": "https://www.shanebrumback.com/images/metasports-intro.png", | |
| "link": "https://www.shanebrumback.com/videos.html" | |
| }, | |
| { | |
| "image": "https://www.shanebrumback.com/images/spacex-3d-falcon-9-launch-pad.jpg", | |
| "link": "https://www.shanebrumback.com/spacex-falcon-9-launch-complex.html" | |
| }, | |
| // Add more image-link pairs as needed | |
| ]; | |
| // Create the Three.js scene | |
| var scene = new THREE.Scene(); | |
| var camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 4000); | |
| // Create the renderer | |
| var renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, depth: true }); | |
| // Configure renderer settings | |
| renderer.setPixelRatio(window.devicePixelRatio * 2); | |
| renderer.precision = "highp"; | |
| renderer.gammaOutput = true; | |
| renderer.gammaFactor = 2.2; | |
| renderer.setPixelRatio(window.devicePixelRatio); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| renderer.toneMapping = THREE.ReinhardToneMapping; | |
| renderer.setClearColor(0x0000FF, 1); // Set initial background color | |
| renderer.domElement.style.position = 'fixed'; | |
| renderer.domElement.id = '3d-menu'; | |
| renderer.domElement.style.zIndex = '-1'; | |
| renderer.domElement.style.left = '0'; | |
| renderer.domElement.style.top = '0'; | |
| document.body.appendChild(renderer.domElement); | |
| // Create a circular pattern of planes | |
| var radius = 1500; | |
| var angle = (2 * Math.PI) / jsonData.length; | |
| var rotationSpeed = 0.002; // Adjust the rotation speed as desired | |
| var raycaster = new THREE.Raycaster(); | |
| var mouse = new THREE.Vector2(); | |
| raycaster.objects = []; // Initialize the objects array | |
| for (var i = 0; i < jsonData.length; i++) { | |
| var jsonEntry = jsonData[i]; | |
| var texture = new THREE.TextureLoader().load(jsonEntry.image); | |
| var material = new THREE.MeshBasicMaterial({ map: texture }); | |
| texture.colorSpace = THREE.SRGBColorSpace; | |
| var plane = new THREE.Mesh(new THREE.PlaneGeometry(1920, 1080), material); | |
| var x = radius * Math.cos(angle * i); | |
| var z = radius * Math.sin(angle * i); | |
| plane.position.set(x, 0, z); | |
| plane.lookAt(camera.position); | |
| scene.add(plane); | |
| // Attach click event to redirect to the URL | |
| plane.userData = { link: jsonEntry.link }; | |
| plane.cursor = 'pointer'; | |
| plane.onClick = function () { | |
| window.location.href = this.userData.link; | |
| }; | |
| plane.addEventListener('click', plane.onClick); | |
| // Add the plane to the raycaster objects for intersection detection | |
| raycaster.objects.push(plane); | |
| } | |
| // Set up camera position and controls | |
| camera.position.z = 30; | |
| camera.position.y = 50; | |
| var controls = new THREE.OrbitControls(camera, renderer.domElement); | |
| controls.minPolarAngle = Math.PI / 180 * 80; // Set the minimum vertical rotation angle to 80 degrees (in radians) | |
| controls.maxPolarAngle = Math.PI / 180 * 100; // Set the maximum vertical rotation angle to 100 degrees (in radians) | |
| // controls.autoRotate = true; // Enable auto rotation | |
| controls.autoRotateSpeed = 0.5; // Set the rotation speed | |
| controls.rotateSpeed = 0.2; // Set the rotation speed for general user control (adjust as needed) | |
| controls.mouseRotateSpeed = 0.0005; // Set the rotation speed when clicking and dragging the mouse (adjust as needed) | |
| // Add grid helper | |
| var gridHelper = new THREE.GridHelper(50, 50); | |
| scene.add(gridHelper); | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| controls.update(); | |
| rotateScene(); | |
| renderer.render(scene, camera); | |
| } | |
| function rotateScene() { | |
| // Rotate the scene around the center origin | |
| scene.rotation.y += rotationSpeed; | |
| } | |
| function onMouseClick(event) { | |
| // Calculate normalized device coordinates (NDC) of mouse click | |
| mouse.x = (event.clientX / window.innerWidth) * 2 - 1; | |
| mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; | |
| // Raycast from camera position | |
| raycaster.setFromCamera(mouse, camera); | |
| // Check for intersections with the planes | |
| var intersects = raycaster.intersectObjects(raycaster.objects); | |
| if (intersects.length > 0) { | |
| if (!isMouseOverSlider(event)) { | |
| controls.reset(); | |
| var link = intersects[0].object.userData.link; | |
| window.location.href = link; | |
| } | |
| } | |
| } | |
| function isMouseOverSlider(event) { | |
| // Check if the mouse is over elements with class 'slider' or its descendants | |
| var slider = document.querySelector('.slider'); | |
| return slider && (slider === event.target || slider.contains(event.target)); | |
| } | |
| animate(); | |
| // Resize renderer when window size changes | |
| window.addEventListener('resize', function () { | |
| camera.aspect = window.innerWidth / window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| }); | |
| // Listen for mouse click events | |
| window.addEventListener('mousedown', onMouseClick, false); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment