天天看點

[極緻使用者體驗] 如何實作響應式canvas?保持canvas比例?教你讓canvas自适應螢幕寬度!

持續創作,加速成長!

這是我的專欄​​《極緻使用者體驗》​​,适合産品、設計、前端閱讀。産品/設計閱讀後,知道怎麼設計具有極緻使用者體驗的互動。前端閱讀後,知道怎麼實作産品需求。如果你是産品經理,按照我專欄裡内容設計了互動,前端卻跟你說某特性實作不了,請把專欄裡的文章甩給他!不客氣!

背景

​canvas​

​是前端常常會用到的東西,尤其是對于遊戲開發者、圖表開發者而言。

響應式設計(網頁元素可自适應各種螢幕寬度的設計)對于前端,也是非常重要的基本功之一。

前端開發者實作響應式設計的方案有多種,已經列舉在之前的文章裡了:​​《2行代碼,讓你的UI适配移動端、PC端,快來收藏》​​​。文中主要講述的方法是用​

​width=device-width​

​​這個viewport結合​

​min-width​

​實作的,這個思路挺牛逼的,因為這同樣适用于canvas。強烈推薦沒看的同學看一下,這個思路真的超級實用。

什麼是極緻使用者體驗

1. 一屏展示全部canvas

但是本文要講的響應式canvas,為了追求極緻的使用者體驗,提出了更嚴格的限制:要求一屏能展示完整個canvas。

為什麼要有這種限制?

尤其是針對遊戲開發,不管在PC端、移動端,都不希望使用者需要上下滾動來看到完整畫面。甚至很多移動端遊戲的互動是需要使用者手指滑動的,這需要canvas監聽使用者滑動操作,使用者就無法通過滑動來實作頁面上下滾動了,導緻看不到一部分網頁。

是以這個限制是完全合理的,完全照顧到了極緻使用者體驗。

2. canvas比例與頁面初始比例一緻

要求canvas比例跟頁面的初始比例保持一緻,這樣使用者不會有空空的感覺。但是之後如果使用者縮放了浏覽器,要求canvas比例保持不變(這其實是技術上不得不做的一種妥協)。

為什麼隻要求跟頁面初始比例相同

  1. 因為canvas在繪制後,要修改比例,那麼内部所有元素的坐标都需要重繪,很多實體引擎計算需要重新做,對性能影響較大;
  2. 而且開發成本也相當高,canvas不像Dom,頁面寬度變了後浏覽器可自動計算新布局,canvas改變布局是需要開發者手動計算每個元素的新位置的;
  3. 頁面初始比例确定後,大機率頁面比例不會修改了。尤其是移動端,使用者幾乎無法修改頁面尺寸(浏覽器小窗的情況并不多)。

根據以上3點,得出結論:動态改canbas比例是開發成本極高、收益很低(使用者使用次數少)的,即ROI很低,是以沒必要動态修改canvas比例,隻需要canvas比例保持頁面的初始比例,就能達到很好的使用者體驗了。

此外,canvas比例常常有些極限值,如果螢幕比例超出canvas比例極限值,應該保持在極限值。例如畫面寬高比例要求在​

​1:1​

​​至​

​1:2​

​​,那麼螢幕太寬時,就隻能設定canvas比例為極限的​

​1:1​

​​了;而螢幕太窄時,隻能設定canvas比例為極限的​

​1:2​

​。直接看例子:

  • 例如螢幕太寬的場合,超過了canvas比例極限值,則高度塞滿,寬度留白:
  • [極緻使用者體驗] 如何實作響應式canvas?保持canvas比例?教你讓canvas自适應螢幕寬度!
  • 例如螢幕太窄的場合,超過了canvas比例極限值,則寬度塞滿,高度留白:
  • [極緻使用者體驗] 如何實作響應式canvas?保持canvas比例?教你讓canvas自适應螢幕寬度!
  • 例如螢幕比例在canvas比例的2個極限值之間,那麼高度、寬度都塞滿:
  • [極緻使用者體驗] 如何實作響應式canvas?保持canvas比例?教你讓canvas自适應螢幕寬度!

真實體驗下上述效果

上面口述,你可能不太清楚,我直接給你個案例,看看什麼是極緻使用者體驗:

參考我之前的文章​​《合成大西瓜 重制版 聯機版》​​,我複刻了《合成大西瓜》遊戲,用canvas實作的,它就為了追求極緻使用者體驗,實作了上述2條規則。

體驗位址: ​​game.hullqin.cn/dxg​​

解決方案

使用transform的scale

建議不要改變canvas的​

​width​

​​和​

​height​

​,你可能會把canvas内部的寬高屬性和css寬高搞混!如下圖:這2個紅框裡的屬性,有着不同的作用。

[極緻使用者體驗] 如何實作響應式canvas?保持canvas比例?教你讓canvas自适應螢幕寬度!

我們直接修改​

​transform​

​​的​

​scale​

​屬性,這樣是可以實作等比例縮放的,完全不影響内部布局!

但是修改​

​scale​

​​後,元素看起來變小了,它實際還是占了​

​width​

​​和​

​height​

​​那麼大,還是會出現滾動條。最簡單的解決方法:給它的父元素設定​

​overflow: hidden​

​。

[極緻使用者體驗] 如何實作響應式canvas?保持canvas比例?教你讓canvas自适應螢幕寬度!

設定canvas初始比例,保持在極限值之間

export const Height = 1408; // 要求畫面高度固定,寬度随比例放大、縮小
let width = 704; // 如果螢幕【太寬】,就會用這個寬度
const { innerWidth, innerHeight } = window; // 擷取初始頁面尺寸
if (innerHeight > innerWidth * 1.3) { // 如果初始頁面尺寸不是【太寬】
  width = Height * innerWidth / innerHeight;  // canvas寬高比就跟初始頁面寬高比保持一緻
  if (width < 320) width = 320; // 如果發現螢幕【太窄】,就設定一個極限最小寬度
}
export const Width = width; // 導出計算後的canvas最終寬度      

監聽resize,動态設定canvas比例

// 注:變量canvas是個畫布的dom element,變量root是canvas的父元素。

const resetSize = () => {
  const { innerWidth, innerHeight } = window;
  if (innerWidth / innerHeight > Width / Height) { // 如果放縮後螢幕【寬】,就用螢幕高度計算放縮
    root.style.height = `${innerHeight}px`; // 高度撐滿
    root.style.width = `${innerHeight / Height * Width}px`;
    // 設定新算出來的scale
    canvas.style.transform = `scale(${innerHeight / Height})`;
  } else { // 如果放縮後螢幕【窄】,就用螢幕寬度計算放縮
    root.style.width = `${innerWidth}px`; // 寬度撐滿
    root.style.height = `${innerWidth / Width * Height}px`;
    // 設定新算出來的scale
    canvas.style.transform = `scale(${innerWidth / Width})`;
  }
};

canvas.style.width = `${Width}px`;
canvas.style.height = `${Height}px`;
resetSize(); // 頁面初始時,執行一次,設定canvas的樣式

window.onresize = resetSize; // 監聽resize事件,頁面尺寸變化時,保持寬高比、動态放縮canvas      

寫在最後