例項講述開發中的圖片使用者體驗要點

語言: CN / TW / HK

theme: v-green highlight: atom-one-dark


本文為掘金社群首發簽約文章,14天內禁止轉載,14天后未獲授權禁止轉載,侵權必究!

前言

在移動應用中,圖片是最佳的視覺衝擊元素,色彩豐富、吸引眼球的圖片能夠刺激使用者的點選衝動,進而提高轉化率。下面的一張圖就是來自淘寶一個典型的對比,右圖和左圖相比,顯然更加具備吸引力。那麼在日常開發中有哪些影響圖片體驗的因素呢? image.png

載入過程

圖文列表通常首先吸引眼球的是圖片,但是如果體驗做得不好差別還挺大的,比如下面這種列表,在載入過程中,由於網路速度較慢,導致圖片區域全是空白 —— 這會導致使用者無法預測介面的元素,從而影響使用者體驗。

image.png

在載入過程中,好的使用者體驗應該是給可預期的結果,而不是盲目猜測。

對於解決這種問題來說,有兩種做法,一個是常見的佔位圖形式,還有一種是骨架屏。對於 Flutter 來說,佔位圖的形式可以使用 CachedNetworkImage 外掛實現。

image.png

示例程式碼如下,這裡使用了 CachedNetworkImageplaceholder 屬性來返回一個佔位元件,從而指示使用者此處是一張圖片。載入過程中,相比之前的左側空了一小半的體驗來說好很多。 ```dart class TextImageListItem extends StatelessWidget { final String imageUrl; final String text; const TextImageListItem({ Key? key, required this.imageUrl, required this.text, }) : super(key: key);

@override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(15.0), child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ CachedNetworkImage( imageUrl: imageUrl, placeholder: (context, string) { return Container( width: 150, height: 100.0, color: Colors.grey[300], child: Icon( Icons.image, size: 20.0, color: Colors.grey[400], ), ); }, width: 150, height: 100.0, ), const SizedBox( width: 10.0, ), Expanded( child: Text( text, maxLines: 3, style: const TextStyle( overflow: TextOverflow.ellipsis, ), ), ), ], ), ); } } ```

至於骨架屏,推薦大家可以使用一個叫做 skeletons 的外掛,既可以簡單地使用也可以自定義骨架屏的樣式,下面是使用骨架屏的最簡單的方法,如果要和介面保持一致的話需要自定義列表檢視。

dart @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('圖文列表'), ), body: Skeleton( isLoading: _isLoading, skeleton: SkeletonListView(), child: ListView.builder( itemBuilder: (context, index) { return const TextImageListItem( imageUrl: 'http://th.bing.com/th/id/OIP.dh9AWBD2vmtsJF6MLwxFdwE9DE?w=277&h=180&c=7&r=0&o=5&pid=1.7', text: '沒有 UE 給互動效果怎麼辦?照著原型開發行不行?一張草圖打天下?開發全憑專案經理一句話?這些情況下,如何保障使用者體驗?本專欄將通過例項來講如何面向使用者體驗開發。', ); }, itemCount: 20, ), ), ); } skeletons.gif

錯誤處理

圖片一般情況下是能夠正常載入的,但是如果遇到網路超時或者圖片檔案被誤刪,就可能載入不出來。這個時候如果沒有任何指示,那麼體驗是很糟糕的。合理的做法是給出一個載入錯誤的佔位圖,這個在 CachedNetworkImage 中也提供了相應的屬性,可以利用 errorWidget 引數返回載入圖片出錯時的一個元件替代圖片位置。這裡我們特意修改了圖片連結為一個錯誤連結來模擬載入錯誤的情況,然後在圖片位置告知使用者圖片載入失敗,程式碼如下。 dart CachedNetworkImage( imageUrl: imageUrl, placeholder: (context, string) { return Container( width: 150, height: 100.0, color: Colors.grey[300], child: Icon( Icons.image, size: 20.0, color: Colors.grey[400], ), ); }, errorWidget: (context, string, obj) { return Container( width: 150, height: 100.0, color: Colors.grey[300], child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.image_not_supported_outlined, size: 20.0, color: Colors.grey[400], ), Text( '圖片載入失敗', style: TextStyle( color: Colors.grey[400], fontSize: 12.0, ), ) ], )); }, width: 150, height: 100.0, ),

image.png

這種方式對於圖片找不到處理是沒問題的,但是如果是網路問題導致的,那麼提供一個重新載入按鈕體驗會更好 —— 畢竟你不能讓使用者為了重新載入一張圖片來個頂部下拉重新整理。這種情況如果滑動了好幾頁的話,那使用者還得重新找到之前的位置,體驗就不怎麼好了。因此,更好的方式是提供一個重新載入按鈕,畢竟實際圖片檔案被誤刪的可能性很低,網路原因導致載入不出來頻次更高(尤其是支援 gif 圖片的情況)。這種情況建議對列表元素進行進行二次封裝,以便支援單個列表元素重新載入。最終實現的效果如下圖所示,點選重新載入會將重新整理列表元素元件,從而實現重新載入。

重新載入.gif

控制圖片檔案大小

這個其實和開發有很大關係,一般來說,如果列表存在圖片的,建議生成縮圖。這是因為,如果直接使用原圖的話,可能一次性載入幾十張圖片。現在的手機拍照圖片動輒10幾 M,如果一下子載入幾十張10M 的圖片檔案,列表大概率會卡死。因此,這種情況推薦是後端同學生成列表所需的縮圖,至於如何裁剪或壓縮也一併交給後端吧。

一個非常牛的縮圖編解碼庫

網上見過一個非常牛的庫,叫做 BlurHash,可以將圖片編碼轉換為字元(20-30個字元),然後前端將這字元解碼渲染為圖片,神奇的地方在於這張圖片就像是原圖模糊後的圖片一樣。這種圖片可以替代佔位圖,下面是他們官網的一個演示效果,可以看到,相比灰不溜秋的佔位圖好很多。 blurhash.gif

總結

圖片是前端應用中吸引眼球的主要元素,可以採取如下三種方式提升圖片元素的使用者體驗:

  1. 載入過程中增加佔位圖或者使用骨架屏;也可以考慮使用 BlurHash 這個庫,只是需要前後端配合。
  2. 充分考慮圖片載入不出來的使用者體驗,最好是提供重新載入操作。
  3. 控制列表的圖片檔案大小,可以要求後端在儲存的時候生成尺寸小、經過壓縮的縮圖,避免列表開始。

原始碼地址:開發中的圖片使用者體驗要點