我使用 disease.sh API 和 Globe.gl 制作了 covid-19 数据可视化。
我努力解决性能问题,几分钟后屏幕冻结。
我在 google 上查看了 PageSeppe Insights,上面写着“减少第三方代码的影响第三方代码阻塞了主线程 25,690 毫秒”
它是globe.gl,免费图书馆。 https://unpkg.com/globe.gl@2.22.3/dist/globe.gl.min.js
如果有人给我建议以改进我,我将不胜感激。
var mycData = null;
var nowMode = "Active";
var myWorld = null;
window.onload = function() {
//covid API data
var url = 'https://disease.sh/v3/covid-19/countries?yesterday=yesterday';
fetch(url)
.then(function (data) {
return data.json();
})
.then(function (cData) {
mycData = cData;
//Vaccination InformationGet!
url = 'https://disease.sh/v3/covid-19/vaccine/coverage/countries?lastdays=2&fullData=true';
fetch(url)
.then(function (data) {
return data.json();
})
.then(function (cData) {
for(var i=0;i<mycData.length;i++){
//console.log(mycData[i]);
for(var ii=0;ii<cData.length;ii++){
var Country = cData[ii].country;
var VaccinationCount = cData[ii].timeline[0].total;
if(Country == mycData[i].country){
//console.log(Country + " => " + VaccinationCount);
mycData[i].vaccination = VaccinationCount;
}
}
}
makeMap(mycData);
});
});
};
// document.getElementById("cases").addEventListener('click', function(){
// nowMode = "Cases";
// document.getElementById("CasesColor").style.display ="block";
// document.getElementById("VaccinationColor").style.display ="none";
// makeMap(mycData);
// });
document.getElementById("active").addEventListener('click', function(){
nowMode = "Active";
document.getElementById("CasesColor").style.display ="block";
document.getElementById("VaccinationColor").style.display ="none";
makeMap(mycData);
});
document.getElementById("vaccination").addEventListener('click', function(){
nowMode = "Vaccination";
document.getElementById("CasesColor").style.display ="none";
document.getElementById("VaccinationColor").style.display ="block";
makeMap(mycData);
});
function makeMap(cData){
const colorScale = d3.scaleSequentialSqrt(d3.interpolateYlOrRd);
const getVal = feat => feat.properties.GDP_MD_EST / Math.max(1e5, feat.properties.POP_EST);
fetch('https://raw.githubusercontent.com/vasturiano/globe.gl/master/example/datasets/ne_110m_admin_0_countries.geojson').then(res => res.json()).then(countries =>
// fetch('js/countries.geojson').then(res => res.json()).then(countries =>
{
const maxVal = Math.max(...countries.features.map(getVal));
colorScale.domain([0, maxVal]);
//adding api data to properties match with ISO_A2
//adjusting data which is not matched, data for -99
for(var i=0;i<countries.features.length;i++){
switch(countries.features[i].properties.ADMIN){
case "France":
countries.features[i].properties.ISO_A2 = "FR";
break;
case "Norway":
countries.features[i].properties.ISO_A2 = "NO";
break;
case "Somaliland":
countries.features[i].properties.ISO_A2 = "SO";
break;
case "Turkmenistan":
countries.features[i].properties.ISO_A2 = "TR";
break;
}
var cExists = false;
//insert data into array
for(var ii=0;ii<cData.length;ii++){
if(countries.features[i].properties.ISO_A2 == cData[ii].countryInfo.iso2){
cExists = true;
var c_data = cData[ii];
countries.features[i].properties["flag"] = c_data.countryInfo.flag; // flag
countries.features[i].properties["cases"] = c_data.cases;
countries.features[i].properties["deaths"] = c_data.deaths;
countries.features[i].properties["recovered"] = c_data.recovered;
countries.features[i].properties["active"] = c_data.active;
countries.features[i].properties["casesPerOneMillion"] = c_data.casesPerOneMillion;
countries.features[i].properties["vaccination"] = c_data.vaccination;
switch(nowMode){
// case "Cases":
// countries.features[i].properties["bgColor"] = getColor(c_data.cases);
// break;
case "Active":
countries.features[i].properties["bgColor"] = getColor(c_data.active);
break;
case "Vaccination":
countries.features[i].properties["bgColor"] = getColorVaccination(c_data.vaccination);
break;
}
}
}
}
if(myWorld != null) myWorld = null;
myWorld = Globe()
.globeImageUrl('https://unpkg.com/three-globe@2.18.1/example/img/earth-night.jpg')
.backgroundColor('#232946')
.lineHoverPrecision(0)
.polygonsData(countries.features.filter(d => d.properties.ISO_A2 !== 'AQ'))
.polygonAltitude(0.06)
.polygonCapColor(d => d.properties.bgColor)
.polygonSideColor(() => 'rgba(255, 215, 0, 0.15)') //shadow to globe
.polygonStrokeColor(() => '#111')
.polygonLabel(({ properties: d }) => `
<div class="info-board">
<span class="flag"><img src="${d.flag}" class="flag"></span>
<span class="country">${d.ADMIN}</span>
<hr class="hr-info">
<div class="info-title-top">Active: ${d.deaths.toLocaleString()} </div>
<div class="info-title">Total Cases: ${d.cases.toLocaleString()} </div>
<div class="info-title">Total Recovered: ${d.recovered.toLocaleString()} </div>
<div class="info-title">Total Deaths: ${d.deaths.toLocaleString()} </div>
<div class="info-title">Cases Per Million: ${d.casesPerOneMillion.toLocaleString()}</div>
<hr class="hr-info2">
<div class="info-title-v">Vaccination: ${d.vaccination.toLocaleString()}</div>
</div>
`)
// (${d.ISO_A2}) if add iso_a2 code to display
.onPolygonHover(hoverD => myWorld
.polygonAltitude(d => d === hoverD ? 0.15 : 0.06) //hover height
//.polygonCapColor(d => d === hoverD ? '#edfaef' : colorScale(getVal(d))) // hover color
)
// .polygonsTransitionDuration(3000)
// .showGlobe(true)
(document.getElementById('globeViz'))
// Add auto-rotation
myWorld.controls().autoRotate = false;
myWorld.controls().autoRotateSpeed = 0.3;
// responsive
window.addEventListener('resize', (event) => {
myWorld.width([event.target.innerWidth])
myWorld.height([event.target.innerHeight])
});
});
}
function getColor(d) {
return d > 10000000 ? '#800026' :
d > 5000000 ? '#BD0026' :
d > 1000000 ? '#E31A1C' :
d > 500000 ? '#FC4E2A' :
d > 100000 ? '#FD8D3C' :
d > 50000 ? '#FEB24C' :
d > 10000 ? '#FED976' :
'#FFEDA0';
}
function getColorVaccination(d) {
return d > 10000000 ? '#084081' :
d > 5000000 ? '#0868ac' :
d > 1000000 ? '#2b8cbe' :
d > 500000 ? '#4eb3d3' :
d > 100000 ? '#7bccc4' :
d > 50000 ? '#a8ddb5' :
d > 10000 ? '#ccebc5' :
'#e0f3db';
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
html,
body {
/* z-index: 999; */
margin: 0;
padding: 0;
background: #232946;
overflow: hidden;
height: 100%;
font-family: 'Space Mono', monospace !important;
}
h1 {
font-weight: 700;
font-size: 34px;
margin-top:10px;
margin-bottom:0px;
color:#fff;
}
@media only screen and (min-width: 600px) and (max-width: 992px) {
h1 {
font-weight: 700;
font-size: 28px;
margin-top:40px;
}
}
@media only screen and (max-width: 599px) {
h1 {
font-weight: 600;
font-size: 20px;
margin-top:40px;
}
}
h3 {
font-weight: 500;
font-size: 26px;
margin-top:3px;
margin-bottom:0px;
color:#fff;
}
p {
margin-bottom: 10px;
color:#fff;
}
hr{
border: 1px solid rgba(153, 153, 153, 0.1);
}
section {
position: absolute;
top: 2%;
text-align: center;
width: 100%;
color: rgba(255, 255, 255, 1.0);
z-index: 999;
}
@media only screen and (max-width: 992px) {
section {
position: absolute;
top: 0%;
text-align: center;
width: 100%;
color: rgba(255, 255, 255, 1.0);
z-index: 99;
}
}
section2 {
position: absolute;
top: 20%;
padding: 0 20px;
max-width: 300px;
width: 100%;
color: rgba(255, 255, 255, 1.0);
line-height: 1.5;
z-index: 99;
}
@media only screen and (max-width: 992px) {
section2 {
position: absolute;
top: 15%;
left:15%;
padding: 0 20px;
width: 100%;
color: rgba(255, 255, 255, 1.0);
z-index: 1;
}
}
section3 {
position: absolute;
bottom: 3%;
padding: 0 20px;
max-width: 400px;
width: 100%;
color: rgba(255, 255, 255, 1.0);
z-index: 99;
line-height: 1.5;
}
@media only screen and (max-width: 992px) {
section3 {
display:none;
}
}
#globeViz{
margin-top: 30px;
}
.list{
margin-left:5px;
}
.info-board{
font-family: 'Space Mono', monospace;
background-color:rgba(0, 0, 0, 0.7);
color:#fff;
padding:20px;
margin:20px;
border: 1px solid white;
border-radius: 15px;
display:inline-block;
white-space: nowrap;
z-index: 9999 !important;
}
@media only screen and (max-width: 992px) {
.info-board {
width: 100%;
background-color:rgba(0, 0, 0, 0.9);
color:#fff;
padding:10px;
margin:10px;
border: 1px solid white;
border-radius: 15px;
/* display:inline-block; */
white-space: nowrap;
z-index: 9999;
}
}
.flag {
text-align: left;
border-radius: 10px;
display: inline-block;
box-shadow: 0px 0px 20px -5px rgba(0, 0, 0, 0.8);
padding-bottom:3px;
margin-right:20px;
margin-left:10px;
margin-bottom:8px;
background-position: center ;
width:70px;
height:45px;
vertical-align: middle;
}
.country{
font-size:30px;
text-transform: uppercase;
vertical-align: sub;
}
.info-title-top{
font-size:18px;
margin-top: 20px;
margin-right:20px;
margin-left:20px;
}
.info-title{
font-size:18px;
margin-top: 10px;
margin-right:20px;
margin-left:20px;
}
.info-title-v{
color:#33ffff;;
font-size:18px;
margin-top: 10px;
margin-bottom: 10px;
margin-right:20px;
margin-left:20px;
}
.new{
font-size:14px;
margin-left:0px;
}
.hr-info{
border:none;
border-top:1px dashed #fff;
height:1px;
width:90%;
}
.hr-info2{
border:none;
border-top:1px dashed #fff;
height:1px;
width:90%;
margin-top:20px;
margin-bottom:10px;
}
.red{
color:rgba(255, 99, 132);
}
.blue{
color:rgba(51,255,255, 0.9);
}
@media only screen and (max-width: 992px) {
.country{
font-size:24px;
text-transform: uppercase;
vertical-align: sub;
}
.info-title-top{
font-size:14px;
margin-top: 16px;
margin-right:10px;
margin-left:10px;
}
.info-title{
font-size:14px;
margin-top: 10px;
margin-right:10px;
margin-left:10px;
}
.info-title-v{
color:#33ffff;;
font-size:14px;
margin-top: 10px;
margin-bottom: 10px;
margin-right:10px;
margin-left:10px;
}
.new{
font-size:12px;
margin-left:0px;
}
}
/*====== list ======*/
.legend .legend-title {
text-align: left;
margin-bottom: 10px;
font-weight: bold;
font-size: 80%;
}
.legend .legend-scale ul {
margin: 0;
padding: 0;
float: left;
list-style: none;
}
.legend .legend-scale ul li {
display: block;
float: left;
width: 40px;
margin-bottom: 6px;
text-align: center;
font-size: 60%;
list-style: none;
}
.legend ul.legend-labels li span {
display: block;
float: left;
height: 15px;
width: 40px;
}
.legend .legend-source {
font-size: 70%;
color: #999;
text-align: left;
clear: both;
}
.legend a {
color: #777;
}
.legend-set{
/* margin-top:40px; */
margin-left:25px;
}
@media only screen and (max-width: 992px) {
.legend-set {
display:none;
}
}
.card1 {
width:100%;
padding: 20px 15px;
border-radius: 10px;
box-shadow: 0 1px 10px 0 rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(221, 221, 221, 0.1);
}
@media only screen and (max-width: 992px) {
.card1 {
width:100%;
padding: 10px 15px;
border-radius: 10px;
box-shadow: 0 1px 10px 0 rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(221, 221, 221, 0.1);
z-index: -1
}
}
.switch-menu {
display: inline-block;
margin-bottom: 10px ;
}
@media only screen and (max-width: 992px) {
.switch-menu {
display: inline-block;
padding: 0px;
}
}
hr.none-m {
margin-bottom: 15px!important;
}
@media only screen and (max-width: 992px) {
.none-m {
display: none;
}
}
.icon{
width:37px;
height:37px;
margin-right: 10px;
vertical-align: text-bottom;
}
.card2 {
width:100%;
margin-top:30px;
padding: 10px 15px;
border-radius: 10px;
box-shadow: 0 1px 10px 0 rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(221, 221, 221, 0.1);
z-index: 100;
}
.card3 {
width:100%;
margin-top:30px;
padding: 10px 15px;
border-radius: 10px;
box-shadow: 0 1px 10px 0 rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(221, 221, 221, 0.1);
z-index: 100;
}
.card4 {
width:100%;
margin-top:30px;
padding: 10px 8px 10px 8px;
border-radius: 10px;
box-shadow: 0 1px 10px 0 rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(221, 221, 221, 0.1);
z-index: 100;
}
hr.mt{
margin-top: 10px!important;
}
.chart-area {
position: relative;
height: 100%;
width: 100%;
margin-top:7px;
}
.casetitle{
font-size: 22px;
text-align: center;
margin-bottom: 10px
}
.c-no{
text-align: center;
margin:10px 0px;
color:rgba(255, 99, 132);
font-size: 24px;
font-weight: 100;
}
.v-no{
text-align: center;
margin:10px 0px;
color:rgba(51,255,255,1.0);
font-size: 24px;
font-weight: 100;x
}
/* ***drag suggestion*** */
.drag{
text-align: center;
color:rgba(255, 255, 255, 0.2);
margin-bottom: 0px;
}
@media only screen and (max-width: 992px) {
.drag {
display: none;
}
}
.left-m {
display: inline-block;
padding: 10px;
animation: move 1s linear infinite;
}
@media only screen and (max-width: 992px) {
.left-m {
display: inline-block;
padding: 0px;
}
}
.center-m {
display: inline-block;
padding: 10px;
}
@media only screen and (max-width: 992px) {
.center-m {
display: inline-block;
padding: 0px;
}
}
.right-m {
display: inline-block;
padding: 10px;
animation: move1 1s linear infinite;
}
@media only screen and (max-width: 992px) {
.right-m {
display: inline-block;
padding: 0px;
}
}
@keyframes move {
50% {
transform: translateX(-10px);
opacity: 0.5;
}
100% {
transform: translateX(-10px);
opacity: 0;
}
}
@keyframes move1 {
50% {
transform: translateX(10px);
opacity: 0.5;
}
100% {
transform: translateX(10px);
opacity: 0;
}
}
.mb-3{
margin-bottom:30px;
}
@media only screen and (max-width: 992px) {
.title {
padding-top: 10px;
}
}
.mobile{
display:none;
}
@media only screen and (max-width: 992px) {
.mobile {
display:block;
margin-top:15px;
}
hr.toggle-menu{
margin-top: 30px!important;
}
}
/*** pop up ***/
.main{
margin: 10px;
}
p {
margin-bottom: 10px;
}
.red{
color:rgba(255, 99, 132);
}
.blue{
color:#17a2b8;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Covid-19 3D Data Visualisation</title>
<link rel="stylesheet" href="globe.css">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
</head>
<body>
<div id="globeViz"></div>
<section>
<h1 class="title">Covid-19 Active Cases 3D Map</h1>
<div class="drag">
<div class="left-m">
<<<
</div>
<div class="center-m">
Drag to Rotate and Zoom
</div>
<div class="right-m">
>>>
</div>
</div>
</section>
<section2>
<form class="card1">
<h3 class="none-m"><img src="img/covid.png" alt="covid" class="icon">Covid-19</h3>
<hr class="none-m">
<!-- <div class="switch-menu">
<label id="cases" class="list">
<input name="group1" type="radio" style="transform:scale(1.5);"checked />
<span>Total Cases</span>
</label>
</div> -->
<div class="switch-menu">
<label id="active" class="list">
<input name="group1" type="radio" style="transform:scale(1.5);" checked />
<span>Active Cases</span>
</label>
</div>
<hr class="mt none-m">
<h3 class="none-m"><img src="img/vaccine.png" alt="vaccination" class="icon">Vaccination</h3>
<hr class="none-m">
<div class="switch-menu">
<label id="vaccination" class="list">
<input name="group1" type="radio" style="transform:scale(1.5);" />
<span>Total Vaccination</span>
</label>
</div>
</form>
</section2>
<section3>
<div class="legend-set">
<div class="legend mt-3">
<div class='legend-scale'>
<ul class='legend-labels' id="CasesColor">
<div class="legend-title2">
Covid-19 Active Cases
</div>
<li><span style='background:#FFEDA0;'></span>> 30K</li>
<li><span style='background:#FED976;'></span>> 50K</li>
<li><span style='background:#FEB24C;'></span>> 70K</li>
<li><span style='background:#FD8D3C;'></span>> 100K</li>
<li><span style='background:#FC4E2A;'></span>> 500K</li>
<li><span style='background:#E31A1C;'></span>> 1M</li>
<li><span style='background:#800026;'></span>< 1M+</li>
</ul>
<ul class='legend-labels' id="VaccinationColor" style="display:none;">
<div class="legend-title2">
Covid-19 Vaccination
</div>
<li><span style='background:#ccebc5;'></span>> 30K</li>
<li><span style='background:#a8ddb5;'></span>> 50K</li>
<li><span style='background:#7bccc4;'></span>> 70K</li>
<li><span style='background:#4eb3d3;'></span>> 100K</li>
<li><span style='background:#2b8cbe;'></span>> 500K</li>
<li><span style='background:#0868ac;'></span>> 1M</li>
<li><span style='background:#084081;'></span>< 1M+</li>
</ul>
</div>
<div class='legend-source'>
Source: <a href="https://corona.lmao.ninja/">disease.sh - Open Disease Data</a>
</div>
</div>
</div>
</section3>
<script type="text/javascript" src="js/d3.min.js"></script>
<script src="https://unpkg.com/globe.gl"></script>
<script type="text/javascript" src="js/globe.js"></script>
</body>
</html>