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

簡單來說,只要為每頁準備一個動畫格(Frame)圖檔,逐一安插在每頁固定位置就可搞定。利用程式產生圖檔對我已不是新把戲[GDI+HTML5 Canvas],考量未來工作上用HTML5的機會多一點,決定用HTML5 Canvas來做,Coding for Fun兼練兵磨槍。

程式碼主要修改自利用HTML5 Canvas動態產生文字圖示,要補充的只有幾點:

  1. 跑馬燈文字演算法有點像小時候參加程式設計比賽的考題。近幾年做大型專案、搞一秒幾十萬上下系統的能力毫無長進,但貼身肉博的格鬥倒是練很多,這種小場面嚇不倒我滴~
    (如果程式開發像打仗,我這輩子註定做不了領軍征戰運籌帷幄的將軍之才,能當個精於三行四進擅長近身博擊的老士官長肯定稱職,自己也樂在其中了無遺憾。)
  2. 由於必須等待跑馬燈上方圖檔(即本例中的旋轉天竺鼠)載入,方能取得寬高執行後續動作,故主要動作需安排在load()事件中,形成非同步情境。為確保動畫格(Frame)依序產生,需將非同步作業循序化,幸好匍伏前進之前已練習過,我蓋上jQuery.Deferred()結束這一回合。
  3. 將DataUri轉存圖檔的部分被我省略掉,但滾進的技巧在之前士官長已為大家示範過,單兵們都應該知道如何處置吧!?

其餘細節請看程式註解,另外我也放了一份線上展示給大家玩。稍息後開始動作,稍息!

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Image Maker</title>
    <script type='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>

Comments

Be the first to post a comment

Post a comment