1. 程式人生 > >CSS或JS實現gif動態圖片的停止與播放

CSS或JS實現gif動態圖片的停止與播放

 by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=5081

 一、

對於習慣性刷微博的我,總時不時會看到類似下面的遊戲:

測測你和小白(白百何)有哪些共同點,戳開動圖,最先看清的詞是什麼?ie瀏覽器的同學可以按esc鍵(或截圖),據說在哪個詞暫停,哪個詞就是你哦![嘻嘻]

人特質gif動畫

OK, 這裡出現一個瀏覽器特性,就是通過ESC快捷鍵,暫停gif的播放。據說FireFox瀏覽器以前也有,後來被幹掉了,根據

@紫雲妃的說法是:

是這樣的,Firefox原來的表現是:在頁面load事件完成,同時x按鈕變成重新整理按鈕之後,esc仍然有幾個作用,中斷當前正在傳送的ajax,websocket,停止gif,apng動畫的播放.但這些功能太小眾了,影響了普通使用者的使用,可能不小心按了esc,結果ajax斷了,網頁出錯了.所以Firefox20修改成:網頁載入完成後,esc鍵完全失效.

然而,這種隱晦的但似乎會影響正常功能的小技巧顯然是無法實現真正意義上的gif動態圖片的停止與播放的。一是相容性,二是功能性,三是移動端沒有ESC鍵。

所以,如果我們遇到需要可以隨時隨地停止gif動態圖片播放的需求的時候,就需要尋找其他的出路。好,寒冬裡的暖身結束,開始進入正題~~

 

二、gif圖片自己可控前提下的方法一:多img資源控制處理

假如說,我們希望暫停的gif是自己(開發人員)傳上去的,不是使用者可以隨機上傳不可控的gif. 我們可以這麼處理,就是準備2套圖片,一個是gif動態圖片,還有一個是隻有一幀的靜止的圖片。然後使用JS來回切換<img>src值為這兩張圖片地址就好了。

此方法甚簡單,我就不放例項了。

img.src="animate.gif";
// 或者呈現的是
img.src="static.png";

這個方法最大的優點就是相容性強,所有瀏覽器都可以實現停止效果。然而,這種方法有個侷限,就是,暫停時候呈現的圖片永遠是同一張。基本上可以說是停止,而不是暫停。

那有沒有什麼方法可以真正意義上的暫停呢?還真有!

三、gif圖片自己可控前提下的方法二:CSS3 animation控制

也就是我們看到的gif效果並不是一個真正的gif圖片,而是使用CSS3的animation屬性控制形成的逐幀動態圖片效果。我搜了下,@DO1路人乙有篇文章“css3-animation製作逐幀動畫”專門介紹了這種技術。說穿了就是animation控制Sprites圖片的background-position值模擬gif效果。

例如,新版twitter的Like的效果,貌似就有使用該技術:
twitter的Like效果

使用CSS3 animation實現類gif效果的好處在於,圖片可以無損,且我們可以很輕鬆地控制圖片動畫的暫停和播放,使用的是:animation-play-state: paused;這個宣告。

您可以狠狠地點選這裡:使用CSS3 animation實現gif動圖的暫停和播放demo

點選demo頁面的暫停按鈕,您會發現,直接就停住了,如下截圖示意,截自IE10瀏覽器:
IE10瀏覽器動畫暫停截圖

再次點選,就會在暫停畫面之後繼續播放了。從而實現了我們對動畫圖片的精確控制效果。

此方法看上去完美,但是,1. IE10+等支援CSS3 animation的瀏覽器才行;2. 最大的問題是圖片需要是自己控制,如果想控制使用者上傳的真正意義的gif圖片,只能……望洋興嘆……………………嗎?

四、自己無法控制的gif圖片的停止與播放

比方說,頁面上使用者上傳了些gif圖片,哎呀,閃瞎了我的中華田園眼,我要全部暫停,腫麼辦?如果後臺同學沒有對gif進行靜態處理,此時,只能靠前端小夥伴,有什麼辦法嗎?

有一個。HTML5 canvas可以讀取圖片資訊,繪製當前圖片。於是可以實現圖片馬賽克,模糊,色值過濾等很多圖片特效。我們這裡不用那麼複雜,只要讀取我們的圖片,重繪下就可以。

您可以狠狠地點選這裡:使用JS和canvas實現gif動圖的停止和播放demo

點選按鈕,然後:
gif停止中

gif圖片播放中……

如何使用?
我對HTMLImageElement原型進行了擴充套件,增加了stop()play()兩個方法,如下:

if ('getContext' in document.createElement('canvas')) {
    HTMLImageElement.prototype.play = function() {
        if (this.storeCanvas) {
            // 移除儲存的canvas
            this.storeCanvas.parentElement.removeChild(this.storeCanvas);
            this.storeCanvas = null;
            // 透明度還原
            image.style.opacity = '';
        }
        if (this.storeUrl) {
            this.src = this.storeUrl;    
        }
    };
    HTMLImageElement.prototype.stop = function() {
        var canvas = document.createElement('canvas');
        // 尺寸
        var width = this.width, height = this.height;
        if (width && height) {
            // 儲存之前的地址
            if (!this.storeUrl) {
                this.storeUrl = this.src;
            }
            // canvas大小
            canvas.width = width;
            canvas.height = height;
            // 繪製圖片幀(第一幀)
            canvas.getContext('2d').drawImage(this, 0, 0, width, height);
            // 重置當前圖片
            try {
                this.src = canvas.toDataURL("image/gif");
            } catch(e) {
                // 跨域
                this.removeAttribute('src');
                // 載入canvas元素
                canvas.style.position = 'absolute';
                // 前面插入圖片
                this.parentElement.insertBefore(canvas, this);
                // 隱藏原圖
                this.style.opacity = '0';
                // 儲存canvas
                this.storeCanvas = canvas;
            }
        }
    };
}

大家只要在頁面中自己的JS檔案中複製上面的程式碼,然後就可以直接:

var image = document.getElementsByTagName("img")[0];
// 停止gif圖片
image.stop();
// 播放gif圖片
image.play();

//zxx: 上面程式碼並未詳盡測試,以及可能的體驗問題(IE閃動)沒有具體處理(影響原理示意),若要實際使用,需要自己再微調完美下。

不足
1. IE9+支援。IE7/IE8不支援canvas沒搞頭。
2. 只能停止gif,不能真正意義的暫停。因為canvas獲得的gif圖片資訊為第一幀的資訊,後面的貌似獲取不到。要想實現暫停,而不是停止,還需要進一步研究,如果你有方法,非常歡迎分享。

------------------------------------------------------------------------------------------------------------------------------

本文gif比較多,如果您是移動裝置檢視本文,會發現,怎麼我的電池怎麼越來越瘦了!不是因為天冷凍小了,而是gif比較耗電。所以,從這個角度講,我們其實有必要在移動端預設停止這些gif的播放,使用者點選再播放。一來省流量,二來省電。

如果沒有靜態圖片資源支援,那不妨試試文章出現的一些方法,有心得記得來這裡反饋哈!

最後,本文的方法都是有瑕疵的,自己也尚未在實際專案中使用過。因此,假如閱讀本文的您:
1. 有更完美的gif暫停與播放方法;
2. 發現文中方法有不足和遺漏;

都非常希望可以不吝賜教!

感謝閱讀!週末溫暖!