iT邦幫忙

2021 iThome 鐵人賽

DAY 25
2
Modern Web

這個網站也太嗨!30 個網頁動態提案系列 第 27

#24-這個播放器也太潮!用Canvas放音樂!w/JS web audio API

  • 分享至 

  • xImage
  •  

今天來試試看利用Canvas來做聲音的視覺表現!
搭配Javascript web audio API
(其實是因為找不到好玩的點子)
主要是根據這個影片實作。

老樣子,先看成果!
看成果兩個呈現方式:(請自行搭配音樂XD)

長條型 放射狀

成品這邊請
原始碼這邊請

在拆解code前,想先談一下Base 64格式
想看code的請跳過:


Base 64

影片中有將音樂檔案轉成 Base 64的格式,(利用這個網站)

查了一下Base 64的好處:

  1. 提升網站速度,不用額外請求圖片/影片資源
  2. 可以加密,無法一眼看出內容(最常在某些網站看到XD)

壞處:(列舉部分)

  1.  無法緩存,下次用戶再訪問網站時無法加快載入
  2. SEO不友善 (無法反連結、或分享)

參見:Why "optimizing" your images with Base64 is almost always a bad idea

轉成base64後我的音樂code變成長長一串,
所以我還是直接引用了!
有其他base 64實作上的心得/建議也請分享~

檔案音樂我是在這邊抓的:https://pixabay.com/music/search/genre/beats/

也很適合拿來當影片用~

來看Code吧!

詳細code

上JS code:
詳細可看影片!或可以參考中文的[基于Web Audio API实现音频可视化效果的方法](https://www.yisu.com/zixun/151229.html

const container = document.getElementById('container');
const file = document.getElementById('fileupload'); //去讀取上傳檔案的DOM
//canvas起手式
const canvas = document.getElementById('canvas1');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext('2d');

//這邊是我上的字
ctx.save();
ctx.font="32px 'Ubuntu', sans-serif";
ctx.fillStyle = 'white';
ctx.fillText('點擊畫面播放音樂', 30, 100);
ctx.fillText('或上傳新的檔案', 30, 150);
ctx.translate(canvas.width/2,canvas.width/2);
ctx.restore();

let audioSource;
let analyser;

//點擊之後開始解析音頻
container.addEventListener('click',function(){
    const audio1 = document.getElementById('audio1');
    audio1.src = '../../JSSound/club-fashion-house-2887.mp3'
    const audioContext = new AudioContext();
    audio1.play();
    audioSource = audioContext.createMediaElementSource(audio1);//接收元素
    analyser = audioContext.createAnalyser();//獲取音頻時間和頻率數據
    audioSource.connect(analyser);
    analyser.connect(audioContext.destination); //音頻最終輸出目標,通常是指最終端的電腦喇吧,連結才會聽到聲音
    //下面三行是計算音頻節點的
    //會創造出一個陣列
    analyser.fftSize = 64;//快速傅立葉變換,決定音頻譜的密度
    const bufferLength = analyser.frequencyBinCount; //fftSize的一半
    const dataArray = new Uint8Array(bufferLength); //將每個字節視為0-255單個數字
    //https://zh.javascript.info/arraybuffer-binary-arrays

    const barWidth = (canvas.width/2) / bufferLength;
    let barHeight;
    let x;

    function animate(){
        x=0;
        ctx.clearRect(0,0,canvas.width, canvas.height);
        analyser.getByteFrequencyData(dataArray);

        drawVisualiser(bufferLength,x, barWidth, barHeight, dataArray);

        requestAnimationFrame(animate);

    }

    animate();
});

//這邊是上傳檔案用,跟上面的code差不多
file.addEventListener('change', function(){
    const files = this.files;
    const audio1 = document.getElementById('audio1');
    audio1.src = URL.createObjectURL(files[0]);
    audio1.load();
    audio1.play();

    audioSource = audioContext.createMediaElementSource(audio1);
    analyser = audioContext.createAnalyser();
    audioSource.connect(analyser);
    analyser.connect(audioContext.destination); 
    analyser.fftSize = 64;
    const bufferLength = analyser.frequencyBinCount;
    const dataArray = new Uint8Array(bufferLength);

    const barWidth = canvas.width / bufferLength;
    let barHeight;
    let x;

    function animate(){
        x=0;
        ctx.clearRect(0,0,canvas.width, canvas.height);
        analyser.getByteFrequencyData(dataArray);
        drawVisualiser(bufferLength,x, barWidth, barHeight, dataArray);

        requestAnimationFrame(animate);
    }

    animate();
    
});

//畫圖用
function drawVisualiser(bufferLength,x, barWidth, barHeight, dataArray){
    
    // 1.中心圓的版本
    // for(let i = 0; i<bufferLength; i++){
    //     barHeight = dataArray[i];
    //     ctx.save(); //將當前狀態保存, 不保存的話下面的圖案都會跟著上一個的參數旋轉
    //     ctx.translate(canvas.width/2, canvas.height/2);
    //     ctx.rotate(i * Math.PI * 2 / bufferLength);
    //     const hue = i * 3 ;
    //     ctx.fillStyle = 'hsl('+hue+',100%, 50%)';
    //     ctx.fillRect(0, 0, barWidth, barHeight);
    //     ctx.restore(); //取出原來所保存狀態
    //     // save & restore說明:https://blog.csdn.net/tiankongcheng6/article/details/83000247
    // }

    //2. 柱狀的版本
		//左側
    for(let i = 0; i<bufferLength; i++){
        barHeight = dataArray[i];
        const red = i * barHeight/10;
        const green = i*4;
        const blue = barHeight/10;
        ctx.fillStyle = 'rgb('+red+','+green+','+blue+')';
        ctx.fillRect(canvas.width /2 - x, canvas.height - barHeight, barWidth, barHeight);
        x += barWidth;
    }

		//右側

    for(let i = 0; i<bufferLength; i++){
        barHeight = dataArray[i];
        const red = i * barHeight/10;
        const green = i*20;
        const blue = barHeight/10;
        ctx.fillStyle = 'rgb('+red+','+green+','+blue+')';
        ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
        x += barWidth;
    }

}

以上!

Canvas可以玩的東西很多!音樂也是滿有趣的議題,
第一次玩JS audio API,
裡面有很多跟音訊相關的硬知識,
比圖像還複雜,算是初體驗吧!

再放一次:
成品這邊請
原始碼這邊請

有錯誤/想法歡迎留言~


上一篇
#23-用Canvas做Google恐龍遊戲(都市老妹生存記!能擊退經痛加班和渣男嗎?)
下一篇
#25-讓長條圖一條條動起來~大數據時代!入手 D3.js~
系列文
這個網站也太嗨!30 個網頁動態提案33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言