Planetary.js 旋转地球插件

本文最后更新于:2023年1月16日 晚上

Planetary.js 是一款生成可交互地球模型的插件。

简介

特点

  • 完全可定制,包括颜色,旋转等等
  • 在任何具有自定义颜色和大小的位置显示动画
  • 支持鼠标拖动+缩放操作
  • 可扩展的基于插件的架构

核心 js 与 json 文件下载

  • 核心 js 有三个,名字叫 d3.v3.min.jstopojson.v1.min.jsplanetaryjs.min.js

  • 需要使用的文件名字叫 world-110m.json

  • planetaryjs.min.js可以在官网下载:http://planetaryjs.com/download/

  • 另外两个 js 文件我是从 github 上面知道了地址自己扒下来的,两个带 http 开头的文件就是了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Quick Start
You'll need to run this page from a web server of some kind so that Planetary.js can load the TopoJSON data via Ajax.

HTML:

<html>
<head>
<script type='text/javascript' src='http://d3js.org/d3.v3.min.js'></script>
<script type='text/javascript' src='http://d3js.org/topojson.v1.min.js'></script>
<script type='text/javascript' src='planetaryjs.min.js'></script>
</head>
<body>
<canvas id='globe' width='500' height='500'></canvas>
<script type='text/javascript' src='yourApp.js'></script>
</body>
</html>

使用方法

  • 拿到文件后可以放在静态网页的某个文件夹,也可以放到自己的图床里,也可以直接引用原始的 js 文件,总之可以访问到就可以
  • 注意 :直接本地运行是不能显示地球的,需要在 web 服务器上才能正确显示。

官网 demo

1
2
3
4
5
6
7
8
9
10
11
<html>
<head>
<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/d3.v3.min.js'></script>
<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/topojson.v1.min.js'></script>
<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/planetaryjs.min.js'></script>
</head>
<body>
<canvas id='globe' width='500' height='500'></canvas>
<script type='text/javascript' src='yourApp.js'></script>
</body>
</html>
  • 在 web 服务器访问该页面

  • 这就是最简单的 demo 了

展示 demo

  • 我上文展示的 demo 本质上是官网的 demo ,但是其中有一点 bug,我做了修改
  • 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<div id='fatherobj'>
<canvas id='quakeCanvas' width=300></canvas>
</div>

<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/d3.v3.min.js'></script>
<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/topojson.v1.min.js'></script>
<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/planetaryjs.min.js'></script>
<script>
(function() {
var canvas = document.getElementById('quakeCanvas');
var board_obj = document.getElementById('fatherobj');
// Create our Planetary.js planet and set some initial values;
// we use several custom plugins, defined at the bottom of the file
var planet = planetaryjs.planet();
planet.loadPlugin(autocenter({extraHeight: -120}));
planet.loadPlugin(autoscale({extraHeight: -120}));
planet.loadPlugin(planetaryjs.plugins.earth({
topojson: { file: 'https://101.43.39.125/HexoFiles/js/planetaryjs/world-110m.json' },
oceans: { fill: '#001320' },
land: { fill: '#06304e' },
borders: { stroke: '#001320' }
}));
planet.loadPlugin(planetaryjs.plugins.pings());
planet.loadPlugin(planetaryjs.plugins.zoom({
scaleExtent: [50, 5000]
}));
planet.loadPlugin(planetaryjs.plugins.drag({
onDragStart: function() {
this.plugins.autorotate.pause();
},
onDragEnd: function() {
this.plugins.autorotate.resume();
}
}));
planet.loadPlugin(autorotate(5));
planet.projection.rotate([100, -10, 0]);
planet.draw(canvas);
// Plugin to resize the canvas to fill the window and to
// automatically center the planet when the window size changes
function autocenter(options) {
options = options || {};
var needsCentering = false;
var globe = null;
var resize = function() {
var width = board_obj.offsetWidth;
var height = board_obj.offsetWidth;
globe.canvas.width = width;
globe.canvas.height = height;
globe.projection.translate([width / 2, height / 2]);
};
return function(planet) {
globe = planet;
planet.onInit(function() {
needsCentering = true;
d3.select(window).on('resize', function() {
needsCentering = true;
});
});
planet.onDraw(function() {
if (needsCentering) { resize(); needsCentering = false; }
});
};
};
// Plugin to automatically scale the planet's projection based
// on the window size when the planet is initialized
function autoscale(options) {
options = options || {};
return function(planet) {
planet.onInit(function() {
var width = board_obj.offsetWidth;
var height = board_obj.offsetWidth;
planet.projection.scale(Math.min(width, height) / 2.3);
});
};
};
// Plugin to automatically rotate the globe around its vertical
// axis a configured number of degrees every second.
function autorotate(degPerSec) {
return function(planet) {
var lastTick = null;
var paused = false;
planet.plugins.autorotate = {
pause: function() { paused = true; },
resume: function() { paused = false; }
};
planet.onDraw(function() {
if (paused || !lastTick) {
lastTick = new Date();
} else {
var now = new Date();
var delta = now - lastTick;
var rotation = planet.projection.rotate();
rotation[0] += degPerSec * delta / 1000;
if (rotation[0] >= 180) rotation[0] -= 360;
planet.projection.rotate(rotation);
lastTick = now;
}
});
};
};
})();
</script>

