UNPKG

uix-kit

Version:

A free web kits for fast web design and development, compatible with Bootstrap v5.

361 lines (247 loc) 10 kB
/* ************************************* * <!-- 3D Gallery with three.js --> ************************************* */ import { UixModuleInstance, UixDebounce, UixThrottle } from '@uixkit/core/_global/js'; import { OrbitControls } from '@uixkit/plugins/THREE/esm/controls/OrbitControls'; export const THREE_GALLERY = ( ( module, $, window, document ) => { if ( window.THREE_GALLERY === null ) return false; module.THREE_GALLERY = module.THREE_GALLERY || {}; module.THREE_GALLERY.version = '0.0.8'; module.THREE_GALLERY.documentReady = function( $ ) { //Prevent this module from loading in other pages if ( $( '#3D-gallery-three-canvas' ).length == 0 || ! Modernizr.webgl ) return false; let sceneSubjects = []; // Import objects and animations dynamically const MainStage = function() { let windowWidth = window.innerWidth, windowHeight = window.innerHeight; const rendererCanvasID = '3D-gallery-three-canvas'; // Generate one plane geometries mesh to scene //------------------------------------- let camera, controls, scene, light, renderer, displacementSprite, theta = 0; let offsetWidth = 1400, offsetHeight = 933, allImages = [], imgTotal, imagesLoaded = false; // we will keep track of the scroll let scrollValue = 0; let lastScrollValue = 0; function init() { //camera camera = new THREE.PerspectiveCamera( 75, windowWidth / windowHeight, 1, 10000 ); camera.position.set(0, 0, 1000); //Scene scene = new THREE.Scene(); //HemisphereLight scene.add( new THREE.AmbientLight( 0x555555 ) ); light = new THREE.SpotLight( 0xffffff, 1.5 ); light.position.set( 0, 500, 2000 ); light.decay = 0; // !!!Important scene.add( light ); //WebGL Renderer renderer = new THREE.WebGLRenderer( { canvas : document.getElementById( rendererCanvasID ), //canvas alpha : true, antialias: true } ); renderer.setSize( windowWidth, windowHeight ); //controls controls = new OrbitControls( camera, renderer.domElement); controls.autoRotate = false; controls.autoRotateSpeed = 0.5; controls.rotateSpeed = 0.5; controls.zoomSpeed = 1.2; controls.panSpeed = 0.8; controls.enableZoom = false; controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled controls.dampingFactor = 0.25; controls.screenSpacePanning = false; controls.minDistance = 100; controls.maxDistance = 500; controls.maxPolarAngle = Math.PI / 2; controls.target.set( 30, 167, 81 ); controls.update(); // Immediately use the texture for material creation // Create a texture loader so we can load our image file const imgs = [ 'https://placehold.co/500x550/blue/white', 'https://placehold.co/700x800/orange/white', 'https://placehold.co/650x550/green/white', 'https://placehold.co/400x300/red/white', 'https://placehold.co/500x550/blue/white', 'https://placehold.co/500x550/orange/white', 'https://placehold.co/500x550/green/white', 'https://placehold.co/500x550/gray/white', 'https://placehold.co/500x550/blue/white', 'https://placehold.co/500x550/orange/white', 'https://placehold.co/500x550/green/white' ]; //A loader for loading all images from array. const loader = new THREE.TextureLoader(); loader.crossOrigin = 'anonymous'; //Preload imgTotal = imgs.length; const gap = 100, circumference = (offsetWidth + gap) * imgTotal, //get circumference from all images width galleryRadius = circumference / ( Math.PI * 2 ), // C = 2πr = Math.PI * 2 * radius eachItemAngleToRad = ( Math.PI * 2 ) / imgTotal; // 360° = 2π = Math.PI * 2 if ( camera.position.length() > galleryRadius ) { camera.position.set( 0, 0, 0 ); } //Load images imgs.forEach( function( element, index ) { loadImage( loader, element, index, offsetWidth, offsetHeight, imgTotal, eachItemAngleToRad, galleryRadius, $( '#3D-gallery-three-canvas__loader' ) ); }); // Add function to the window that should be resized const debounceFuncWindow = UixDebounce(windowUpdate, 50); window.removeEventListener('resize', debounceFuncWindow); window.addEventListener('resize', debounceFuncWindow); // Add function to the element that should be used as the scrollable area. const throttleFunc = UixThrottle(scrollUpdate, 5); window.removeEventListener('scroll', throttleFunc); window.removeEventListener('touchmove', throttleFunc); window.addEventListener('scroll', throttleFunc); window.addEventListener('touchmove', throttleFunc); throttleFunc(); } function render() { requestAnimationFrame( render ); theta += 0.1; //To set a background color. renderer.setClearColor( 0x000000 ); // listen to scroll to update let delta = scrollValue - lastScrollValue; // threshold if (delta > 60) { delta = 60; } else if(delta < -60) { delta = -60; } camera.position.x = camera.position.x + delta; //check all images loaded if ( typeof allImages != typeof undefined ) { if ( !imagesLoaded && allImages.length === imgTotal ) { allImages.forEach( function (element ) { scene.add( element ); }); imagesLoaded = true; } } //update camera and controls controls.update(); //push objects /* @Usage: function CustomObj( scene ) { const elements = new THREE...; scene.add( elements ); this.update = function( time ) { elements.rotation.y = time*0.003; } } sceneSubjects.push( new CustomObj( MainStage.getScene() ) ); */ for( let i = 0; i < sceneSubjects.length; i++ ) { sceneSubjects[i].update( clock.getElapsedTime()*1 ); } //render the scene to display our scene through the camera's eye. renderer.render( scene, camera ); } function windowUpdate() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } function scrollUpdate() { lastScrollValue = scrollValue; scrollValue = window.pageYOffset; console.log( 'lastScrollValue: ' + lastScrollValue + ', scrollValue: ' + scrollValue ); } /* * Load Image * * @param {Element} imgLoader - A loader for loading all images from array. * @param {String} src - URL of image. * @param {Number} index - Index of image. * @param {Number} w - The width of an image, in pixels. * @param {Number} h - The height of an image, in pixels. * @param {Number} total - Total number of preload images. * @param {Number} itemRadAngle - An equal radian angle of a sphere for each element. * @param {Number} radius - Radius length of the sphere (circumference). * @param {Element|String} loading - Progress bar display control. * @return {Void} */ function loadImage( imgLoader, src, index, w, h, total, itemRadAngle, radius, loading ) { // load a resource imgLoader.load( // resource URL src, // onLoad callback function ( texture ) { // in this example we create the material when the texture is loaded const material = new THREE.MeshBasicMaterial( { map: texture } ); const geometry = new THREE.PlaneGeometry( w, h ); const mesh = new THREE.Mesh( geometry, material ); //LinearFilter, which takes the four closest texels and bilinearly interpolates among them. mesh.minFilter = THREE.LinearFilter; mesh.overdraw = true; //Calculate the position of the image //X axis: a = sinA * c = Math.sin( rad ) * radius //Z axis: b = cosA * c = Math.cos( rad ) * radius mesh.rotation.y = -index * itemRadAngle; mesh.position.set( radius * Math.sin(index * itemRadAngle), 0, -radius * Math.cos(index * itemRadAngle) ); allImages.push( mesh ); //loading TweenMax.to( loading, 0.5, { width : Math.round(100 * allImages.length / total ) + '%', onComplete : function() { if ( $( this.target ).width() >= windowWidth - 50 ) { TweenMax.to( this.target, 0.5, { alpha: 0 }); } } }); }, // onProgress callback currently not supported undefined, // onError callback function ( err ) { console.error( 'An error happened.' ); } ); } // //------------------------------------- return { init : init, render : render, getRendererCanvasID : function () { return rendererCanvasID; }, getScene : function () { return scene; }, getCamera : function () { return camera; } }; }(); MainStage.init(); MainStage.render(); }; module.components.documentReady.push( module.THREE_GALLERY.documentReady ); return class THREE_GALLERY { constructor() { this.module = module; } }; })( UixModuleInstance, jQuery, window, document );