![AWS se aprofunda em IA, potência de chip... e economia de custos](https://optimuscloud.com.br/wp-content/uploads/2024/01/1706190046_AWS-se-aprofunda-em-IA-potencia-de-chip-e-economia-150x150.jpg)
AWS se aprofunda em IA, potência de chip… e economia de custos
25 de janeiro de 2024![Jogo da velha com Python e Tkinter](https://optimuscloud.com.br/wp-content/uploads/2024/01/1706194649_Jogo-da-velha-com-Python-e-Tkinter-150x150.jpg)
Jogo da velha com Python e Tkinter
25 de janeiro de 2024Você viu o Globo BFCM (Black Friday Cyber Monday) deste ano do Shopify? Eu fiz e adorei. Todos os anos a equipe do Shopify arrasa e este ano não foi exceção! No entanto, a menos que você possua habilidades de engenharia WebGL de nível NASA, criar um globo 3D é difícil. Felizmente o react-globe.gl de Vasco Asturiano está aqui para ajudar. Usei essa biblioteca em várias ocasiões, mas desta vez queria ver o quão perto poderia chegar do globo BFCM ’23 do Shopify.
Você pode ver uma prévia e todo o código referenciado nesta postagem nos links abaixo.
- 🚀 Visualização: https://tns-react-3d-globe.netlify.app
- ⚙️ Código: https://github.com/PaulieScanlon/tns-react-3d-globe
Mas antes de explicar como criei o Globe, falarei sobre os fundamentos do uso do react-globe-gl.
Instalação
Para usar react-globe.gl, primeiro você precisa instalá-lo.
npm install react-globe.gl
Depois que o pacote for instalado, você precisará importá-lo.
import Globe from 'react-globe.gl'; const Page = () => { return null } export default Page
Imagem Básica
![Imagem básica da terra do globo 3D](https://optimuscloud.com.br/wp-content/uploads/2024/01/1706194138_64_Recriando-o-BFCM-Globe-do-Shopify-usando-react-globegl.jpg)
Neste exemplo, abordarei os fundamentos da criação do globo, da adição de uma imagem para que o globo se pareça com a Terra e de como você pode traçar pontos ao redor do globo.
- 🚀 Visualização: https://tns-react-3d-globe.netlify.app/basic-image
- ⚙️ Código: src/routes/basic-image.jsx
// src/routes/basic-image.jsx import Globe from 'react-globe.gl'; import globeImage from '../assets/earth-dark.jpg'; const Page = () => { const myData = ( { lat: 29.953204744601763, lng: -90.08925929478903, altitude: 0.4, color: '#00ff33', }, { lat: 28.621322361013092, lng: 77.20347613099612, altitude: 0.4, color: '#ff0000', }, { lat: -43.1571459086602, lng: 172.72338919659848, altitude: 0.4, color: '#ffff00', }, ); return ( <div className="cursor-move"> <Globe globeImageUrl={globeImage} pointsData={myData} pointAltitude="altitude" pointColor="color" /> </div> ); }; export default Page;
globoImageUrl
Usando este suporte você pode passar uma imagem real da terra para ser renderizada no globo. Para garantir que os pontos reais de latitude e longitude estejam alinhados com os países, a imagem da Terra precisa ser preparada corretamente. No link a seguir, você encontrará diversas variações para escolher; tudo funcionará com react-globe.gl: https://unpkg.com/browse/world-atlas@2.0.2/
pontosDados
Esta matriz de dados contém quatro pares de valores-chave. Cada um deve ser autoexplicativo o suficiente e, quando transmitido ao globo usando a propriedade pointsData, aparecerá em suas localizações geográficas corretas.
pontoAltitude
Esta propriedade permite definir o “tamanho” de cada um dos pontos. Quanto maior o número, mais “alto” é o ponto. A string passada para esta propriedade é um nome de chave da matriz de dados.
pointColor
Este adereço permite definir a cor de cada um dos pontos. A string passada para esta propriedade é um nome de chave da matriz de dados.
Hexágono GeoJson
Neste exemplo, utilizo dados geojson para exibir os países do mundo como hexágonos, em vez de usar uma imagem da Terra.
- 🚀 Visualização: https://tns-react-3d-globe.netlify.app/geojson-hexagon
- ⚙️ Código: src/routes/geojson-hexagon.jsx
// src/routes/geojson-hexagon.jsx import Globe from 'react-globe.gl'; import globeJson from '../assets/countries_110m.json'; const Page = () => { const myData = ( { lat: 29.953204744601763, lng: -90.08925929478903, altitude: 0.4, color: '#00ff33', }, { lat: 28.621322361013092, lng: 77.20347613099612, altitude: 0.4, color: '#ff0000', }, { lat: -43.1571459086602, lng: 172.72338919659848, altitude: 0.4, color: '#ffff00', }, ); return ( <div className="cursor-move"> <Globe hexPolygonsData={globeJson.features} hexPolygonColor={(geometry) => { return ('#0000ff', '#0000cc', '#000099', '#000066')(geometry.properties.abbrev_len % 4); }} pointsData={myData} pointAltitude="altitude" pointColor="color" /> </div> ); }; export default Page;
dados de polígonos hexadecimais
Usando este suporte você pode passar dados geojson do mundo real para serem renderizados no globo, em vez de uma imagem. No link a seguir você encontrará vários conjuntos de dados que podem ser usados: https://unpkg.com/browse/world-atlas@2.0.2/
hexPolygonColor
Este adereço pode ser usado para mudar a cor de cada país. Para determinar qual cor da primeira matriz usar, eu uso o operador Remainder do JavaScript, ou operador módulo, para criar um índice com base no comprimento dos códigos de abreviatura dos países que são retornados do parâmetro de geometria.
Polígono Geojson
Neste exemplo, utilizo dados geojson para exibir os países do mundo como polígonos, em vez de usar uma imagem da Terra.
- 🚀 Visualização: https://tns-react-3d-globe.netlify.app/geojson-polygon
- ⚙️ Código: src/routes/geojson-polygon.jsx
// src/routes/geojson-polygon.jsx import Globe from 'react-globe.gl'; import globeJson from '../assets/countries_110m.json'; const Page = () => { const myData = ( { lat: 29.953204744601763, lng: -90.08925929478903, altitude: 0.4, color: '#00ff33', }, { lat: 28.621322361013092, lng: 77.20347613099612, altitude: 0.4, color: '#ff0000', }, { lat: -43.1571459086602, lng: 172.72338919659848, altitude: 0.4, color: '#ffff00', }, ); return ( <div className="cursor-move"> <Globe polygonsData={globeJson.features} polygonCapColor={(geometry) => { return ('#0000ff', '#0000cc', '#000099', '#000066')(geometry.properties.abbrev_len % 4); }} polygonSideColor={(geometry) => { return ('#0000ff', '#0000cc', '#000099', '#000066')(geometry.properties.abbrev_len % 4); }} polygonAltitude={0.08} pointsData={myData} pointAltitude="altitude" pointColor="color" /> </div> ); }; export default Page;
polígonosDados
Usando este suporte você pode passar dados geojson do mundo real para serem renderizados no globo, em vez de uma imagem. No link a seguir você encontrará vários conjuntos de dados que podem ser usados: https://unpkg.com/browse/world-atlas@2.0.2/
polígonoCapColor
Este adereço pode ser usado para mudar a cor de cada país. Para determinar qual cor da primeira matriz usar, eu uso o operador Remainder do JavaScript ou módulo para criar um índice baseado no comprimento dos códigos de abreviatura dos países que são retornados do parâmetro de geometria.
polígonoSideColor
Semelhante ao anterior, este adereço controla a cor das “bordas” de cada país.
polígonoAltitude
Este adereço aumenta o nível dos polígonos (você notará mais as cores das “bordas” ao usar este adereço).
Arcos
![Dados de arcos de globo 3D](https://optimuscloud.com.br/wp-content/uploads/2024/01/1706194138_277_Recriando-o-BFCM-Globe-do-Shopify-usando-react-globegl.jpg)
Neste exemplo, conecto os pontos entre si usando arcos. Existem mais algumas opções de configuração para usar arcos, mas o mesmo método que mencionei anteriormente sobre fornecer um nome de chave do objeto de dados ainda se aplica.
- 🚀 Visualização: https://tns-react-3d-globe.netlify.app/arcs-data
- ⚙️ Código: src/routes/arcs-data.jsx
// src/routes/arcs-data.jsx import Globe from 'react-globe.gl'; import globeImage from '../assets/earth-dark.jpg'; const Page = () => { const myData = ( { startLat: 29.953204744601763, startLng: -90.08925929478903, endLat: 28.621322361013092, endLng: 77.20347613099612, color: ('#00ff33', '#ff0000'), stroke: 1, gap: 0.02, dash: 0.02, scale: 0.3, time: 2000, }, { startLat: 28.621322361013092, startLng: 77.20347613099612, endLat: -43.1571459086602, endLng: 172.72338919659848, color: ('#ff0000', '#ffff00'), stroke: 3, gap: 0.05, dash: 0.3, scale: 0.5, time: 8000, }, ); return ( <div className="cursor-move"> <Globe globeImageUrl={globeImage} arcsData={myData} arcColor="color" arcStroke="stroke" arcDashGap='gap' arcDashLength="dash" arcAltitudeAutoScale="scale" arcDashAnimateTime="time" /> </div> ); }; export default Page;
arcosData
Esta é a matriz de dados usada para posicionar os arcos.
arcoColor
Um identificador chave da matriz de dados é usado para alterar a cor dos arcos. Os valores das cores podem ser uma matriz, o que significa que você pode criar um efeito de “gradiente” onde o arco começa e termina usando cores diferentes.
arcoStroke
Esta é a espessura do arco.
arcoDashGap
Esta é a lacuna entre cada “traço” no traço.
comprimento do arcoDash
Este é o tamanho do “traço” no traço.
arcoAltitudeAutoScale
Este suporte determina a que distância da superfície do globo o arco deve ser posicionado.
arcDashAnimateTime
Esta é a velocidade da animação do arco.
argolas
Neste exemplo, adiciono anéis pulsantes ao globo. Existem mais algumas opções de configuração para usar anéis, mas o mesmo método que mencionei anteriormente sobre fornecer um nome de chave do objeto de dados ainda se aplica.
- 🚀 Visualização: https://tns-react-3d-globe.netlify.app/rings-data
- ⚙️ Código: src/routes/rings-data.jsx
// src/routes/rings-data.jsx import Globe from 'react-globe.gl'; import hexRgb from 'hex-rgb'; import globeImage from '../assets/earth-dark.jpg'; const Page = () => { const myData = ( { lat: 29.953204744601763, lng: -90.08925929478903, radius: 20, color: '#00ff33', speed: 10, repeat: 500, }, { lat: 28.621322361013092, lng: 77.20347613099612, radius: 40, color: '#ffff00', speed: 20, repeat: 500, }, { lat: -43.1571459086602, lng: 172.72338919659848, radius: 5, color: '#ff0000', speed: 2, repeat: 1000, }, ); return ( <div className="cursor-move"> <Globe globeImageUrl={globeImage} ringsData={myData} ringMaxRadius="radius" ringColor={(ring) => const { red, green, blue } = hexRgb(ring.color); return `rgba(${red},${green},${blue},${Math.sqrt(1 - t)})`; }} ringPropagationSpeed='speed' ringRepeatPeriod='repeat' /> </div> ); }; export default Page;
anéisData
Esta é a matriz de dados usada para posicionar os anéis.
ringMaxRadius
Isso determina o tamanho que cada anel deve ter.
anelColor
Isso é um pouco diferente de antes com a cor dos arcos, pois curro o parâmetro de tempo e o uso como o valor alfa de uma referência de cor rgba. Isso permite que o anel desapareça à medida que cresce.
ringPropagationSpeed
A velocidade que os anéis animam.
ringRepeatPeriod
A velocidade com que os anéis são gerados.
Marcador HTML
Neste exemplo, uso um ícone Svg para atuar como um “marcador” para cada uma das posições na matriz de dados. Você pode usar qualquer elemento HTML que desejar para criar um marcador e qualquer um dos valores da matriz de dados pode ser abstraído da propriedade de dados.
- 🚀 Visualização: https://tns-react-3d-globe.netlify.app/html-marker
- ⚙️ Código: src/routes/html-marker.jsx
// src/routes/html-marker.jsx import Globe from 'react-globe.gl'; import globeImage from '../assets/earth-dark.jpg'; const Page = () => { const myData = ( { city: 'New Orleans', lat: 29.953204744601763, lng: -90.08925929478903, altitude: 0.1, color: '#00ff33', }, { city: 'New Delhi', lat: 28.621322361013092, lng: 77.20347613099612, altitude: 0.1, color: '#ff0000', }, { city: 'New Zealand', lat: -43.1571459086602, lng: 172.72338919659848, altitude: 0.1, color: '#ffff00', }, ); const icon = ` `; return ( <div className="cursor-move"> <Globe globeImageUrl={globeImage} htmlElementsData={myData} htmlAltitude="altitude" htmlElement={(data) => { const { city, color } = data; const element = document.createElement('div'); element.style.color = color; element.innerHTML = ` <div> <svg viewBox="0 0 24 24" style="width:24px;margin:0 auto;"> <path fill="currentColor" fill-rule="evenodd" d="M11.54 22.351l.07.04.028.016a.76.76 0 00.723 0l.028-.015.071-.041a16.975 16.975 0 001.144-.742 19.58 19.58 0 002.683-2.282c1.944-1.99 3.963-4.98 3.963-8.827a8.25 8.25 0 00-16.5 0c0 3.846 2.02 6.837 3.963 8.827a19.58 19.58 0 002.682 2.282 16.975 16.975 0 001.145.742zM12 13.5a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/> </svg> <strong style="font-size:10px;text-align:center">${city}</strong> </div>`; return element; }} /> </div> ); }; export default Page;
htmlElementsData
Os dados usados para posicionar os pontos/marcadores.
htmlAltitude
Esta é a distância acima da superfície do globo onde o marcador deve ser posicionado.
htmlElemento
O elemento HTML deve ser exibido como um marcador. A propriedade data pode ser usada para acessar qualquer dado da matriz de dados.
Camada personalizada
![Camada personalizada do globo 3D](https://optimuscloud.com.br/wp-content/uploads/2024/01/1706194139_983_Recriando-o-BFCM-Globe-do-Shopify-usando-react-globegl.jpg)
Este exemplo é um pouco diferente, pois não é usado para adicionar elementos ao globo em si, mas sim para adicionar elementos à atmosfera que envolve o globo; ou neste caso, sóis/estrelas no universo.
- 🚀 Visualização: https://tns-react-3d-globe.netlify.app/custom-layer
- ⚙️ Código: src/routes/custom-layer.jsx
// src/routes/custom-layer import { useRef } from 'react'; import Globe from 'react-globe.gl'; import * as THREE from 'three'; import globeImage from '../assets/earth-dark.jpg'; const Page = () => { const globeEl = useRef(null); const myData = ( { lat: 29.953204744601763, lng: -90.08925929478903, altitude: 0.4, color: '#00ff33', }, { lat: 28.621322361013092, lng: 77.20347613099612, altitude: 0.4, color: '#ff0000', }, { lat: -43.1571459086602, lng: 172.72338919659848, altitude: 0.4, color: '#ffff00', }, ); return ( <div className="cursor-move"> <Globe ref={globeEl} globeImageUrl={globeImage} pointsData={myData} pointAltitude="altitude" pointColor="color" customLayerData={(...Array(500).keys()).map(() => ({ lat: (Math.random() - 1) * 360, lng: (Math.random() - 1) * 360, altitude: Math.random() * 2, size: Math.random() * 1, color: '#9999cc', }))} customThreeObject={(data) => { const { size, color } = data; return new THREE.Mesh(new THREE.SphereGeometry(size), new THREE.MeshBasicMaterial({ color })); }} customThreeObjectUpdate={(obj, data) => { const { lat, lng, altitude } = data; return Object.assign(obj.position, globeEl.current?.getCoords(lat, lng, altitude)); }} /> </div> ); }; export default Page;
customLayerData
Os pares de valores-chave que usei aqui são semelhantes aos valores usados em todas as outras matrizes de dados de exemplo, mas desta vez estou criando 500 novos “pontos” e definindo aleatoriamente suas posições lat/lng, altitude, cor e tamanho.
customThreeObject
Esta é a geometria e o material do three.js usado para criar uma nova “forma” do three.js. Posso acessar o tamanho e a cor desestruturando seus valores a partir do parâmetro data.
customThreeObjectUpdate
Para que a camada personalizada se mova com o globo quando ele é girado ou ampliado, pego uma referência ao globo usando uma referência React e defino a posição de cada um dos “pontos” para que sejam relativos à rotação ou posição atual do globo .
… esse é o básico e usei alguns dos métodos acima para criar o globo finalizado.
Finalizado
Agora, algumas coisas que ainda não abordei neste exemplo; ou seja, como girar automaticamente o globo e o uso de uma textura.
- 🚀 Visualização: https://tns-react-3d-globe.netlify.app
- ⚙️ Código: src/routes/finished.jsx
// src/routes/finished.jsx import { useRef } from 'react'; import Globe from 'react-globe.gl'; import * as THREE from 'three'; import * as topojson from 'topojson-client'; import landTopology from '../assets/land_10m.json'; import pointsData from '../assets/random-locations.json'; import texture from '../assets/texture.jpg'; const min = 1000; const max = 4000; const sliceData = pointsData.sort(() => (Math.random() > 0.5 ? 1 : -1)).slice(20, 90); const arcsData = sliceData.map(() => { const randStart = Math.floor(Math.random() * sliceData.length); const randEnd = Math.floor(Math.random() * sliceData.length); const randTime = Math.floor(Math.random() * (max - min + 1) + min); return { startLat: sliceData(randStart).lat, startLng: sliceData(randStart).lng, endLat: sliceData(randEnd).lat, endLng: sliceData(randEnd).lng, time: randTime, color: ('#ffffff00', '#faf7e6', '#ffffff00'), }; }); const Page = () => { const globeRef = useRef(null); const globeReady = () => { if (globeRef.current) { globeRef.current.controls().autoRotate = true; globeRef.current.controls().enableZoom = false; globeRef.current.pointOfView({ lat: 19.054339351561637, lng: -50.421161072148465, altitude: 1.8, }); } }; return ( <div className="cursor-move"> <Globe ref={globeRef} onGlobeReady={globeReady} backgroundColor="#08070e" rendererConfig={{ antialias: true, alpha: true }} globeMaterial={ new THREE.MeshPhongMaterial({ color: '#1a2033', opacity: 0.95, transparent: true, }) } atmosphereColor="#5784a7" atmosphereAltitude={0.5} pointsMerge={true} pointsData={pointsData} pointAltitude={0.01} pointRadius={0.2} pointResolution={5} pointColor={() => '#eed31f'} arcsData={arcsData} arcAltitudeAutoScale={0.3} arcColor="color" arcStroke={0.5} arcDashGap={2} arcDashAnimateTime="time" polygonsData={topojson.feature(landTopology, landTopology.objects.land).features} polygonSideColor={() => '#00000000'} polygonCapMaterial={ new THREE.MeshPhongMaterial({ color: '#49ac8f', side: THREE.DoubleSide, map: new THREE.TextureLoader().load(texture), }) } polygonAltitude={0.01} customLayerData={(...Array(500).keys()).map(() => ({ lat: (Math.random() - 1) * 360, lng: (Math.random() - 1) * 360, altitude: Math.random() * 2, size: Math.random() * 0.4, color: '#faadfd', }))} customThreeObject={(sliceData) => { const { size, color } = sliceData; return new THREE.Mesh(new THREE.SphereGeometry(size), new THREE.MeshBasicMaterial({ color })); }} customThreeObjectUpdate={(obj, sliceData) => { const { lat, lng, altitude } = sliceData; return Object.assign(obj.position, globeRef.current?.getCoords(lat, lng, altitude)); }} /> </div> ); }; export default Page;
onGlobeReady
Este suporte pode ser usado para chamar uma função quando o globo estiver totalmente carregado. A função GlobeReady pode então acessar o globo através da referência React e há uma série de funções que podem ser usadas para controlar o globo. Para fazer o globo girar, você pode usar a função autoRotate.
polígonosDados
Este método usa uma abordagem ligeiramente diferente. Para garantir que os países fossem planos (sem profundidade), usei um arquivo geojson diferente. Para traduzir o geojson em dados que o react-globe.gl pode usar, usei o topojson-client.
polígonoCapMaterial
É aqui que aplico uma textura a cada um dos países, em vez do método que expliquei anteriormente, que simplesmente define cada país com uma cor diferente.
E é isso. Está longe de ser uma “recriação” do trabalho impressionante da equipe do Shopify, mas está perto o suficiente para mim.
Se você tiver alguma dúvida sobre os métodos que usei nesta postagem, sinta-se à vontade para me encontrar no Twitter/X: Paulie Scanlon.
A postagem Recriando o BFCM Globe do Shopify usando react-globe.gl apareceu pela primeira vez em The New Stack.