Globe: Add hover on marker dot, Comment code
This commit is contained in:
@@ -9,12 +9,12 @@ import GlobeVS from './globe-vs'
|
|||||||
import GlobeFS from './globe-fs'
|
import GlobeFS from './globe-fs'
|
||||||
|
|
||||||
function hexToRgb(hex) {
|
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 ? [
|
return result ? [
|
||||||
parseInt(result[1], 16) / 255,
|
parseInt(result[1], 16) / 255,
|
||||||
parseInt(result[2], 16) / 255,
|
parseInt(result[2], 16) / 255,
|
||||||
parseInt(result[3], 16) / 255
|
parseInt(result[3], 16) / 255
|
||||||
] : [0,0,0];
|
] : [0,0,0]
|
||||||
}
|
}
|
||||||
|
|
||||||
function lonLatToVector3(lng, lat) {
|
function lonLatToVector3(lng, lat) {
|
||||||
@@ -22,30 +22,29 @@ function lonLatToVector3(lng, lat) {
|
|||||||
theta = (lng+180)*(Math.PI/180),
|
theta = (lng+180)*(Math.PI/180),
|
||||||
x = -( Math.sin(phi) * Math.cos(theta) ),
|
x = -( Math.sin(phi) * Math.cos(theta) ),
|
||||||
z = Math.sin(phi) * Math.sin(theta),
|
z = Math.sin(phi) * Math.sin(theta),
|
||||||
y = Math.cos(phi);
|
y = Math.cos(phi)
|
||||||
return [x,y,z];
|
return [x,y,z]
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebglGlobe {
|
class WebglGlobe {
|
||||||
// Constructor
|
// Constructor
|
||||||
constructor (options) {
|
constructor (options) {
|
||||||
|
// Camera position cache
|
||||||
|
this.cameraX = 0
|
||||||
|
this.cameraY = 0
|
||||||
|
this.cameraZ = 0
|
||||||
|
|
||||||
//camera position cache
|
this.options = options
|
||||||
this.cameraX = 0;
|
this.$el = options.el
|
||||||
this.cameraY = 0;
|
|
||||||
this.cameraZ = 0;
|
|
||||||
|
|
||||||
this.options = options;
|
|
||||||
this.$el = options.el;
|
|
||||||
this.cities = options.markers
|
this.cities = options.markers
|
||||||
let gl;
|
let gl
|
||||||
let canvas = document.createElement('canvas')
|
let canvas = document.createElement('canvas')
|
||||||
try { gl = canvas.getContext("webgl"); }
|
try { gl = canvas.getContext('webgl') }
|
||||||
catch (x) {
|
catch (x) {
|
||||||
try { gl = canvas.getContext("experimental-webgl"); }
|
try { gl = canvas.getContext('experimental-webgl') }
|
||||||
catch (x) { gl = null; }
|
catch (x) { gl = null }
|
||||||
}
|
}
|
||||||
this.supportWebgl = gl !== null;
|
this.supportWebgl = gl !== null
|
||||||
if (this.supportWebgl) {
|
if (this.supportWebgl) {
|
||||||
this.buildWebglScene()
|
this.buildWebglScene()
|
||||||
this.resize()
|
this.resize()
|
||||||
@@ -58,14 +57,15 @@ class WebglGlobe {
|
|||||||
this.renderer = new Renderer({
|
this.renderer = new Renderer({
|
||||||
alpha: this.alpha,
|
alpha: this.alpha,
|
||||||
antialias: window.innerWidth < 768 ? true : false,// this.antialias,
|
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, {
|
this.texture = Texture.fromUrl(this.renderer.gl, this.options.texture, {
|
||||||
loaded: ()=>{
|
loaded: () => {
|
||||||
requestAnimationFrame(()=>{
|
requestAnimationFrame(() => {
|
||||||
requestAnimationFrame(()=>{
|
requestAnimationFrame(() => {
|
||||||
this.imageLoaded = true;
|
this.imageLoaded = true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -73,6 +73,7 @@ class WebglGlobe {
|
|||||||
|
|
||||||
this.scene = new Container()
|
this.scene = new Container()
|
||||||
|
|
||||||
|
// Setup camera
|
||||||
this.camera = new Camera({
|
this.camera = new Camera({
|
||||||
fov: settings.fov,
|
fov: settings.fov,
|
||||||
near: 1,
|
near: 1,
|
||||||
@@ -83,14 +84,14 @@ class WebglGlobe {
|
|||||||
lookAt: [0,0,0],
|
lookAt: [0,0,0],
|
||||||
position: [0,0, (this.options.cameraDistance) / 2 / Math.tan(Math.PI * settings.fov / 360)],
|
position: [0,0, (this.options.cameraDistance) / 2 / Math.tan(Math.PI * settings.fov / 360)],
|
||||||
pointerParent: this.$el
|
pointerParent: this.$el
|
||||||
});
|
})
|
||||||
this.camera.lookAt = vec3.create()
|
this.camera.lookAt = vec3.create()
|
||||||
this.inverseViewProjectionMatrix = mat4.create()
|
this.inverseViewProjectionMatrix = mat4.create()
|
||||||
this.viewProjectionMatrix = mat4.create()
|
this.viewProjectionMatrix = mat4.create()
|
||||||
this.cameraDirection = vec3.create();
|
this.cameraDirection = vec3.create()
|
||||||
this.cameraPosition = vec3.create();
|
this.cameraPosition = vec3.create()
|
||||||
|
|
||||||
this.globeMesh = new Mesh();
|
this.globeMesh = new Mesh()
|
||||||
this.globeMesh.material = new Material(this.renderer.gl, {
|
this.globeMesh.material = new Material(this.renderer.gl, {
|
||||||
uniforms: {
|
uniforms: {
|
||||||
tInput: this.texture
|
tInput: this.texture
|
||||||
@@ -108,47 +109,63 @@ class WebglGlobe {
|
|||||||
this.markers = []
|
this.markers = []
|
||||||
let markers = this.cities
|
let markers = this.cities
|
||||||
|
|
||||||
for (let i=0; i<markers.length; i++) {
|
// Instance all markers
|
||||||
|
for (let i = 0; i < markers.length; i++) {
|
||||||
let markerMesh = new Mesh();
|
let markerMesh = new Mesh();
|
||||||
markerMesh.material = new Material(this.renderer.gl, {
|
markerMesh.material = new Material(this.renderer.gl, {
|
||||||
blend: true,
|
blend: true,
|
||||||
uniforms: {
|
uniforms: {
|
||||||
color: hexToRgb( "#FF6C89" ),
|
color: hexToRgb('#FF6C89'),
|
||||||
alpha: 0,
|
alpha: 0,
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
markerMesh.geometry = new SphereGeometryBuffer(this.renderer.gl, {
|
markerMesh.geometry = new SphereGeometryBuffer(this.renderer.gl, {
|
||||||
radius: 0.01,
|
radius: 0.01,
|
||||||
widthSegments: 10, heightSegments: 10
|
widthSegments: 10, heightSegments: 10
|
||||||
})
|
})
|
||||||
let p = lonLatToVector3( markers[i].lng, markers[i].lat )
|
|
||||||
|
|
||||||
|
// Position marker
|
||||||
|
let p = lonLatToVector3( markers[i].lng, markers[i].lat )
|
||||||
markerMesh.position[0] = p[0]
|
markerMesh.position[0] = p[0]
|
||||||
markerMesh.position[1] = p[1]
|
markerMesh.position[1] = p[1]
|
||||||
markerMesh.position[2] = p[2]
|
markerMesh.position[2] = p[2]
|
||||||
this.scene.add( markerMesh )
|
this.scene.add( markerMesh )
|
||||||
|
|
||||||
|
// Wrap marker in link
|
||||||
let el = document.createElement('a')
|
let el = document.createElement('a')
|
||||||
el.setAttribute('href', '/location/' + markers[i].countrySlug + '/' + markers[i].slug )
|
el.setAttribute('href', '/location/' + markers[i].countrySlug + '/' + markers[i].slug )
|
||||||
el.setAttribute('sapper-noscroll','')
|
el.setAttribute('sapper-noscroll','')
|
||||||
|
|
||||||
|
// Add label
|
||||||
let span = document.createElement('span')
|
let span = document.createElement('span')
|
||||||
span.classList.add('marker__label')
|
span.classList.add('marker__label')
|
||||||
el.appendChild(span)
|
el.appendChild(span)
|
||||||
|
|
||||||
el.addEventListener('click', ()=>{
|
// 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()
|
this.options.onLinkClicked && this.options.onLinkClicked()
|
||||||
})
|
})
|
||||||
|
|
||||||
let spanCity = document.createElement('span')
|
// Add class on hover
|
||||||
spanCity.classList.add('marker__city')
|
el.addEventListener('mouseenter', () => el.classList.add('hover'))
|
||||||
spanCity.innerHTML = markers[i].name;
|
el.addEventListener('animationend', () => el.classList.remove('hover'))
|
||||||
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')
|
// Append marker to HTML
|
||||||
this.$el.appendChild( el )
|
this.$el.appendChild( el )
|
||||||
this.markers.push({
|
this.markers.push({
|
||||||
mesh: markerMesh,
|
mesh: markerMesh,
|
||||||
@@ -161,8 +178,8 @@ class WebglGlobe {
|
|||||||
|
|
||||||
// Resize method
|
// Resize method
|
||||||
resize () {
|
resize () {
|
||||||
this.width = this.$el.clientWidth;
|
this.width = this.$el.clientWidth
|
||||||
this.height = this.$el.clientHeight;
|
this.height = this.$el.clientHeight
|
||||||
|
|
||||||
// console.log('GLOBE RESIZE', this.width, this.height)
|
// console.log('GLOBE RESIZE', this.width, this.height)
|
||||||
|
|
||||||
@@ -171,12 +188,11 @@ class WebglGlobe {
|
|||||||
if (!this.supportWebgl) {
|
if (!this.supportWebgl) {
|
||||||
return
|
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.renderer.resize(settings.resolution[0], settings.resolution[1])
|
||||||
this.camera.aspect = settings.resolution[0] / settings.resolution[1]
|
this.camera.aspect = settings.resolution[0] / settings.resolution[1]
|
||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
|
|
||||||
|
|
||||||
this.camera.lookAt[0] = 0
|
this.camera.lookAt[0] = 0
|
||||||
this.camera.lookAt[1] = 0
|
this.camera.lookAt[1] = 0
|
||||||
this.camera.lookAt[2] = 0
|
this.camera.lookAt[2] = 0
|
||||||
@@ -186,19 +202,18 @@ class WebglGlobe {
|
|||||||
this.camera.position[0] = 0
|
this.camera.position[0] = 0
|
||||||
this.camera.position[1] = 0
|
this.camera.position[1] = 0
|
||||||
this.camera.position[2] = (3) / 2 / Math.tan(Math.PI * settings.fov / 360)
|
this.camera.position[2] = (3) / 2 / Math.tan(Math.PI * settings.fov / 360)
|
||||||
this.camera.render();
|
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);
|
|
||||||
|
|
||||||
|
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()
|
let refPos = vec3.create()
|
||||||
vec3.set(refPos, 0, 1, 0 )
|
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 refx = ( (refPos[0] + 1) / 2) * this.width
|
||||||
let refy = (1. - (refPos[1] + 1) / 2) * this.height
|
let refy = (1. - (refPos[1] + 1) / 2) * this.height
|
||||||
|
|
||||||
@@ -209,33 +224,32 @@ class WebglGlobe {
|
|||||||
let dir2d2 = vec2.clone(dir2, dir2)
|
let dir2d2 = vec2.clone(dir2, dir2)
|
||||||
vec2.subtract( dir2d2, dir2d2, center2 )
|
vec2.subtract( dir2d2, dir2d2, center2 )
|
||||||
this.circleScreenSize = vec2.length( dir2d2 ) * 1.04
|
this.circleScreenSize = vec2.length( dir2d2 ) * 1.04
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
update () {
|
update () {
|
||||||
if (!this.supportWebgl){
|
if (!this.supportWebgl) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//manually call this as we prevent ithe camera from update between passes
|
// Manually call this as we prevent the camera from update between passes
|
||||||
this.camera.update();
|
this.camera.update()
|
||||||
vec3.set(this.cameraPosition, this.camera.worldMatrix[12], this.camera.worldMatrix[13], this.camera.worldMatrix[14]);
|
vec3.set(this.cameraPosition, this.camera.worldMatrix[12], this.camera.worldMatrix[13], this.camera.worldMatrix[14])
|
||||||
vec3.copy(this.cameraDirection, this.camera.lookAt);
|
vec3.copy(this.cameraDirection, this.camera.lookAt)
|
||||||
vec3.subtract(this.cameraDirection, this.cameraDirection, this.cameraPosition);
|
vec3.subtract(this.cameraDirection, this.cameraDirection, this.cameraPosition)
|
||||||
vec3.normalize(this.cameraDirection, this.cameraDirection);
|
vec3.normalize(this.cameraDirection, this.cameraDirection)
|
||||||
mat4.copy(this.viewProjectionMatrix, this.camera.projectionMatrix);
|
mat4.copy(this.viewProjectionMatrix, this.camera.projectionMatrix)
|
||||||
mat4.multiply(this.viewProjectionMatrix, this.viewProjectionMatrix, this.camera.inverseWorldMatrix);
|
mat4.multiply(this.viewProjectionMatrix, this.viewProjectionMatrix, this.camera.inverseWorldMatrix)
|
||||||
mat4.invert(this.inverseViewProjectionMatrix, this.viewProjectionMatrix);
|
mat4.invert(this.inverseViewProjectionMatrix, this.viewProjectionMatrix)
|
||||||
|
|
||||||
let needsUpdate = false;
|
let needsUpdate = false
|
||||||
if (this.cameraX != this.camera.worldMatrix[12] ||
|
if (this.cameraX != this.camera.worldMatrix[12] ||
|
||||||
this.cameraY != this.camera.worldMatrix[13] ||
|
this.cameraY != this.camera.worldMatrix[13] ||
|
||||||
this.cameraZ != this.camera.worldMatrix[14]) {
|
this.cameraZ != this.camera.worldMatrix[14]) {
|
||||||
this.cameraX = this.camera.worldMatrix[12];
|
this.cameraX = this.camera.worldMatrix[12]
|
||||||
this.cameraY = this.camera.worldMatrix[13];
|
this.cameraY = this.camera.worldMatrix[13]
|
||||||
this.cameraZ = this.camera.worldMatrix[14];
|
this.cameraZ = this.camera.worldMatrix[14]
|
||||||
needsUpdate = true;
|
needsUpdate = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!needsUpdate && this.imageLoaded) {
|
if (!needsUpdate && this.imageLoaded) {
|
||||||
@@ -245,9 +259,9 @@ class WebglGlobe {
|
|||||||
// console.log('RENDER WEBGL')
|
// console.log('RENDER WEBGL')
|
||||||
|
|
||||||
let screenPos = vec3.create()
|
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.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 x = ( (screenPos[0] + 1) / 2) * this.width
|
||||||
let y = (1. - (screenPos[1] + 1) / 2) * this.height
|
let y = (1. - (screenPos[1] + 1) / 2) * this.height
|
||||||
@@ -259,7 +273,7 @@ class WebglGlobe {
|
|||||||
let V = vec3.create()
|
let V = vec3.create()
|
||||||
vec3.set( V, marker.position[0], marker.position[1], marker.position[2] )
|
vec3.set( V, marker.position[0], marker.position[1], marker.position[2] )
|
||||||
vec3.subtract( V, V, this.cameraPosition )
|
vec3.subtract( V, V, this.cameraPosition )
|
||||||
vec3.normalize( V, V );
|
vec3.normalize( V, V )
|
||||||
|
|
||||||
//behind
|
//behind
|
||||||
if ( vec3.dot( V, N ) * -1 < 0 ) {
|
if ( vec3.dot( V, N ) * -1 < 0 ) {
|
||||||
@@ -269,15 +283,15 @@ class WebglGlobe {
|
|||||||
vec2.set( center, this.width/2, this.height/2 )
|
vec2.set( center, this.width/2, this.height/2 )
|
||||||
let dir2d = vec2.clone(dir, dir)
|
let dir2d = vec2.clone(dir, dir)
|
||||||
vec2.subtract( dir2d, dir2d, center )
|
vec2.subtract( dir2d, dir2d, center )
|
||||||
vec2.normalize( dir2d, dir2d );
|
vec2.normalize( dir2d, dir2d )
|
||||||
vec2.scale( dir2d, dir2d, this.circleScreenSize );
|
vec2.scale( dir2d, dir2d, this.circleScreenSize )
|
||||||
vec2.add(dir2d, dir2d, center)
|
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')
|
marker.el.classList.remove('is-active')
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//vec3.dot( V, N ) * -1 < 0 ? (1 - vec3.dot( V, N ) / 0.1 ) : 1;//Math.max( 0, vec3.dot( N, V ) );
|
//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.classList.add('is-active')
|
||||||
}
|
}
|
||||||
// marker.el.style.opacity = 1.
|
// marker.el.style.opacity = 1.
|
||||||
@@ -285,7 +299,7 @@ class WebglGlobe {
|
|||||||
|
|
||||||
this.renderer.clearColor(0,0,0,0)
|
this.renderer.clearColor(0,0,0,0)
|
||||||
this.renderer.clear()
|
this.renderer.clear()
|
||||||
this.renderer.render(this.scene, this.camera);
|
this.renderer.render(this.scene, this.camera)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,3 +112,17 @@
|
|||||||
0% { transform: translateY(0); }
|
0% { transform: translateY(0); }
|
||||||
50% { transform: translateY(-3px); }
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
// Marker
|
// Marker
|
||||||
.marker {
|
.marker {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -35,11 +36,18 @@
|
|||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background: #ff6c89;
|
background: #ff6c89;
|
||||||
|
will-change: transform;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
transition: color 0.4s $ease-quart;
|
transition: color 0.4s $ease-quart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hover glow effect
|
||||||
|
&.hover {
|
||||||
|
animation: globeMarkerPulse 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label
|
||||||
&__label {
|
&__label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -230%;
|
bottom: -230%;
|
||||||
|
|||||||
Reference in New Issue
Block a user