更多精彩内容尽在数字孪生平台,关注公众号【sky的数孪技术】,技术交流、源码下载请添加VX:digital_twin123
根据两点生成动画的工具。
首先,找到你想要开始的视图,点击“设置起点视图”,然后调整到目的视图,点击“设置终点视图”。
点击“开始动画”来创建一张gif图。
源码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Fly to a location</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gif.js@0.2.0/dist/gif.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new mapboxgl.Map({
container: 'map',
center: [-74.5, 40],
zoom: 4,
preserveDrawingBuffer: true
});
let guiData
async function makeAnimation(frames, duration) {
async function dataURLtoBlob(dataURL) {
var arr = dataURL.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
async function saveBlobToFile(blob, fileName) {
var a = document.createElement('a');
document.body.appendChild(a);
a.style = 'display: none';
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
}
async function startAnimation(frames, duration) {
return new Promise((resolve, reject) => {
let msRemaining = duration
let frameCounter = 0
let images
if (guiData.exportPngs) {
images = new JSZip();
} else {
images = []
}
async function assembleFrame(dataUrl) {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = dataUrl;
resolve(img)
})
}
async function makeImage() {
frameCounter++
msRemaining = msRemaining - duration / frames;
if (msRemaining <= 0) {
clearInterval(intervalId);
resolve(images)
}
let dataUrl = map.getCanvas().toDataURL('image/png')
if (guiData.exportPngs) {
let blob = dataURLtoBlob(dataUrl);
images.file(`image${String(frameCounter).padStart(3, '0')}.png`, blob);
} else {
assembleFrame(dataUrl).then((img) => {
images.push(img)
})
}
}
const intervalId = setInterval(makeImage, duration / frames);
})
}
startAnimation(frames, duration).then((images) => {
if (guiData.exportPngs) {
images.generateAsync({ type: 'blob' }).then(function (content) {
saveBlobToFile(content, 'images.zip');
});
} else {
const gif = new GIF({
workers: 5,
quality: 10,
width: images[0].width,
height: images[0].height
});
for (var i = 0; i < images.length; i++) {
gif.addFrame(images[i], { delay: guiData.frameDelay });
}
gif.on('finished', function (blob) {
window.open(URL.createObjectURL(blob));
});
gif.render();
}
})
}
let startPosition, endPosition, directoryHandle;
function addTerrainSource() {
map.addSource('mapbox-dem', {
'type': 'raster-dem',
'url': 'mapbox://mapbox.mapbox-terrain-dem-v1',
'tileSize': 512,
'maxzoom': 14
});
}
function toggleTerrain(value) {
if (value) {
map.setTerrain({ 'source': 'mapbox-dem', 'exaggeration': 1.5 });
} else {
map.setTerrain()
}
}
map.on('load', (e) => {
addTerrainSource()
guiData = {
setStart: function () {
setStartPosition()
},
setEnd: function () {
setEndPosition()
},
duration: 20000,
frames: 100,
frameDelay: 100,
exportPngs: false,
dryRun: true,
mapStyle: 'Streets',
terrain: true,
startAnimation: function () {
startAnimation()
},
showHelp: function () {
showHelp()
},
};
let mapStyles = {
'Light': 'light-v10',
'Dark': 'dark-v10',
'Outdoors': 'outdoors-v11',
'Satellite': 'satellite-v9',
'Sat Streets': 'satellite-streets-v11',
'Standard': 'standard',
'Streets': 'streets-v11',
}
const gui = new dat.GUI()
const positionsFolder = gui.addFolder('位置设置')
positionsFolder.open()
let setStart = positionsFolder.add(guiData, "setStart").name('设置起点视图');
let setEnd = positionsFolder.add(guiData, "setEnd").name('设置终点视图');
const mapFolder = gui.addFolder('地图设置')
let mapStyle = mapFolder.add(guiData, "mapStyle", mapStyles).name('地图样式').setValue('standard').onChange(function (value) {
let mapStylePath = `mapbox://styles/mapbox/${value}`
map.setStyle(mapStylePath);
map.once('idle', (e) => {
addTerrainSource()
toggleTerrain(guiData.terrain)
})
});
let terrain = mapFolder.add(guiData, "terrain").name('地形').setValue(true).onChange(function (value) {
toggleTerrain(value)
})
const animationFolder = gui.addFolder('动画设置')
let durationGUI = animationFolder.add(guiData, "duration", 0, 30000, 100).name('持续时间 (ms)');
let framesGUI = animationFolder.add(guiData, "frames", 0, 1000, 10).name('帧数');
let frameDelay = animationFolder.add(guiData, "frameDelay", 1, 1000, 10).name('帧延迟');
let exportPngs = animationFolder.add(guiData, "exportPngs").name('导出 PNG').onChange(function (value) {
if (value) {
alert("图片将下载为zip包,帧数太多的话文件也会很大.")
}
});
let dryRun = animationFolder.add(guiData, "dryRun").name('只运行动画').onChange(function (value) { });
let startAnimationGUI = gui.add(guiData, "startAnimation").name('开始动画');
let showHelpGUI = gui.add(guiData, "showHelp").name('帮助');
function setStartPosition() {
startPosition = map.getFreeCameraOptions();
}
function setEndPosition() {
endPosition = {
center: map.getCenter(),
bearing: map.getBearing(),
pitch: map.getPitch(),
zoom: map.getZoom()
}
}
function showHelp() {
alert(`根据两点生成动画的工具。
首先,找到你想要开始的视图,点击“设置起点视图”,然后调整到目的视图,点击“设置终点视图”。
点击“开始动画”来创建一张gif图。
`)
}
function startAnimation() {
if (endPosition == undefined) {
alert("请设置起止点.")
} else {
function fly() {
map.flyTo({
center: endPosition.center,
bearing: endPosition.bearing,
pitch: endPosition.pitch,
zoom: endPosition.zoom,
essential: true,
duration: guiData.duration,
maxDuration: Infinity
});
if (!guiData.dryRun) {
makeAnimation(guiData.frames, guiData.duration)
}
}
if (startPosition !== undefined) {
map.setFreeCameraOptions(startPosition);
map.once('idle', (e) => {
fly()
})
} else {
fly()
}
}
}
})
</script>
</body>
</html>