最近為小閃光做了國小英文1200單字記憶卡,因為每頁右下角有片留白,一時手癢,決定順便加上翻書動畫(Flip Book)[影片]:
Image may be NSFW.
Clik here to view.
Clik here to view.

Image may be NSFW.
Clik here to view.
Clik here to view.

Image may be NSFW.
Clik here to view.
Clik here to view.

簡單來說,只要為每頁準備一個動畫格(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>
Clik here to view.