周囲の音に基づき 3D 建物をアニメーション化
runtime styling API を Web Audio API と接続し、3D 建物が周囲環境に適応するマップを作成します。
<!DOCTYPE html><html><head><meta charset="utf-8" /><title>周囲の音に基づき 3D 建物をアニメーション化</title><meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" /><script src="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.js"></script><link href="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.css" rel="stylesheet" /><style> body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; width: 100%; }</style></head><body><div id="map"></div> <script> mapboxgl.accessToken = '<your access token here>';/* global Promise */ // Use a minimal variant of the Mapbox Dark style, with certain features removed.var map = new mapboxgl.Map({style: 'mapbox://styles/examples/cj68bstx01a3r2rndlud0pwpv',center: {lng: -74.00649562332922,lat: 40.70811328605049},zoom: 15,pitch: 55,container: 'map',antialias: true}); map.addControl(new mapboxgl.FullscreenControl()); map.on('load', function() {var bins = 16;var maxHeight = 200;var binWidth = maxHeight / bins; // Divide the buildings into 16 bins based on their true height, using a layer filter.for (var i = 0; i < bins; i++) {map.addLayer({'id': '3d-buildings-' + i,'source': 'composite','source-layer': 'building','filter': ['all',['==', 'extrude', 'true'],['>', 'height', i * binWidth],['<=', 'height', (i + 1) * binWidth]],'type': 'fill-extrusion','minzoom': 15,'paint': {'fill-extrusion-color': '#aaa','fill-extrusion-height-transition': {duration: 0,delay: 0},'fill-extrusion-opacity': 0.6}});} // Older browsers might not implement mediaDevices at all, so we set an empty object firstif (navigator.mediaDevices === undefined) {navigator.mediaDevices = {};} // Some browsers partially implement mediaDevices. We can't just assign an object// with getUserMedia as it would overwrite existing properties.// Here, we will just add the getUserMedia property if it's missing.if (navigator.mediaDevices.getUserMedia === undefined) {navigator.mediaDevices.getUserMedia = function(constraints) {// First get ahold of the legacy getUserMedia, if presentvar getUserMedia =navigator.webkitGetUserMedia || navigator.mozGetUserMedia; // Some browsers just don't implement it - return a rejected promise with an error// to keep a consistent interfaceif (!getUserMedia) {return Promise.reject(new Error('getUserMedia is not implemented in this browser'));} // Otherwise, wrap the call to the old navigator.getUserMedia with a Promisereturn new Promise(function(resolve, reject) {getUserMedia.call(navigator, constraints, resolve, reject);});};} navigator.mediaDevices.getUserMedia({ audio: true }).then(function(stream) {// Set up a Web Audio AudioContext and AnalyzerNode, configured to return the// same number of bins of audio frequency data.var audioCtx = new (window.AudioContext ||window.webkitAudioContext)(); var analyser = audioCtx.createAnalyser();analyser.minDecibels = -90;analyser.maxDecibels = -10;analyser.smoothingTimeConstant = 0.85; var source = audioCtx.createMediaStreamSource(stream);source.connect(analyser); analyser.fftSize = bins * 2; var dataArray = new Uint8Array(bins); function draw(now) {analyser.getByteFrequencyData(dataArray); // Use that data to drive updates to the fill-extrusion-height property.var avg = 0;for (var i = 0; i < bins; i++) {avg += dataArray[i];map.setPaintProperty('3d-buildings-' + i,'fill-extrusion-height',10 + 4 * i + dataArray[i]);}avg /= bins; // Animate the map bearing and light color over time, and make the light more// intense when the audio is louder.map.setBearing(now / 500);map.setLight({color:'hsl(' +((now / 100) % 360) +',' +Math.min(50 + avg / 4, 100) +'%,50%)',intensity: Math.min(1, (avg / 256) * 10)}); requestAnimationFrame(draw);} requestAnimationFrame(draw);}).catch(function(err) {console.log('The following gUM error occured: ' + err);});});</script> </body></html>