From 7a44d5b0ed811e2ca6c0e991edb5f6551e24b13a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fe=CC=81lix=20Pe=CC=81ault?= Date: Thu, 9 Apr 2020 20:22:10 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20The=20interactive=20globe?= =?UTF-8?q?=20has=20arrived=20(WIP=20but=20pretty=20good)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Control the width/height of the globe via CSS --- src/globe/beam/Camera.js | 11 +- src/globe/beam/Texture.js | 1 + src/globe/index.js | 155 ++++++++++++++------------ src/molecules/InteractiveGlobe.svelte | 60 +++++----- src/routes/choose.svelte | 4 +- src/routes/credits.svelte | 6 +- src/style/layouts/_explore.scss | 13 +++ src/style/molecules/_globe.scss | 65 +++++++++-- src/style/pages/_homepage.scss | 12 ++ src/style/pages/_page.scss | 2 +- static/img/globe/map-2k.png | Bin 0 -> 26199 bytes 11 files changed, 208 insertions(+), 121 deletions(-) create mode 100644 static/img/globe/map-2k.png diff --git a/src/globe/beam/Camera.js b/src/globe/beam/Camera.js index 4028a8d..ce4614a 100755 --- a/src/globe/beam/Camera.js +++ b/src/globe/beam/Camera.js @@ -247,6 +247,8 @@ class Camera extends Object3d { this._isPointerDown = true; + this._pointerParent.classList.add('is-grabbing') + this.touchEvent = TOUCH ? (event.touches[0] || event.changedTouches[0]) : event; this.touchEventPageX = this.touchEvent.pageX; @@ -273,7 +275,7 @@ class Camera extends Object3d { return; } - event.preventDefault(); + // event.preventDefault(); this.touchEvent = TOUCH ? (event.touches[0] || event.changedTouches[0]) : event; this.touchEventPageX = this.touchEvent.pageX; @@ -281,6 +283,7 @@ class Camera extends Object3d { this.touchEventPageX -= window.pageXOffset || document.documentElement.scrollLeft; this.touchEventPageY -= window.pageYOffset || document.documentElement.scrollTop; + if (this.isRightClick) { this.pointerXMove = this.startPointerX + (this.touchEventPageX - this.pointerXDown); this.pointerYMove = this.startPointerY + (this.touchEventPageY - this.pointerYDown); @@ -291,6 +294,11 @@ class Camera extends Object3d { this.theta = this.thetaDown + ( this.pointerXOrbiter / this.winWidth * 2 * Math.PI); this.phi = this.phiDown + ( this.pointerYOrbiter / this.winHeight * 2 * Math.PI * -1); this.phi = Math.max( this._minPolarAngle, Math.min( this._maxPolarAngle, this.phi ) ); + + if( TOUCH ) { + this.phi = 0; + } + } @@ -299,6 +307,7 @@ class Camera extends Object3d { _onPointerUp() { this._isPointerDown = false; this.isRightClick = false; + this._pointerParent.classList.remove('is-grabbing') } update(force) { diff --git a/src/globe/beam/Texture.js b/src/globe/beam/Texture.js index bda6ab5..0b2c2fa 100755 --- a/src/globe/beam/Texture.js +++ b/src/globe/beam/Texture.js @@ -218,6 +218,7 @@ Texture.fromUrl = function(gl, url, options) { img.onload = null; img.onerror = null; TEXTURE_CACHE[url] = img; + options && options.loaded && options.loaded() texture.bindImage(img); }; diff --git a/src/globe/index.js b/src/globe/index.js index 74cef24..6b31f05 100644 --- a/src/globe/index.js +++ b/src/globe/index.js @@ -11,11 +11,11 @@ import GlobeFS from './globe-fs' function hexToRgb(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [ - parseInt(result[1], 16) / 255, - parseInt(result[2], 16) / 255, - parseInt(result[3], 16) / 255 - ] : [0,0,0]; - } + parseInt(result[1], 16) / 255, + parseInt(result[2], 16) / 255, + parseInt(result[3], 16) / 255 + ] : [0,0,0]; +} function lonLatToVector3(lng, lat) { var phi = (90-lat)*(Math.PI/180), @@ -29,6 +29,12 @@ function lonLatToVector3(lng, lat) { class WebglGlobe { // Constructor constructor (options) { + + //camera position cache + this.cameraX = 0; + this.cameraY = 0; + this.cameraZ = 0; + this.options = options; this.$el = options.el; this.cities = options.markers @@ -50,29 +56,38 @@ class WebglGlobe { // Build buildWebglScene () { this.renderer = new Renderer({ - alpha: this.alpha, - antialias: this.antialias, + alpha: this.alpha, + antialias: window.innerWidth < 768 ? true : false,// this.antialias, }); this.$el.appendChild(this.renderer.canvas); - this.texture = Texture.fromUrl(this.renderer.gl, this.options.texture) + this.texture = Texture.fromUrl(this.renderer.gl, this.options.texture, { + loaded: ()=>{ + requestAnimationFrame(()=>{ + requestAnimationFrame(()=>{ + this.imageLoaded = true; + }) + }) + } + }) this.scene = new Container() - this.camera = new Camera({ - fov: settings.fov, - near: 1, - far: settings.cameraFar, - type: 'perspective', - orbitControl: true, - firstPerson: false, - lookAt: [0,0,0], - position: [0,0, (3) / 2 / Math.tan(Math.PI * settings.fov / 360)], + this.camera = new Camera({ + fov: settings.fov, + near: 1, + far: settings.cameraFar, + type: 'perspective', + orbitControl: true, + firstPerson: false, + lookAt: [0,0,0], + position: [0,0, (this.options.cameraDistance) / 2 / Math.tan(Math.PI * settings.fov / 360)], + pointerParent: this.$el }); this.camera.lookAt = vec3.create() this.inverseViewProjectionMatrix = mat4.create() - this.viewProjectionMatrix = mat4.create() - this.cameraDirection = vec3.create(); + this.viewProjectionMatrix = mat4.create() + this.cameraDirection = vec3.create(); this.cameraPosition = vec3.create(); this.globeMesh = new Mesh(); @@ -90,7 +105,6 @@ class WebglGlobe { }) this.scene.add( this.globeMesh ) - this.markers = [] let markers = this.cities @@ -108,18 +122,32 @@ class WebglGlobe { widthSegments: 10, heightSegments: 10 }) let p = lonLatToVector3( markers[i].lng, markers[i].lat ) - markerMesh.position[0] = p[0] * 1. - markerMesh.position[1] = p[1] * 1. - markerMesh.position[2] = p[2] * 1. + + markerMesh.position[0] = p[0] + markerMesh.position[1] = p[1] + markerMesh.position[2] = p[2] this.scene.add( markerMesh ) - let el = document.createElement('div') + let el = document.createElement('a') + el.setAttribute('href', '/location/' + markers[i].countrySlug + '/' + markers[i].slug ) + el.setAttribute('sapper-noscroll','') let span = document.createElement('span') - span.innerHTML = markers[i].name + span.classList.add('marker__label') el.appendChild(span) + el.addEventListener('click', ()=>{ - alert('click on ' + markers[i].name) + this.options.onLinkClicked && this.options.onLinkClicked() }) + + let spanCity = document.createElement('span') + spanCity.classList.add('marker__city') + spanCity.innerHTML = markers[i].name; + span.appendChild(spanCity) + let spanCountry = document.createElement('span') + spanCountry.classList.add('marker__country') + spanCountry.innerHTML = markers[i].countryName; + span.appendChild(spanCountry) + el.classList.add('marker') this.$el.appendChild( el ) this.markers.push({ @@ -135,14 +163,17 @@ class WebglGlobe { resize () { this.width = this.$el.clientWidth; this.height = this.$el.clientHeight; + + // console.log('GLOBE RESIZE', this.width, this.height) + settings.resolution[0] = this.width //* settings.devicePixelRatio settings.resolution[1] = this.height //* settings.devicePixelRatio if (!this.supportWebgl) { return } - this.renderer.setPixelRatio(settings.devicePixelRatio); - this.renderer.resize(settings.resolution[0], settings.resolution[1]) - this.camera.aspect = settings.resolution[0] / settings.resolution[1] + this.renderer.setPixelRatio(window.innerWidth < 768 ? 1 : settings.devicePixelRatio); + this.renderer.resize(settings.resolution[0], settings.resolution[1]) + this.camera.aspect = settings.resolution[0] / settings.resolution[1] this.camera.updateProjectionMatrix() @@ -168,13 +199,13 @@ class WebglGlobe { let refPos = vec3.create() vec3.set(refPos, 0, 1, 0 ) vec3.transformMat4(refPos, refPos, this.viewProjectionMatrix); - let refx = ( (refPos[0] + 1) / 2) * window.innerWidth - let refy = (1. - (refPos[1] + 1) / 2) * window.innerHeight + let refx = ( (refPos[0] + 1) / 2) * this.width + let refy = (1. - (refPos[1] + 1) / 2) * this.height let dir2 = vec2.create() vec2.set( dir2, refx, refy ) let center2 = vec2.create() - vec2.set( center2, window.innerWidth/2, window.innerHeight/2 ) + vec2.set( center2, this.width/2, this.height/2 ) let dir2d2 = vec2.clone(dir2, dir2) vec2.subtract( dir2d2, dir2d2, center2 ) this.circleScreenSize = vec2.length( dir2d2 ) * 1.04 @@ -187,11 +218,6 @@ class WebglGlobe { return; } - // this.currPointer[0] += ( this.pointerRatio[0] - this.currPointer[0]) * 0.05 - // this.currPointer[1] += ( this.pointerRatio[1] - this.currPointer[1]) * 0.05 - // this.globeMesh.rotation[1] = this.currPointer[0] - // this.globeMesh.rotation[2] = this.currPointer[1] - //manually call this as we prevent ithe camera from update between passes this.camera.update(); vec3.set(this.cameraPosition, this.camera.worldMatrix[12], this.camera.worldMatrix[13], this.camera.worldMatrix[14]); @@ -202,15 +228,29 @@ class WebglGlobe { mat4.multiply(this.viewProjectionMatrix, this.viewProjectionMatrix, this.camera.inverseWorldMatrix); mat4.invert(this.inverseViewProjectionMatrix, this.viewProjectionMatrix); + let needsUpdate = false; + if (this.cameraX != this.camera.worldMatrix[12] || + this.cameraY != this.camera.worldMatrix[13] || + this.cameraZ != this.camera.worldMatrix[14]) { + this.cameraX = this.camera.worldMatrix[12]; + this.cameraY = this.camera.worldMatrix[13]; + this.cameraZ = this.camera.worldMatrix[14]; + needsUpdate = true; + } + if (!needsUpdate && this.imageLoaded) { + return + } + + // console.log('RENDER WEBGL') let screenPos = vec3.create() this.markers.forEach((marker, i)=>{ vec3.set(screenPos, marker.position[0], marker.position[1], marker.position[2] ) vec3.transformMat4(screenPos, screenPos, this.viewProjectionMatrix); - let x = ( (screenPos[0] + 1) / 2) * window.innerWidth - let y = (1. - (screenPos[1] + 1) / 2) * window.innerHeight + let x = ( (screenPos[0] + 1) / 2) * this.width + let y = (1. - (screenPos[1] + 1) / 2) * this.height let N = vec3.create() vec3.set( N, marker.position[0], marker.position[1], marker.position[2] ) @@ -222,11 +262,11 @@ class WebglGlobe { vec3.normalize( V, V ); //behind - if ( vec3.dot( V, N ) * -1 < 0) { + if ( vec3.dot( V, N ) * -1 < 0 ) { let dir = vec2.create() vec2.set( dir, x, y ) let center = vec2.create() - vec2.set( center, window.innerWidth/2, window.innerHeight/2 ) + vec2.set( center, this.width/2, this.height/2 ) let dir2d = vec2.clone(dir, dir) vec2.subtract( dir2d, dir2d, center ) vec2.normalize( dir2d, dir2d ); @@ -247,39 +287,6 @@ class WebglGlobe { this.renderer.clear() this.renderer.render(this.scene, this.camera); } - - // initPointer() { - // this.currPointer = [0,0] - // this.pointer = [0,0] - // this.pointerRatio = [0,0] - // this._onPointerDown = this._onPointerDown.bind(this) - // this._onPointerMove = this._onPointerMove.bind(this) - // this._onPointerUp = this._onPointerUp.bind(this) - // this._handleOrientation = this._handleOrientation.bind(this) - // document.addEventListener(support.pointerdown, this._onPointerDown, false); - // document.addEventListener(support.pointermove, this._onPointerMove, false); - // document.addEventListener(support.pointerup, this._onPointerUp, false); - // window.addEventListener('deviceorientation', this._handleOrientation); - // } - // _handleOrientation(event) { - // this.pointerRatio[0] = (event.gamma) / 25 - // this.pointerRatio[1] = (event.beta-45) / 25 - // } - // _onPointerDown() { - // this._isPointerDown = true; - // } - // _onPointerUp() { - // this._isPointerDown = false - // } - // _onPointerMove(event) { - // let pe = support.touch && event.type != 'mousemove' ? (event.touches[0] || event.changedTouches[0]) : event; - // this.pointer[0] = pe.pageX; - // this.pointer[1] = pe.pageY; - // this.pointer[1] -= window.pageYOffset || document.documentElement.scrollTop - // this.pointerRatio[0] = (this.pointer[0] / window.innerWidth - .5) * 2 - // this.pointerRatio[1] = (this.pointer[1] / window.innerHeight - .5) * 2 - // } - } // window.WebglGlobe = WebglGlobe diff --git a/src/molecules/InteractiveGlobe.svelte b/src/molecules/InteractiveGlobe.svelte index 11046b0..2033c8e 100644 --- a/src/molecules/InteractiveGlobe.svelte +++ b/src/molecules/InteractiveGlobe.svelte @@ -8,7 +8,10 @@ let globe // Functions - const resize = () => globe.resize() + const resize = () => { + globe.resize() + globe.update() + } const update = () => { requestAnimationFrame(update) globe.update() @@ -20,37 +23,36 @@ */ onMount(async () => { // For browser only - // if (process.browser) { - // // Import libraries and code - // let WebglGlobe - // await import('globe').then(module => WebglGlobe = module.default) + if (process.browser) { + // Import libraries and code + let WebglGlobe + await import('globe').then(module => WebglGlobe = module.default) - // // Init the globe from library - // globe = new WebglGlobe({ - // el: scope, - // texture: '/img/globe/map-4k.png', - // markers: [...$locations.map(location => { - // return { - // name: location.name, - // slug: location.slug, - // countrySlug: location.country.slug, - // lat: location.coordinates.lat, - // lng: location.coordinates.lng - // } - // })] - // }) + // Init the globe from library + globe = new WebglGlobe({ + el: scope, + texture: '/img/globe/map-2k.png', + markers: [...$locations.map(location => { + return { + name: location.name, + slug: location.slug, + countryName: location.country.name, + countrySlug: location.country.slug, + lat: location.coordinates.lat, + lng: location.coordinates.lng + } + })], + onLinkClicked: () => {}, + cameraDistance: 3 + }) - // // Run the globe - // resize() - // update() - // } + // Run the globe + resize() + update() + } }) - + -
-
- -
-
+
diff --git a/src/routes/choose.svelte b/src/routes/choose.svelte index aa39e7b..e2352a1 100644 --- a/src/routes/choose.svelte +++ b/src/routes/choose.svelte @@ -11,7 +11,7 @@ // Components import IconArrow from 'atoms/IconArrow' import TitleSite from 'atoms/TitleSite' - import Globe from 'molecules/InteractiveGlobe' + import InteractiveGlobe from 'molecules/InteractiveGlobe' import Locations from 'organisms/Locations' import Footer from 'organisms/Footer' import Transition from 'utils/Transition' @@ -65,7 +65,7 @@
- + diff --git a/src/routes/credits.svelte b/src/routes/credits.svelte index 55e49b6..cc277aa 100644 --- a/src/routes/credits.svelte +++ b/src/routes/credits.svelte @@ -78,8 +78,10 @@ {/if} - +
+ +