效果展示

  • 上述 demo 的效果是展示动态的地球

加入响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
<div id='fatherobj2'>
<canvas id='quakeCanvas2'></canvas>
</div>
<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/d3.v3.min.js'></script>
<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/topojson.v1.min.js'></script>
<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/planetaryjs.min.js'></script>
<script type='text/javascript' src='https://101.43.39.125/HexoFiles/js/planetaryjs/moment.min.js'></script>
<script>
(function() {
var canvas = document.getElementById('quakeCanvas2');
var board_obj = document.getElementById('fatherobj2');
// Create our Planetary.js planet and set some initial values;
// we use several custom plugins, defined at the bottom of the file
var planet = planetaryjs.planet();
planet.loadPlugin(autocenter({extraHeight: -120}));
planet.loadPlugin(autoscale({extraHeight: -120}));
planet.loadPlugin(planetaryjs.plugins.earth({
topojson: { file: 'https://101.43.39.125/HexoFiles/js/planetaryjs/world-110m.json' },
oceans: { fill: '#001320' },
land: { fill: '#06304e' },
borders: { stroke: '#001320' }
}));
planet.loadPlugin(planetaryjs.plugins.pings());
planet.loadPlugin(planetaryjs.plugins.zoom({
scaleExtent: [50, 5000]
}));
planet.loadPlugin(planetaryjs.plugins.drag({
onDragStart: function() {
this.plugins.autorotate.pause();
},
onDragEnd: function() {
this.plugins.autorotate.resume();
}
}));
planet.loadPlugin(autorotate(5));
planet.projection.rotate([100, -10, 0]);
planet.draw(canvas);
// Create a color scale for the various earthquake magnitudes; the
// minimum magnitude in our data set is 2.5.
var colors = d3.scale.pow()
.exponent(3)
.domain([2, 4, 6, 8, 10])
.range(['white', 'yellow', 'orange', 'red', 'purple']);
// Also create a scale for mapping magnitudes to ping angle sizes
var angles = d3.scale.pow()
.exponent(3)
.domain([2.5, 10])
.range([0.5, 15]);
// And finally, a scale for mapping magnitudes to ping TTLs
var ttls = d3.scale.pow()
.exponent(3)
.domain([2.5, 10])
.range([2000, 5000]);
// Create a key to show the magnitudes and their colors
d3.select('#magnitudes').selectAll('li')
.data(colors.ticks(9))
.enter()
.append('li')
.style('color', colors)
.text(function(d) {
return "Magnitude " + d;
});
d3.json('https://101.43.39.125/HexoFiles/js/planetaryjs/year_quakes_small.json', function(err, data) {
if (err) {
alert("Problem loading the quake data.");
return;
}
var start = parseInt(data[0].time, 10);
var end = parseInt(data[data.length - 1].time, 10);
var currentTime = start;
var lastTick = new Date().getTime();
var updateDate = function() {
d3.select('#date').text(moment(currentTime).utc().format("MMM DD YYYY HH:mm UTC"));
};
// A scale that maps a percentage of playback to a time
// from the data; for example, `50` would map to the halfway
// mark between the first and last items in our data array.
var percentToDate = d3.scale.linear()
.domain([0, 100])
.range([start, end]);
// A scale that maps real time passage to data playback time.
// 12 minutes of real time maps to the entirety of the
// timespan covered by the data.
var realToData = d3.scale.linear()
.domain([0, 1000 * 60 * 12])
.range([0, end - start]);
var paused = false;
// Pause playback and update the time display
// while scrubbing using the range input.
d3.select('#slider')
.on('change', function(d) {
currentTime = percentToDate(d3.event.target.value);
updateDate();
})
.call(d3.behavior.drag()
.on('dragstart', function() {
paused = true;
})
.on('dragend', function() {
paused = false;
})
);
// The main playback loop; for each tick, we'll see how much
// time passed in our accelerated playback reel and find all
// the earthquakes that happened in that timespan, adding
// them to the globe with a color and angle relative to their magnitudes.
d3.timer(function() {
var now = new Date().getTime();
if (paused) {
lastTick = now;
return;
}
var realDelta = now - lastTick;
// Avoid switching back to the window only to see thousands of pings;
// if it's been more than 500 milliseconds since we've updated playback,
// we'll just set the value to 500 milliseconds.
if (realDelta > 500) realDelta = 500;
var dataDelta = realToData(realDelta);
var toPing = data.filter(function(d) {
return d.time > currentTime && d.time <= currentTime + dataDelta;
});
for (var i = 0; i < toPing.length; i++) {
var ping = toPing[i];
planet.plugins.pings.add(ping.lng, ping.lat, {
// Here we use the `angles` and `colors` scales we built earlier
// to convert magnitudes to appropriate angles and colors.
angle: angles(ping.mag),
color: colors(ping.mag),
ttl: ttls(ping.mag)
});
}
currentTime += dataDelta;
if (currentTime > end) currentTime = start;
updateDate();
d3.select('#slider').property('value', percentToDate.invert(currentTime));
lastTick = now;
});

});
// Plugin to resize the canvas to fill the window and to
// automatically center the planet when the window size changes
function autocenter(options) {
options = options || {};
var needsCentering = false;
var globe = null;
var resize = function() {
var width = board_obj.offsetWidth;
var height = board_obj.offsetWidth;
globe.canvas.width = width;
globe.canvas.height = height;
globe.projection.translate([width / 2, height / 2]);
};
return function(planet) {
globe = planet;
planet.onInit(function() {
needsCentering = true;
d3.select(window).on('resize', function() {
needsCentering = true;
});
});
planet.onDraw(function() {
if (needsCentering) { resize(); needsCentering = false; }
});
};
};
// Plugin to automatically scale the planet's projection based
// on the window size when the planet is initialized
function autoscale(options) {
options = options || {};
return function(planet) {
planet.onInit(function() {
var width = board_obj.offsetWidth;
var height = board_obj.offsetWidth;
planet.projection.scale(Math.min(width, height) / 2.3);
});
};
};
// Plugin to automatically rotate the globe around its vertical
// axis a configured number of degrees every second.
function autorotate(degPerSec) {
return function(planet) {
var lastTick = null;
var paused = false;
planet.plugins.autorotate = {
pause: function() { paused = true; },
resume: function() { paused = false; }
};
planet.onDraw(function() {
if (paused || !lastTick) {
lastTick = new Date();
} else {
var now = new Date();
var delta = now - lastTick;
var rotation = planet.projection.rotate();
rotation[0] += degPerSec * delta / 1000;
if (rotation[0] >= 180) rotation[0] -= 360;
planet.projection.rotate(rotation);
lastTick = now;
}
});
};
};
})();
</script>
  • 带响应效果展示:

参考资料


Planetary.js 旋转地球插件
https://www.zywvvd.com/notes/tools/planetary-js/planetary-js/
作者
Yiwei Zhang
发布于
2023年1月11日
许可协议