Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ShaneBrumback/b2d918bd8fa9b45473f1822cf351aa68 to your computer and use it in GitHub Desktop.

Select an option

Save ShaneBrumback/b2d918bd8fa9b45473f1822cf351aa68 to your computer and use it in GitHub Desktop.
Three.js Examples How to Program a 3D Menu
<!--////////////////////////////////////////////////////////////////////////////////////////
/// ///
/// 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