iT邦幫忙

DAY 25
1

SVG 與 D3.js系列 第 25

D3.js 也可以像音樂一樣動吃動吃動(2)

完整範例請看:http://wcc723.github.io/d3js/2014/10/24/Ironman-30-days-25/

上一篇介紹了資料的來源以及動機,這篇就來介紹執行的層面吧。

在原本的範例中,是只有一個圓在跳動,但是音樂所能擷取的資料是一段很長的陣列,我就思考著是不是有辦法做成折線圖,並且讓他有躍動的感覺。

音樂來源:http://unlimited.kptaipei.tw/

範例參考:http://webfinal.herokuapp.com/slides.html

HTML

先看一下HTML的結構,在左邊呈現圖像化的效果,右邊是柯P的音樂選單,重要的是下方的class="controller",這並不是js的套件或是code,僅僅是Chrome的html5物件。

<div class="demo">
<div class="player">
  <div class="visual">
  </div>
  <div class="playlist">
  </div>
  <div class="controller">
  </div>
</div>
</div>

柯P資料

直接透過d3.json去接柯P的資料,之前有提過KP這方面處理得相當好,用d3.js就可以直接接資料,接回來的範例如下,也可以直接打開chrome console看更完整資料。

d3.json(kpMusic, function(data){ //取得柯P音樂資料
  console.log(data.data)
  dataMusic = data.data; //存到dataMusic
  playlist() //執行下一個function
});

Line Chart

這一段是特別需要注意的,因為音樂的所傳回的陣列長度是2048,值大概像下面這樣,範圍由-1 ~ 1,也因為長度過長,在繪製成圖形上會有效能上的問題,所以擷取其中一段即可。

所以該段js大概像這樣:

//buffer 為傳來的值

newBuffer = Array.prototype.slice.call(buffer,0,64)
//由於來源陣列長度有2048,這邊只取64就好

var xScale = d3.scale.linear().domain([0, newBuffer.length]).range([0, w]);
  var yScale = d3.scale.linear().domain([1, -1]).range([h, 0]);
  //先前有提到資料最大及最小就是-1 ~ 1,輸出範圍就是曲線跳動的最大範圍

  var line = d3.svg.line()
      .x(function(d,i) { 
        return xScale(i + 1); //利用尺度運算資料索引,傳回x的位置
      })
      .y(function(d) { 
        return yScale(d); //利用尺度運算資料的值,傳回y的位置
      });

  path.attr('d', line(newBuffer)); //將音樂資料套用至曲線

DEMO

完整範例請看:http://wcc723.github.io/d3js/2014/10/24/Ironman-30-days-25/

很抱歉,目前本範例只支援Chrome

完整Code

如果有興趣想要跟著實驗,只要把這段貼回家就可以玩囉

var kpMusic = 'http://api.kptaipei.tw/v1/musics/1?accessToken=kp54103aa1efbe14.85567715'
//柯p資料路徑

var dataMusic = ""; //音樂資料
var colorBase = 0; //圓圈的顏色起始
var context; //音樂格式
var h = 100, w = 300; 

d3.json(kpMusic, function(data){ //取得柯P音樂資料
  console.log(data.data)
  dataMusic = data.data; //存到dataMusic
  playlist() //執行下一個function
});


//這部分是建立基本的layout
var svg = d3.select(".visual").append('svg')
  .attr({
    width: 300,
    height: 300
  });

//畫面中的圓圈
circle = svg.append('circle')
  .attr({
    cx: 150,
    cy: 150
  });

//播放中音樂的文字
playtext = svg.append('text')
  .attr({
    x: 20,
    y: 20,
    width: 260,
    fill: "white"
  })

//中央跳動的曲線
rect = svg.append('g')//增加一個群組g
    .attr('width', w) 
    .attr('height', h) 
    .attr('transform', 'translate(0,' + (h)  + ')');
path = rect.append('path')


var audio = new Audio(); //建立音樂
function playlist(){
d3.select(".playlist").selectAll("a").data(dataMusic) //右方先插入歌單
  .enter() 
  .append("a")
  .text(function(d){ return d.song_name }) //傳入音樂名稱
  .attr("class", "song"); //插入Class,作為控制用

  audio.controls = true; //html5 音樂控制器
  audio.preload = true; //html5 預先下載
  document.querySelector('.controller').appendChild(audio);
  //指定部分插入html5 audio元件

  d3.selectAll(".song").on("click", function(d){
    playsong(d); //點擊歌單時載入音樂
  });
}

//瀏覽器驗證
try
{
  context = new webkitAudioContext();
}
catch(e)
{
  try
    {
      context= new AudioContext();
    }
  catch(e)
    {
    }
}

function processAudio(e) { //如果音樂開始執行時
  //取得音軌資訊
  var buffer = e.inputBuffer.getChannelData(0);
  var out = e.outputBuffer.getChannelData(0);
  var amp = 0;
  
  // Iterate through buffer to get max amplitude
  for (var i = 0; i < buffer.length; i++) {
    var loud = Math.abs(buffer[i]);
    if(loud > amp) {
      amp = loud;
    }
    // write input samples to output unchanged
    out[i] = buffer[i];
  };

  newBuffer = Array.prototype.slice.call(buffer,0,64)
  //由於來源陣列長度有2048,這邊只取64就好


  var xScale = d3.scale.linear().domain([0, newBuffer.length]).range([0, w]);
  var yScale = d3.scale.linear().domain([1, -1]).range([h, 0]);
  //先前有提到資料最大及最小就是-1 ~ 1,輸出範圍就是曲線跳動的最大範圍

  var line = d3.svg.line()
      .x(function(d,i) { 
        return xScale(i + 1); //利用尺度運算資料索引,傳回x的位置
      })
      .y(function(d) { 
        return yScale(d); //利用尺度運算資料的值,傳回y的位置
      });

  colorBase ++; //讓顏色隨著時間變化
  var color = d3.hsl(colorBase % 360, .7,.7); //d3.hsl色彩運算

  circle.attr("r",20+(amp*50)) //圓圈大小變換,以及顏色變換
  .attr("fill", color)
  .attr("stroke", color);

  path.attr('d', line(newBuffer)); //將音樂資料套用至曲線
}

//以下為音樂相關的code
var node = context.createMediaElementSource(audio);
var processor = context.createScriptProcessor(2048,1,1);

function playsong(d){
  console.log(d)
  audio.src = d.stream_url;
  playtext.text(d.song_name + ' / ' + d.groupname) 
  audio.addEventListener('canplaythrough',function() {
    
    processor.onaudioprocess = processAudio;
    node.connect(processor);
    processor.connect(context.destination);
  });
}

還有CSS要補上喔~

.player {
  background-color: #000;
  width: 540px;
  padding: 20px;
  border-radius: 2px;
}
.player circle{
  fill: none;
}
path {
  stroke: DodgerBlue;
  fill: none;
}
.visual{
  float: left;
}
.playlist{
  float: right;
  width: 200px;
  height: 300px;
  overflow-y: auto;
}
.playlist a{
  color: white; 
  display: block;
  cursor: pointer;
}
.playlist a.selected {
  color: DeepSkyBlue;
}
.controller{
  clear: both;
}

後記

鐵人賽快要結束了,不過我到快結束才更了解D3js,有很多豐富的技巧還沒介紹的說...。


上一篇
D3.js 也可以像音樂一樣動吃動吃動(1)
下一篇
d3.js layout (以環保局資料為例)
系列文
SVG 與 D3.js30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言