From a4feadb80fdba1db45bd942825c4aeda9caf1e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fe=CC=81lix=20Pe=CC=81ault?= Date: Thu, 9 Apr 2020 23:10:50 +0200 Subject: [PATCH] Globe: Add hover on marker dot, Comment code --- src/globe/index.js | 170 +++++++++++++++++--------------- src/style/_animations.scss | 14 +++ src/style/molecules/_globe.scss | 8 ++ 3 files changed, 114 insertions(+), 78 deletions(-) diff --git a/src/globe/index.js b/src/globe/index.js index 6b31f05..30e9a32 100644 --- a/src/globe/index.js +++ b/src/globe/index.js @@ -9,12 +9,12 @@ import GlobeVS from './globe-vs' 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); + 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]; + ] : [0,0,0] } function lonLatToVector3(lng, lat) { @@ -22,30 +22,29 @@ function lonLatToVector3(lng, lat) { theta = (lng+180)*(Math.PI/180), x = -( Math.sin(phi) * Math.cos(theta) ), z = Math.sin(phi) * Math.sin(theta), - y = Math.cos(phi); - return [x,y,z]; + y = Math.cos(phi) + return [x,y,z] } class WebglGlobe { // Constructor constructor (options) { + // Camera position cache + this.cameraX = 0 + this.cameraY = 0 + this.cameraZ = 0 - //camera position cache - this.cameraX = 0; - this.cameraY = 0; - this.cameraZ = 0; - - this.options = options; - this.$el = options.el; + this.options = options + this.$el = options.el this.cities = options.markers - let gl; + let gl let canvas = document.createElement('canvas') - try { gl = canvas.getContext("webgl"); } + try { gl = canvas.getContext('webgl') } catch (x) { - try { gl = canvas.getContext("experimental-webgl"); } - catch (x) { gl = null; } + try { gl = canvas.getContext('experimental-webgl') } + catch (x) { gl = null } } - this.supportWebgl = gl !== null; + this.supportWebgl = gl !== null if (this.supportWebgl) { this.buildWebglScene() this.resize() @@ -58,14 +57,15 @@ class WebglGlobe { this.renderer = new Renderer({ alpha: this.alpha, antialias: window.innerWidth < 768 ? true : false,// this.antialias, - }); - this.$el.appendChild(this.renderer.canvas); + }) + this.$el.appendChild(this.renderer.canvas) + // Load texture this.texture = Texture.fromUrl(this.renderer.gl, this.options.texture, { - loaded: ()=>{ - requestAnimationFrame(()=>{ - requestAnimationFrame(()=>{ - this.imageLoaded = true; + loaded: () => { + requestAnimationFrame(() => { + requestAnimationFrame(() => { + this.imageLoaded = true }) }) } @@ -73,6 +73,7 @@ class WebglGlobe { this.scene = new Container() + // Setup camera this.camera = new Camera({ fov: settings.fov, near: 1, @@ -83,14 +84,14 @@ class WebglGlobe { 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.cameraPosition = vec3.create(); + this.cameraDirection = vec3.create() + this.cameraPosition = vec3.create() - this.globeMesh = new Mesh(); + this.globeMesh = new Mesh() this.globeMesh.material = new Material(this.renderer.gl, { uniforms: { tInput: this.texture @@ -108,47 +109,63 @@ class WebglGlobe { this.markers = [] let markers = this.cities - for (let i=0; i{ + // Add city label + let spanCity = document.createElement('span') + spanCity.classList.add('marker__city') + spanCity.innerHTML = markers[i].name + span.appendChild(spanCity) + + // Add country label + let spanCountry = document.createElement('span') + spanCountry.classList.add('marker__country') + spanCountry.innerHTML = markers[i].countryName + span.appendChild(spanCountry) + + // Add class + el.classList.add('marker') + + // Callback on click + el.addEventListener('click', () => { 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) + // Add class on hover + el.addEventListener('mouseenter', () => el.classList.add('hover')) + el.addEventListener('animationend', () => el.classList.remove('hover')) - el.classList.add('marker') + // Append marker to HTML this.$el.appendChild( el ) this.markers.push({ mesh: markerMesh, @@ -161,8 +178,8 @@ class WebglGlobe { // Resize method resize () { - this.width = this.$el.clientWidth; - this.height = this.$el.clientHeight; + this.width = this.$el.clientWidth + this.height = this.$el.clientHeight // console.log('GLOBE RESIZE', this.width, this.height) @@ -171,12 +188,11 @@ class WebglGlobe { if (!this.supportWebgl) { return } - this.renderer.setPixelRatio(window.innerWidth < 768 ? 1 : settings.devicePixelRatio); + 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() - this.camera.lookAt[0] = 0 this.camera.lookAt[1] = 0 this.camera.lookAt[2] = 0 @@ -186,19 +202,18 @@ class WebglGlobe { this.camera.position[0] = 0 this.camera.position[1] = 0 this.camera.position[2] = (3) / 2 / Math.tan(Math.PI * settings.fov / 360) - this.camera.render(); - - vec3.set(this.cameraPosition, this.camera.worldMatrix[12], this.camera.worldMatrix[13], this.camera.worldMatrix[14]); - vec3.copy(this.cameraDirection, this.camera.lookAt); - vec3.subtract(this.cameraDirection, this.cameraDirection, this.cameraPosition); - vec3.normalize(this.cameraDirection, this.cameraDirection); - mat4.copy(this.viewProjectionMatrix, this.camera.projectionMatrix); - mat4.multiply(this.viewProjectionMatrix, this.viewProjectionMatrix, this.camera.inverseWorldMatrix); + this.camera.render() + vec3.set(this.cameraPosition, this.camera.worldMatrix[12], this.camera.worldMatrix[13], this.camera.worldMatrix[14]) + vec3.copy(this.cameraDirection, this.camera.lookAt) + vec3.subtract(this.cameraDirection, this.cameraDirection, this.cameraPosition) + vec3.normalize(this.cameraDirection, this.cameraDirection) + mat4.copy(this.viewProjectionMatrix, this.camera.projectionMatrix) + mat4.multiply(this.viewProjectionMatrix, this.viewProjectionMatrix, this.camera.inverseWorldMatrix) let refPos = vec3.create() vec3.set(refPos, 0, 1, 0 ) - vec3.transformMat4(refPos, refPos, this.viewProjectionMatrix); + vec3.transformMat4(refPos, refPos, this.viewProjectionMatrix) let refx = ( (refPos[0] + 1) / 2) * this.width let refy = (1. - (refPos[1] + 1) / 2) * this.height @@ -209,33 +224,32 @@ class WebglGlobe { let dir2d2 = vec2.clone(dir2, dir2) vec2.subtract( dir2d2, dir2d2, center2 ) this.circleScreenSize = vec2.length( dir2d2 ) * 1.04 - } // Update update () { - if (!this.supportWebgl){ - return; + if (!this.supportWebgl) { + return } - //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]); - vec3.copy(this.cameraDirection, this.camera.lookAt); - vec3.subtract(this.cameraDirection, this.cameraDirection, this.cameraPosition); - vec3.normalize(this.cameraDirection, this.cameraDirection); - mat4.copy(this.viewProjectionMatrix, this.camera.projectionMatrix); - mat4.multiply(this.viewProjectionMatrix, this.viewProjectionMatrix, this.camera.inverseWorldMatrix); - mat4.invert(this.inverseViewProjectionMatrix, this.viewProjectionMatrix); + // Manually call this as we prevent the camera from update between passes + this.camera.update() + vec3.set(this.cameraPosition, this.camera.worldMatrix[12], this.camera.worldMatrix[13], this.camera.worldMatrix[14]) + vec3.copy(this.cameraDirection, this.camera.lookAt) + vec3.subtract(this.cameraDirection, this.cameraDirection, this.cameraPosition) + vec3.normalize(this.cameraDirection, this.cameraDirection) + mat4.copy(this.viewProjectionMatrix, this.camera.projectionMatrix) + mat4.multiply(this.viewProjectionMatrix, this.viewProjectionMatrix, this.camera.inverseWorldMatrix) + mat4.invert(this.inverseViewProjectionMatrix, this.viewProjectionMatrix) - let needsUpdate = false; + 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; + this.cameraX = this.camera.worldMatrix[12] + this.cameraY = this.camera.worldMatrix[13] + this.cameraZ = this.camera.worldMatrix[14] + needsUpdate = true } if (!needsUpdate && this.imageLoaded) { @@ -245,9 +259,9 @@ class WebglGlobe { // console.log('RENDER WEBGL') let screenPos = vec3.create() - this.markers.forEach((marker, i)=>{ + this.markers.forEach((marker, i) => { vec3.set(screenPos, marker.position[0], marker.position[1], marker.position[2] ) - vec3.transformMat4(screenPos, screenPos, this.viewProjectionMatrix); + vec3.transformMat4(screenPos, screenPos, this.viewProjectionMatrix) let x = ( (screenPos[0] + 1) / 2) * this.width let y = (1. - (screenPos[1] + 1) / 2) * this.height @@ -259,7 +273,7 @@ class WebglGlobe { let V = vec3.create() vec3.set( V, marker.position[0], marker.position[1], marker.position[2] ) vec3.subtract( V, V, this.cameraPosition ) - vec3.normalize( V, V ); + vec3.normalize( V, V ) //behind if ( vec3.dot( V, N ) * -1 < 0 ) { @@ -269,15 +283,15 @@ class WebglGlobe { vec2.set( center, this.width/2, this.height/2 ) let dir2d = vec2.clone(dir, dir) vec2.subtract( dir2d, dir2d, center ) - vec2.normalize( dir2d, dir2d ); - vec2.scale( dir2d, dir2d, this.circleScreenSize ); + vec2.normalize( dir2d, dir2d ) + vec2.scale( dir2d, dir2d, this.circleScreenSize ) vec2.add(dir2d, dir2d, center) - marker.el.style.transform = `translate(${dir2d[0]}px, ${dir2d[1]}px) translateZ(0)`; + marker.el.style.transform = `translate(${dir2d[0]}px, ${dir2d[1]}px) translateZ(0)` marker.el.classList.remove('is-active') } else { //vec3.dot( V, N ) * -1 < 0 ? (1 - vec3.dot( V, N ) / 0.1 ) : 1;//Math.max( 0, vec3.dot( N, V ) ); - marker.el.style.transform = `translate(${x}px, ${y}px) translateZ(0)`; + marker.el.style.transform = `translate(${x}px, ${y}px) translateZ(0)` marker.el.classList.add('is-active') } // marker.el.style.opacity = 1. @@ -285,7 +299,7 @@ class WebglGlobe { this.renderer.clearColor(0,0,0,0) this.renderer.clear() - this.renderer.render(this.scene, this.camera); + this.renderer.render(this.scene, this.camera) } } diff --git a/src/style/_animations.scss b/src/style/_animations.scss index 6e53ae4..93915ae 100644 --- a/src/style/_animations.scss +++ b/src/style/_animations.scss @@ -112,3 +112,17 @@ 0% { transform: translateY(0); } 50% { transform: translateY(-3px); } } + + +/* +** Globe +*/ +// Marker +@keyframes globeMarkerPulse { + 0% { + box-shadow: 0 0 0 0 rgba($color-secondary, 1); + } + 100% { + box-shadow: 0 0 0 32px rgba(#fff, 0); + } +} diff --git a/src/style/molecules/_globe.scss b/src/style/molecules/_globe.scss index df2be0a..b7db8e1 100644 --- a/src/style/molecules/_globe.scss +++ b/src/style/molecules/_globe.scss @@ -26,6 +26,7 @@ // Marker .marker { position: absolute; + z-index: 2; cursor: pointer; display: block; top: 0; @@ -35,11 +36,18 @@ border-radius: 100%; opacity: 1; background: #ff6c89; + will-change: transform; span { transition: color 0.4s $ease-quart; } + // Hover glow effect + &.hover { + animation: globeMarkerPulse 1s; + } + + // Label &__label { position: absolute; bottom: -230%;