blog.Ring.idv.tw

Articles

目前的Web Audio API

#2012-06-12 update - iOS6支援Web Audio API

Web Audio API是一個提供你方便處理或合成聲音的JavaScript API,它是最近這一年內才逐漸開始發展的標準,主要負責推動此標準的是W3C Audio Working Group,該組識是在去年3月25日才成立的,根據他們預期的規劃來看~ 2011年第2季會發佈初步的工作草案(Working Draft),並預計在2012年第3季成為正式標準,不過直到去年2011年12月15日該組識才發佈初步的Web Audio API - Working Draft,談到這邊.. 那為何需要有此標準呢?HTML5不是已經定義好<audio>標籤了嗎?我想應該這麼說~ HTML5 <audio> Tag的主要目的是用於Media Playback,所以它允許你可以將聲音檔放在網路上,並透過瀏覽器直接播放而不需要仰賴其它的Plugin(如:Flash Player),但...如果我們想動態製造些混音或聲音特效時該怎麼處理?為何當初Angry Birds Chrome的聲音功能還需要仰賴Flash呢?而這也就是為何需要有Web Audio API存在的原因。

P.S. Angry Birds Chrome開發人員使用Web Audio API後的心得

玩玩看吧! 改一下下述程式你也能很快的做出一些混音或聲音特效。

var audioContext;
var audioBuffer;

window.addEventListener('load', init, false);
function init() 
{
	try 
	{
		audioContext = new webkitAudioContext();	
		loadSound("music.mp3");
	}	
	catch(e) {
		alert(e);
	}
}
function loadSound(url)
{
	var xhr = new XMLHttpRequest();
	xhr.open("GET", url, true);
	xhr.responseType = "arraybuffer";
	xhr.onload = function() 
	{
		audioContext.decodeAudioData(xhr.response, function(buffer) 
			{
				audioBuffer = buffer;
				playSound();
			}, function(e){alert(e);}
		);
	};
	xhr.send();
}
function playSound()
{
	var source = audioContext.createBufferSource();
	var gainNode = audioContext.createGainNode();
	var filter = audioContext.createBiquadFilter();
	source.connect(filter);
	source.buffer = audioBuffer;
	
	filter.connect(gainNode);
	filter.type = 0; // Low-pass filter. See BiquadFilterNode docs
	filter.frequency.value = 500; // Set cutoff to 500 HZ
	
	gainNode.gain.value = 1.0;
	gainNode.connect(audioContext.destination);		
	source.noteOn(0);
}

相關資源

GETTING STARTED WITH WEB AUDIO API

DEVELOPING GAME AUDIO WITH THE WEB AUDIO API

2012-04-24 16:29:36 | Add Comment

聊聊目前的WebSocket技術

#2012-06-12 update - iOS6支援RFC 6455

The WebSocket Protocol在去年12月已經被IETF(Internet Engineering Task Force)提出作為正式標準了(The WebSocket Protocol {RFC 6455}),另外由W3C(World Wide Web Consortium)所主導的The WebSocket API標準也在同個月份從Working Draft改為Candidate Recommendation狀態,所以如果您有需求想開發HTML5相關的多人互動遊戲或應用程式,其實可以開始善用這個技術了! 因為在一個禮拜前Apache也釋出Tomcat 7.0.27版本,該版本也開始支援WebSocket的技術,雖然比Jetty晚了許久... Orz 不過有一點值得注意的是,在今年的2月份由Oracle的Danny Coward所領導的JSR 356: JavaTM API for WebSocket規格也正式被提出了,不過該規格目前仍尚未定案,所以現階段你採用Tomcat所寫的WebSocket服務如果想移植到Jetty的話,仍需要作部份的改寫。

另外針對Mobile Devices的支援而言,根據mobilehtml5.org所整理的資訊來看,以目前兩大行動裝置平台iOS和Android來說,iOS從4.2版即開始支援WebSocket技術,不過根據筆者測試的結果~ iOS 5.1目前仍尚未支援正式標準的WebSocket Protocol,不過這不打緊~ 下一個版本更新也許就會改善了~ 另一方面Android 4.0內建的Browser雖然也尚未支援WebSocket技術,但是Google所發表的Chrome Beta Browser已經開始支援了,這意味著下一個Android版本的Browser很快就會完整支援WebSocket技術了! (筆者猜測)

倘若從應用的角度上來看,大約兩個禮拜前Mozilla也發表了一個結合WebSocket和Canvas技術的多人線上遊戲實驗BrowserQuest,所以想要開發多人線上即時體驗的應用可以不需要依賴Flash了,不過仍然值得注意的是先前筆者曾在「PhoneGap 1.0.0 for Android」一文所提到的一致性與相容性的問題,畢竟採用Flash技術不太需要去擔心這方面的問題,而現階段每個Browser支援HTML5的程度不一,所以這是需要被考慮的。

相關資源

WebSockets: A Guide

2012-04-13 14:09:19 | Add Comment

近期熱門的Gesture-based Apps

將近快兩個月沒有更新Blog了... Orz 來寫一下近期看到的東西~

