主題:CSS技巧:逐幀動畫抖動解決方案    

刊登時間:2019/5/16 13:54:55    點閱率:70


     



 

筆者所在的前端團隊主要從事移動端的H5頁面開發,而團隊使用的適配方案是:  viewport units + rem具體可以參見凹凸實驗室的文章–  利用視口單位實現適配佈局  。

筆者目前(2017.08.12)接觸到的移動端適配方案中,「利用視口單位實現適配佈局」是最好的方案。不過使用  rem 作為單位會遇到以下兩個難點:

  • 微觀尺寸(20px左右)定位不准
  • 逐幀動畫容易有抖動

第一個難點的通常出現在  icon 繪製過程,可以使用圖片或者  svg-icon  解決這個問題,筆者強烈建議使用  svg-icon,具體理由可以參見:「擁抱Web設計新趨勢:SVG Sprites實踐應用」。

第二個難點筆者舉個例子來分析抖動的原因和尋找解決方案。

一個抖動的例子

做一個8幀的逐幀動畫,每幀的尺寸為:360×540。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.steps_anim {
position : absolute;
width : 9rem ;
height : 13.5rem ;
background : url (//misc.aotu.io/leeenx/sprite/m.png) 0 no-repeat;
background-size : 45rem 13.5rem ;
top : 50% ;
left : 50% ;
margin : – 5.625rem 0 – 5.625rem ;
animation : step 1.2s steps (5) infinite;
}
 
keyframes step {
100% {
background-position : – 45rem ;
}
}

觀察在主流(手機)分辨率下的播放情況:

iPhone 6 
(375×667)
iPhone 6+ 
(414×736)
iPhone 5 
(320×568)
Android 
(360×640)

四種分辨率下,可以看到除了  ip6 其它的三種分辨率都發生了抖動。ip6 不抖動的原因是適配方案是基本於  ip6 的分辨率訂製的。)

分析抖動

圖像由終端(屏幕)顯示,而終端則是一個個光點(物理像素)組成的矩陣,換句話說圖片也一組光點矩陣。為了方便描述,筆者假設終端上的一個光點代表css中的1px。

以下是一張  9px * 3px 的sprite:
9px * 3px

每幀的尺寸為  3px * 3px,逐幀的取位過程如下:
9px * 3px

把sprite的background-size的寬度取一半,那麼終端會怎麼處理?
9 / 2 = 4.5 
終端的光點都是以自然數的形式出現的,這裡需要做取整處理。取整一般是三種方式:round/ceil/floor假設是round ,那麼  background-size: 5px,sprite會是以下三種的一個:

情況一 情況二 情況三
9px * 3px 9px * 3px 9px * 3px

理論上,5 / 3 = 1.666...但實際上光點取整後,三個幀的寬度都不可能等於  1.666...,而是有一個幀的寬度降級為  1px(虧),另外兩個寬度升級為  2px(盈),筆者把這個現象稱作「盈虧互補」。

再看一下盈虧互補後,逐幀的取位過程:

情況一 情況二 情況三
9px * 3px 9px * 3px 9px * 3px

可以看到由於盈虧互補導致了三個幀的寬度不一致,虧的那一幀在動畫中的表示就是抖動

筆者總結抖動的原因是:sprite在尺寸縮放後,幀與幀之間的盈虧互補現象導致動畫抖動

附註:1px 由幾個光點表示是由以終端的dpr 決定

解決方案

「盈虧互補」也可以說是「盈虧不一致」,如果尺寸在縮放後「盈虧一致」那麼抖動現象可以解決。

解決構想一

筆者根據「盈虧一致」設計了「解決構想一」:

9px * 3px

根據上圖,其實很容易就聯想到一個簡單的方案:不用雪碧圖(即一幀對應一張圖片)
這個方案確實是可以解決抖問題,不過筆者並不推薦使用它,因為它有兩個負面的東西:

  • KB變大與請求數增多
  • 多餘的animation 代碼

