maptalks 开发GIS地图(12)maptalks.three.05 bar-music
1. 说明
使用柱状图,并根据音乐节奏显示动画效果。
2. 初始化地图
1 var map = new maptalks.Map("map", { 2 center: [120.88083857368815, 31.494732837748273], 3 zoom: 10, 4 pitch: 35, 5 bearing: -43.600000000000136, 6 7 centerCross: true, 8 doubleClickZoom: false, 9 // baseLayer: new maptalks.TileLayer('tile', { 10 // urlTemplate: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', 11 // subdomains: ['a', 'b', 'c', 'd'], 12 // attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>' 13 // }) 14 });
3. 添加threelayer 图层,并设置环境。
其中 oReq 对象读取 MP3文件的数据,并进行解析。
1 // the ThreeLayer to draw 2 var threeLayer = new maptalks.ThreeLayer('t', { 3 forceRenderOnMoving: true, 4 forceRenderOnRotating: true 5 // animation: true 6 }); 7 threeLayer.prepareToDraw = function (gl, scene, camera) { 8 var light = new THREE.PointLight(0xffffff); 9 // light.position.set(0, -10, 10).normalize(); 10 camera.add(light); 11 12 var ambientlight = new THREE.AmbientLight(0x999999, 1.57); 13 scene.add(ambientlight); 14 15 var oReq = new XMLHttpRequest(); 16 oReq.open('GET', './data/roll-it-up.mp3', true); 17 oReq.responseType = 'arraybuffer'; 18 19 oReq.onload = function (e) { 20 audioContext.decodeAudioData(oReq.response, initVisualizer); 21 }; 22 oReq.send(); 23 // threeLayer.config('animation', true); 24 animation(); 25 }; 26 27 threeLayer.addTo(map);
4. MP3数据解析
1 // code from https://www.echartsjs.com/examples/zh/editor.html?c=bar3d-music-visualization&gl=1 2 function initVisualizer(audioBuffer) { 3 inited = true; 4 5 var source = audioContext.createBufferSource(); 6 source.buffer = audioBuffer; 7 8 // Must invoked right after click event 9 if (source.noteOn) { 10 source.noteOn(0); 11 } else { 12 source.start(0); 13 } 14 15 var analyzer = audioContext.createAnalyser(); 16 var gainNode = audioContext.createGain(); 17 analyzer.fftSize = 4096; 18 19 gainNode.gain.value = 1; 20 source.connect(gainNode); 21 gainNode.connect(analyzer); 22 analyzer.connect(audioContext.destination); 23 24 var frequencyBinCount = analyzer.frequencyBinCount; 25 var dataArray = new Uint8Array(frequencyBinCount); 26 27 28 function update() { 29 analyzer.getByteFrequencyData(dataArray); 30 31 var item = []; 32 var size = 50; 33 var dataProvider = []; 34 35 for (var i = 0; i < size * size; i++) { 36 var x = i % size; 37 var y = Math.floor(i / size); 38 var dx = x - size / 2; 39 var dy = y - size / 2; 40 41 var angle = Math.atan2(dy, dx); 42 if (angle < 0) { 43 angle = Math.PI * 2 + angle; 44 } 45 var dist = Math.sqrt(dx * dx + dy * dy); 46 var idx = Math.min( 47 frequencyBinCount - 1, Math.round(angle / Math.PI / 2 * 60 + dist * 60) + 100 48 ); 49 50 var val = Math.pow(dataArray[idx] / 100, 3); 51 dataProvider.push([x, y, Math.max(val, 0.1)]); 52 } 53 var musdata = []; 54 for (var i = 0; i < dataProvider.length; i++) { 55 var d = dataProvider[i]; 56 var x = d[0], 57 y = d[1], 58 z = d[2]; 59 var lng = minLng + x * averageLng; 60 var lat = minLat + y * averageLat; 61 var height = z * scale; 62 if (height < 2000) continue; 63 musdata.push({ 64 // name: Math.random() * 10000, 65 value: [lng, lat, height] 66 }); 67 } 68 addBars(musdata); 69 70 setTimeout(update, UPDATE_DURATION); 71 } 72 update(); 73 }
5. 更新数据柱状图
1 function addBars(data) { 2 if (bars) { 3 threeLayer.removeMesh(bars, false); 4 } 5 bars = data.map(function (d) { 6 var value = d.value; 7 minValue = Math.min(minValue, value[2]); 8 maxValue = Math.max(maxValue, value[2]); 9 const material = getMaterial(value[2]); 10 if (!barCache[value[2]]) { 11 barCache[value[2]] = threeLayer.toBox(value.slice(0, 2), { 12 height: value[2], 13 radius: 600, 14 topColor: '#fff', 15 interactive: false 16 }, material); 17 } 18 //复用geometry 19 const geometry = barCache[value[2]].getObject3d().geometry; 20 const options = barCache[value[2]].getOptions(); 21 const coordinate = value.slice(0, 2); 22 const bar = new maptalks.BaseObject(); 23 bar._initOptions(Object.assign({}, options, { coordinate })); 24 bar._createMesh(geometry, material); 25 const position = threeLayer.coordinateToVector3(coordinate); 26 bar.getObject3d().position.copy(position); 27 return bar; 28 }); 29 threeLayer.addMesh(bars, false); 30 }
6. 显示效果
这个动画确实是根据音乐节奏来的, 音乐可参考 ../data/roll-it-up.mp3。
这让我想起了windowsXP自带的音乐播放器。
7. 源码参考