大約在二月中旬時,有一個以Gesture-based的Task Management App創造了不少話題,它叫「Clear」,顧名思義它的操作方式就如同它的名字一樣「簡潔」,它允許你可以透過「Pull」、「Swipe」和「Pinch」等手勢方式來操作待辦清單的管理,透過這樣的方式來節省操作時間,同時也提高使用者的使用體驗~

另外在三月中旬時,也有一個以Gesture-based的Calculator App發佈,它叫「Rechner Calculator」,它的概念和一般傳統的計算機最大的不同在於,它只提供「數字鍵」!其餘的加減乘除完全都仰賴手勢的方式操作~ 所以它的介面相對來說也非常的「簡潔」! 從視覺上看起來也來的舒服些~

從這兩個例子都可以知道一個好的使用者操作介面有多麼重要! 尤其是開發視窗系統的時候,多一個按鈕或對話框,從工程開發的角度而言或許很正常,但從使用者的操作體驗而言,這個按鈕或對話框可能每天就要多點上百次!

還記得去年七月寫的一篇「jQuery Mobile - Adding a swipe to delete button to a listview component」文章,裡頭提到了用jQuery Mobile所支援的Touch Event: Swipe來實作一些功能,那它是如何實作的呢?

其實從上圖這四個參數就可以猜得到它是如何定義Swipe這個手勢事件的判斷:

1. scrollSupressionThreshold: 10

水平滑動超過10px位移則呼叫「event.preventDefault()」來防止Scrolling的動作。

2. durationThreshold: 1000

在1秒的時間內完成這個動作,否則它就不是Swipe

3. horizontalDistanceThreshold: 30

水平滑動的位移至少要超過30px

4. verticalDistanceThreshold: 75

垂直位移必須小於75px

只要同時滿足上述四個條件即觸發Swipe事件,不過更令我感到好奇的是,不曉得Apple的「UISwipeGestureRecognizer」是如何定義上述這些參數來決定Swipe事件?

有興趣的朋友可以參考jQM的原始碼:jquery.mobile-1.0.1.js

2012-03-30 18:45:28 | Add Comment

HTML5 video autoplay on iOS

許多時候我們在使用iPhone/iPad瀏覽一些包含HTML5 video的網頁時,會發現為何網頁中的video都不會自動播放?而使用一般PC平臺上的瀏覽器卻又都能自動播放?

根據iOS-Specific Considerations的說明如下:

In Safari on iOS (for all devices, including iPad), where the user may be on a cellular network and be charged per data unit, 
preload and autoplay are disabled. No data is loaded until the user initiates it. This means the JavaScript play() and load() methods 
are also inactive until the user initiates playback, unless the play() or load() method is triggered by user action. 
In other words, a user-initiated Play button works, but an onLoad="play()" event does not.

簡單來說~ 其實主要是用來防止「以量計價」的行動網路費爆增~ 這考量的確沒錯! 但是為何不將此功能改為可選項開關呢?因為現階段有許多採用資費吃到飽方案或是用WiFi網路的客群,若能提供可選項開關或是像使用GPS定位一樣的作法,主動彈出一個confirm視窗來提示是否允許自動播放也不失為一種方式呀!

所以~ 現階段如果想在iPhone/iPad瀏覽器中播放video那就得透過使用者觸發才行... Orz 話雖如此,但其實還可以透過另一種方式來達到video自動播放,那就是透過UIWebView元件來達成,也就是說~ 如果你是採用PhoneGap開發的應用程式,那恭禧你~ 只需要對AppDelegate.m 原始碼加上下述設定:

theWebView.allowsInlineMediaPlayback = YES;
theWebView.mediaPlaybackRequiresUserAction = NO;

同時也需要針對你的video element加上「webkit-playsinline」attribute即可,如:

<video id="video" src="xxx.mp4" autoplay webkit-playsinline></video>

現在開啟你的App就能看到video自動播放!

不過iOS Safari針對Canvas的支援仍有些不足~ 所以上述HTML5 Canvas&Video: Big Buck Bunny範例就算移植到UIWebView仍然無法看到效果的~ 因為iOS Safari針對Canvas少了下述APIs的支援:

void drawImage(HTMLVideoElement image, double dx, double dy);
void drawImage(HTMLVideoElement image, double dx, double dy, double dw, double dh);
void drawImage(HTMLVideoElement image, double sx, double sy, double sw, double sh, double dx, double dy, double dw, double dh);

如此就只能被迫將video轉成一張張image才能達到類似的效果了...

參考資源

UNSOLVED HTML5 VIDEO ISSUES ON IOS

iPad and iPhone html5 video autoplay

2012-01-04 10:41:59 | Add Comment

淺談Canvas - save/restore

CanvasHTML5規格中的一部份,它允許你可以用程式來繪製一些2D圖形或是點陣圖,通常也被拿來製作HTML5遊戲之用,而本文主要介紹Canvas API中最常被初學者混淆的一組函式,它們分別為:「save()」和「restore()」,簡單來說~ 「save()」主要是用來保存目前Canvas的狀態,例如:style, lineWidth, font等,透過「save()」函式的呼叫它會將目前Canvas的狀態「Push」到「Stack」之中,而「restore()」函式就是從「Stack」來「Pop」出上一個Canvas的狀態,讀到這邊如果您已經了解「save/restore」的話,那非常恭禧你已經掌握要領了,反之~ 如果仍然有所疑惑的話,請看下面的範例介紹:

實作範例

本文將透過兩種不同的方式來實作出上圖的範例,以方便理解為何需要「save/restore」函式,使用它有何好處?

範例1 - 不採用save/restore

範例1完整的程式碼如下:(筆者假設您已經有基本的Canvas概念,所以會跳過一些基本的解說)

<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body>
<canvas id="a_canvas" width="200" height="150"></canvas>
<script type="text/javascript">
  var canvas = document.getElementById("a_canvas");
  var ctx = canvas.getContext("2d");
  function drawRect(context, color) {
    context.fillStyle = color;
    context.fillRect(0, 0, 100, 30);
  }
  function rotateDeg(context, deg) {
    var rad = deg * Math.PI / 180;
    context.rotate(rad);
  }
  drawRect(ctx, "red");
  ctx.translate(100,30); 
  rotateDeg(ctx, 45);    
  drawRect(ctx, "blue");
  rotateDeg(ctx, 45);    
  ctx.translate(20, -100); 
  drawRect(ctx, "green");
</script>
</body></html>

P.S. 下述圖形的虛線都作為解說之用,並非繪製在Canvas上的線條

drawRect(ctx, "red");

透過自訂的「drawRect()」函式,然後在Canvas上繪製一個從座標點(0,0)的位置畫一個長為100px寬為30px的紅色矩形。

ctx.translate(100,30); 

將目前Canvas的基準點位置設為(100,30),如上圖所示。

P.S. Canvas基準點位置預設為左上角,和Flash座標系統一致。

rotateDeg(ctx, 45);
drawRect(ctx, "blue");

透過自訂的「rotateDeg()」函式將Canvas往順時針旋轉45度,並繪製一個藍色矩形。

rotateDeg(ctx, 45);

接著再進行一次「rotateDeg()」函式將Canvas再往順時針旋轉45度。(此時已旋轉90度)

ctx.translate(20, -100);

到這邊的程式碼就是重點了,理論上如果以基準點為(0,0)的位置來說,目前要設定的相對基準點位置就是位於(200,50)的位置,但是在沒有運用「save/restore」的方式之下,我們必須自己在腦海裡進行一下矩陣轉換,同時根據上一次的基準點位置(100,30)以重設目前基準點位置(20,-100),最後再透過「drawRect(ctx, "green");」繪製出綠色的矩形出來。

範例2 - 採用save/restore

範例2完整的程式碼如下:

<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body>
<canvas id="a_canvas" width="200" height="150"></canvas>
<script type="text/javascript">
  var canvas = document.getElementById("a_canvas");
  var ctx = canvas.getContext("2d");
  function drawRect(context, color) {
    context.fillStyle = color;
    context.fillRect(0, 0, 100, 30);
  }
  function rotateDeg(context, deg) {
    var rad = deg * Math.PI / 180;
    context.rotate(rad);
  }
  drawRect(ctx, "red");
  ctx.save();            
  ctx.translate(100,30); 
  rotateDeg(ctx, 45);    
  drawRect(ctx, "blue");
  ctx.restore();         
  ctx.translate(200,50); 
  rotateDeg(ctx, 90);    
  drawRect(ctx, "green");
</script>
</body></html>

drawRect(ctx, "red");
ctx.save();            

除了在Canvas上繪製一個紅色的矩形外,這邊還呼叫了「save()」函式,意指為將目前的基準點位置(0,0)狀態「Push」到「Stack」中,以方便待會取回。

ctx.translate(100,30); 
rotateDeg(ctx, 45);    
drawRect(ctx, "blue");

將目前Canvas的基準點位置設為(100,30),同時旋轉45度並繪製上一個藍色矩形。

ctx.restore(); 

這裡呼叫「restore()」函式的重點就是要將剛剛「Push」到「Stack」的Canvas狀態取回,也就是重設基準點位置回到(0,0),並忘掉「save()」和「restore()」之間所設定的Canvas狀態,如:translate()、rotate()。

ctx.translate(200,50); 
rotateDeg(ctx, 90);    
drawRect(ctx, "green");

最後再重設基準點位置為(200,50),並順時針旋轉90度,然後畫上綠色矩形即大功告成,到這邊可以發現端倪了嗎?假設我們不採用「save/restore」函式來輔助的話,我們需要去進行矩陣轉換,而這只是個小範例~ 如果更複雜的圖形那勢必會轉到頭昏眼花... Orz,而透過「save/restore」函式的輔助,不僅讓程式碼的可讀性大大地增加,而且維護上也較為容易些嚕。

參考資源

Understanding save() and restore() for the Canvas Context

2011-11-02 22:14:01 | Add Comment

Next Posts~:::~Previous Posts
Copyright (C) Ching-Shen Chen. All rights reserved.

::: 搜尋 :::

::: 分類 :::

::: Ads :::

::: 最新文章 :::

::: 最新回應 :::

::: 訂閱 :::

Atom feed
Atom Comment