這個方案很簡單,這裡就不贅述了。

解決構想二

把逐幀取位與圖像縮放拆分成兩個獨立的過程,就是筆者的「解決構想二」:
9px * 3px

實現「構想二」,筆者首先想到的是使用  transform: scale(),於是整理了一個實現方案A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
.steps_anim {
position : absolute;
width : 360px ;
height : 540px ;
background : url (//misc.aotu.io/leeenx/sprite/m.png) 0 no-repeat;
background-size : 1800px 540px ;
top : 50% ;
left : 50% ;
transform-origin : left top;
margin : – 5.625rem 0 – 5.625rem ;
transform : scale (.5);
animation : step 1.2s steps (5) infinite;
}
keyframes step {
100% {
background-position : – 1800px ;
}
}
/* 寫斷點*/
media screen and (width: 320px ) {
.steps_anim {
transform : scale (0.4266666667);
}
}
media screen and (width: 360px ) {
.steps_anim {
transform : scale (0.48);
}
}
media screen and (width: 414px ) {
.steps_anim {
transform : scale (0.552);
}
}

這個實現方案A存在明顯的缺陷:scale的值需要寫很多斷點代碼於是筆者結全一段js代碼來改善這個實現方案B:

css:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.steps_anim {
position : absolute;
width : 360px ;
height : 540px ;
background : url ( “//misc.aotu.io/leeenx/sprite/m.png” ) 0 no-repeat;
background-size : 1800 540px ;
top : 50% ;
left : 50% ;
transform-origin : left top;
margin : – 5.625rem 0 – 5.625rem ;
animation : step 1.2s steps (5) infinite;
}
keyframes step {
100% {
background-position : – 1800px ;
}
}

 

javascript:

1
2
3
4
5
6
7
8
9
// 以下代碼放到<head></head> 中
<script>
document .write( “<style id=’scaleStyleSheet’>.steps_anim {scale(.5); }</style>” );
function doResize () {
scaleStyleSheet.innerHTML = “.steps_anim {-webkit-transform: scale(“ + ( document.documentElement.clientWidth / 750 ) + “)}” ;
}
window .onresize = doResize;
doResize();
/script>

 

通過改善後的方案CSS 的斷點沒了,感覺是不錯了,不過筆者覺得這個方案不是個純粹的構建方案。

我們知道  <img> 是可以根據指定的尺寸自適應縮放尺寸的,如果逐幀動畫也能與  <img> 自適應縮放,那就可以從純構建角度實現「構想二」。

SVG剛好可以解決難題!SVG 的表現與  <img> 類似同時可以做動畫。以下是筆者的實現方案C。

html:

1
2
3
svg viewBox = “0, 0, 360, 540” class = “steps_anim” >
image xlink:href = “//misc.aotu.io/leeenx/sprite/m.png” width = “1800” height = “540” />
</ svg >

 

css:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.steps_anim {
position : absolute;
width : 9rem ;
height : 13.5rem ;
top : 50% ;
left : 50% ;
margin : – 5.625rem 0 – 5.625rem ;
image {
animation : step 1 .2s steps (5) infinite ;
}
}
 
keyframes step {
100% {
transform : translate3d (-1800px, 0, 0);
}
}

 

方案C的改良

實現方案C很好地解決了方案A和方案B的缺陷,不過方案C也有它的問題:不利於自動化工具去處理圖片

自動化工具一般是怎麼處理圖片的?
自動化工具一般是掃描CSS文件找出所有的  url(...) 語句,然後再處理這些語句指向的圖片文件。

如果  <image> 可以改用CSS的  background-image 就可以解決這個問題,不過  SVG 不支持CSS的  background-image但是,SVG有一個擴展標籤:foreignObject,它允許向  <svg></svg> 插入  html 代碼。在使用它前,先看一下它的兼容情況:

caniuse

iOS與Android 4.3一片草綠兼容情況算是良好,筆者實機測試騰訊  X5 內核的瀏覽器兼容仍舊良好。以下是改良後的方案。

html:

1
2
3
4
5
svg viewBox = “0, 0, 360, 540” class = “steps_anim” >
foreignObject class = “html” width = “360” height = “540” >
div class = “img” > </ div >
</ foreignObject >
</ svg >

 

css:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.steps_anim {
position : absolute;
width : 9rem ;
height : 13.5rem ;
top : 50% ;
left : 50% ;
margin : – 5.625rem 0 – 5.625rem ;
}
.html {
width : 360px ;
height : 540px ;
}
.img {
width : 1800px ;
height : 540px ;
background : url (//misc.aotu.io/leeenx/sprite/m.png) 0 no-repeat;
background-size : 1800px 540px ;
animation : step 1.2s steps (5) infinite;
}
 
keyframes step {
100% {
background-position : – 1800px 0 ;
}
}

 

改良後的方案DEMO:  http://jdc.jd.com/fd/promote/leeenx/201708/svg-sprite.html

結語

感謝閱讀完本文章的讀者。本文是筆者的個人觀點,希望能幫助到有相關問題的朋友,如果本文有不妥之處請不吝賜教。


參考資料:

https://stackoverflow.com/questions/9946604/insert-html-code-inside-svg-text-element 
https://www.w3.org/TR/SVG/extend.html 
https://developer.mozilla. org/en-US/docs/Web/SVG/Element/foreignObject

 

 

熱門文章 :
JQuery應用 : HTML5應用 : CCS3應用 :
  • 使用jQuery & CSS3 製作美麗的照片畫廊(96)


  • 5種風格的jQuery 分頁效果(90)


  • Notyf – 響應式的JavaScript 通知插件!(83)


  • 基於jQuery 實現的精緻作品集圖片導航效果(63)


  • jQuery移動端雙擊圖片放大特效(57)


  • 基於jQuery 實現動畫內容菜單(56)


  • jQuery留言評論發布代碼(53)


  • 帶分類和縮略圖顯示的jquery相冊特效(46)


  • 使用jQuery UI 創建簡單的電影搜索應用界面(45)


  • 20款移動開發中很有用的jQuery插件(43)


  • HTML5 SVG卡通水母動畫特效(178)


  • HTML5鼠標跟隨立方體碎片特效(152)


  • HTML5 Canvas大波浪動畫特效(140)


  • HTML5 SVG情人節盒子動畫特效(122)


  • HTML5文字跑馬燈動畫特效(98)


  • HTML5 Canvas流動線條動畫特效(74)


  • HTML5線條照射愛心動畫特效(70)


  • 12個很炫的HTML5 和CSS3 應用示例(59)


  • HTML5繪製彩色圓圈動畫特效(54)


  • HTML5線條像素背景動畫特效(48)


  • Creative CSS Loading Animations(268)


  • CSS3圓點冒泡背景動畫特效(263)


  • CSS3轉盤抽獎動畫特效樣式(156)


  • CSS技巧:逐幀動畫抖動解決方案(70)


  • 純CSS實現帶縮略圖的圖片相冊幻燈片效果(68)


  • 純CSS打造的鼠標懸停會顯示邊框和描述的圖片相冊特效(58)


  • 純CSS3聖誕樹雪花飄落動畫特效(56)


  • 經典網頁設計:30個創意的CSS 應用案例(50)


  • 23種漂亮的純CSS導航菜單(48)


  • 45款高質量的免費(X)HTML/CSS模板(46)


  • 素材資源 : 網頁設計 : 創意欣賞 :
  • SVG的形狀懸停效果 (一)(553)


  • SVG的形狀懸停效果 (二)(117)


  • 精品資源:40個實用的PSD 貼紙模板《下篇》(55)


  • SVG的形狀懸停效果 (三) (53)


  • HTML5三維立體泡沫方塊移動特效(50)


  • 創建紋理文本的技巧(50)


  • 支付信息提交組件網頁模板(49)


  • Sliding Header Layout (二)(49)


  • Recreating the “Design Samsung” Grid Loading Effect(40)


  • 3D Shading with Box-Shadows(39)


  • LightGallery.js – 功能齊全的Lightbox 效果(122)


  • 舌尖上的設計!10個美味的餐館和食品網站(78)


  • 令人稱讚的以辦公環境為背景的網站作品(76)


  • 具有Three.js的交互式排斥效果 (二)(75)


  • 具有Three.js的交互式排斥效果 (一)(65)


  • 20款優秀的國外Mobile App 界面設計案例(64)


  • 絲帶(Ribbon)在網頁設計中應用的20佳案例(53)


  • 20個國外優秀的網頁色彩搭配案例(51)


  • 20個以矩形元素為特色的創意網站設計作品(51)


  • 基於jQuery實現聯想大屏焦點圖切換(46)


  • 經典網頁設計:20個值得關注的創意作品集網站示例(120)


  • 50個最優秀的Photoshop 實例教程【文字特效篇】(118)


  • 【特別推薦】12套精緻的網站和移動應用界面設計素材(76)


  • 激發靈感:26個清爽的藍色調網頁設計作品欣賞(70)


  • 【推薦閱讀】簡單之美:30個極簡主義風格的網站作品(54)


  • 經典網頁設計:15個使用網格系統的精美網站作品(49)


  • 【年度盛宴】2019年排名前20位的CSS 網站作品(48)


  • 太有才了!史上最有創意的404頁面設計集錦(44)


  • 經典回顧——2019年度最佳網頁設計作品出爐【下篇】(41)


  • 15款加速Web開發的JavaScript框架(37)


  • 網頁模板 : 手機模板 : 網路科技 :
  • 照片藝術日誌網頁模板(80)


  • 圖片日誌信息網頁模板(69)


  • SNS分享圖標按鈕網頁模板(61)


  • 歡迎使用TEMPLATED提供的免費響應式網站模板(50)


  • Mountain Slider(41)


  • 個人圖片信息介紹網頁模板(39)


  • 藍色精美風格的品牌家居商城網站模板下載(39)


  • 黑色標準風格的引導頁模板下載(36)


  • 紅色圖片列表網頁模板(35)


  • 流行時尚登錄框網頁模板(34)


  • 香脆食品料理響應式網頁模板(70)


  • 圖片相冊展示響應式網頁模板(63)


  • Metro圖片信息展示響應式網頁模板(59)


  • 簡潔產品展示網店響應式網頁模板(57)


  • Fringilla Fermentum Tellus(54)


  • 藝術畫館網頁模板(53)


  • 藝術圖片展示響應式網頁模板(46)


  • 女包商城展示響應式網頁模板(45)


  • 建築設計團隊介紹響應式網頁模板(45)


  • 精品餐廳美食展示響應式網站模板(44)


  • 15款方便團隊溝通的即時聊天工具(181)


  • 50款Android 移動應用程序圖標【下篇】(86)


  • 20款你沒聽說過的小眾Web瀏覽器(79)


  • 傳說這個是35 個最好用Vue 開源庫(74)


  • 30個超棒的SVG動畫展示【上篇】(51)


  • 8款Chrome插件讓你的標籤頁更酷炫(49)


  • 50個最實用的Photoshop 實例教程【照片特效篇】(48)


  • OverAPI.com – 史上最全的開發人員在線速查手冊(44)


  • 20個使用手寫字體的作品欣賞(44)


  • Clipboard.js – 現代方式複製文本到剪貼板(41)


  •     

    服務電話:0982-450920(亞太)、0905-147856(中華)、0970-755956(台哥大) 陳老師