最近為小閃光做了國小英文1200單字記憶卡,因為每頁右下角有片留白,一時手癢,決定順便加上翻書動畫(Flip Book)[影片]:
簡單來說,只要為每頁準備一個動畫格(Frame)圖檔,逐一安插在每頁固定位置就可搞定。利用程式產生圖檔對我已不是新把戲[GDI+、HTML5 Canvas],考量未來工作上用HTML5的機會多一點,決定用HTML5 Canvas來做,Coding for Fun兼練兵磨槍。
程式碼主要修改自利用HTML5 Canvas動態產生文字圖示,要補充的只有幾點:
- 跑馬燈文字演算法有點像小時候參加程式設計比賽的考題。近幾年做大型專案、搞一秒幾十萬上下系統的能力毫無長進,但貼身肉博的格鬥倒是練很多,這種小場面嚇不倒我滴~
(如果程式開發像打仗,我這輩子註定做不了領軍征戰運籌帷幄的將軍之才,能當個精於三行四進擅長近身博擊的老士官長肯定稱職,自己也樂在其中了無遺憾。) - 由於必須等待跑馬燈上方圖檔(即本例中的旋轉天竺鼠)載入,方能取得寬高執行後續動作,故主要動作需安排在load()事件中,形成非同步情境。為確保動畫格(Frame)依序產生,需將非同步作業循序化,幸好匍伏前進之前已練習過,我蓋上jQuery.Deferred()結束這一回合。
- 將DataUri轉存圖檔的部分被我省略掉,但滾進的技巧在之前士官長已為大家示範過,單兵們都應該知道如何處置吧!?
其餘細節請看程式註解,另外我也放了一份線上展示給大家玩。稍息後開始動作,稍息!
<!DOCTYPEhtml>
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Image Maker</title>
<scripttype='text/javascript'
src='http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js'></script>
<style>
img { margin: 2px; }
body { background-color: #cccccc; }
.animation { position: absolute; top: 12px; left: 12px; display: none; }
</style>
</head>
<body>
<script>
//利用canvas為圖檔加上跑馬燈文字製作成動畫格(Frame)圖檔
function createFrameImage(idx, text, imgPath, opt) {
//預設參數
var defaultOptions = {
fontStyle: "bold", //normal, bold, italic
fontName: "Courier New",
fontSize: 14, //以Pixel為單位
fgColor: "black",
padding: 10
};
var options = $.extend(defaultOptions, opt);
//建立Canvas,開始幹活兒
var canvas = document.createElement("canvas"),
context = canvas.getContext("2d");
//評估文字尺寸
var font = options.fontStyle + " " + options.fontSize + "px " +
options.fontName;
context.font = font;
var metrics = context.measureText(text);
//Deferred物件, 用來同步圖檔製作完成時間,實現循序執行效果
var dfd = $.Deferred();
//建立圖檔物件
var $img = $("<img />"); $img.appendTo("body");
//圖片載入後才能由.with()/.height()取寬高,故等待load後再執行
$img.one("load", function () {
//由圖檔及文字決定Canvas尺寸
var w = Math.max(metrics.width, $img.width());
var h = $img.height() + options.padding + options.fontSize;
canvas.width = w; canvas.height = h;
//在Canvas放入圖檔,上方置頂,左右置中
context.drawImage($img[0], (w - $img.width()) / 2, 0);
//印出文字
context.textAlign = "left";
context.fillStyle = options.fgColor;
context.font = font;
context.fillText(text, 0,
$img.height() + options.fontSize / 2 + options.padding);
//移除原始圖檔
$img.remove();
//將圖檔新增至body
$("<img />", { src: canvas.toDataURL(), "class": "frame" })
.appendTo("body");
//通知執行完畢
dfd.resolve();
}).attr("src", imgPath);
return dfd.promise();
}
var idx = 0;
var origWord = "darkthread";
var p = origWord.split(""); //拆成字串陣列
//將跑馬燈之文字移動過程展開為字串陣列
var words = [], prefix = "";
//第一張先放入長度與origWord相同的空白字元字串
words.push(new Array(origWord.length).join(" "));
//做出字母逐一由右移至左堆積的跑馬燈效果
for (var i = 0; i < p.length; i++) {
prefix = origWord.substr(0, i);
var runLen = p.length - i;
for (var j = runLen - 1; j >= 0; j--) {
var runStr = $.map(new Array(runLen), function (v, k) {
return k == j ? p[i] : " ";
}).join("");
words.push(prefix + runStr);
}
}
//利用jQuery.Deferred()循序產生跑馬燈動畫圖檔
var chain = $.Deferred().resolve();
$.each(words, function (idx, word) {
//將跑馬燈文字套在八張連續動畫格上
var imgPath = "GPA" + ((idx % 8) + 1) + ".png";
//利用.then()進行循序串接
chain = chain.then(function () {
return createFrameImage(idx, word, imgPath, {});
});
});
//待全部執行完畢後模擬翻書動畫效果
chain.done(function () {
alert("製作完畢,進行模擬播放!");
//用快速切換各Frame圖檔顯示模擬翻書動畫效果
var $frames = $(".frame");
//透過css將所有Frame移至同一絕對座標並先隱藏
$frames.addClass("animation");
var idx = 0, lastIdx = -1;
setInterval(function () {
if (lastIdx > -1) $frames.eq(lastIdx).hide();
$frames.eq(idx).show();
lastIdx = idx;
idx = (idx + 1) % words.length;
}, 150);
});
</script>
</body>
</html>