電視機頂盒WEB開發-前端經驗分享

前言

此篇文章不只是爲講解在電視機頂盒WEB環境開發的經驗,也包含對於簡化後瀏覽器內核的開發環境與低性能設備踩坑。css

由於發現目前針對於機頂盒的前端開發分享很是之少,質量也廣泛不高,在此作記錄分享,文章中所用技術皆爲前端技術,不包含Andoird等其餘語言,可放心食用。前端


項目運行環境

因爲市面機頂盒生產廠商不少,其中表現基本都有區別,也有內置機頂盒功能的網絡電視,因此在硬件不一樣的基礎上,採用統一的軟件服務環境開發是頗有必要的。react

本人開發項目所項目運行環境是Android部門基於Chrome內核的某個版本作技術支持而打包的環境(其中拋棄了某些特性 好比不支持視頻音頻Cookie等),但有的盒子已經在市場上使用了幾年,性能很低,使用的也是更低版本的內核,致使開發上會有一些阻礙,因此若您的環境也有所差別,技術上有疑惑,可留言告訴我。android


框架與插件選擇

關於框架

目前市面上開發團隊用的比較多的框架無非是 React、Vue、Angular,但在此不推薦用這些東西,而是使用jQuery做爲開發的基礎支持,插件等效果本身開發實現——爲了性能與加載速度。由於這些框架的插件在電腦上運行是沒有壓力的,但若在機頂盒上,其實能夠再優化一些的,與其優化別人的代碼不如本身寫一個 ~ 若是你webpack學得不錯,能夠搭建一個以jQuery爲核心的打包環境,優化加載方式與速度,那就更好了。webpack

選擇它的緣由是什麼:

1.遙控器每次點擊後跳轉一次新的頁面,都會重新請求其餘頁面的數據,在頁面onload以前,android爲了避免讓用戶看到閃白,是以黑屏狀態顯示頁面的,當頁面加載完成後觸發onload,纔會展示頁面。如若使用框架三巨頭來作,頁面加載性能會慢那麼一點點,固然,能夠經過prefetch作優化,但這不是主要緣由,請往下看。css3

2.簡化後的瀏覽器內核對history對象的支持不友好,若是使用react-router,會致使後退出現bug。git

3.公司業務開發速度的考量,jQuery較爲方便敏捷,不要喜新厭舊,實用纔是最重要的。github

關於插件

一行文字放不下怎麼辦?動畫循環滾動之 文字單行跑馬燈 (做者對於CSS動畫兼容性沒有作適配,須要修改JS源碼,加入CSS前綴的生成)web

這裏可分享的暫此一個插件,由於目前對於盒子上能用到的插件推薦很少,我注重性能,喜歡寫針對於盒子性能優化後的代碼邏輯,在盡力的狀況下不浪費一絲絲內存(貧窮令人拮据)算法

好比一定會用到的滾動動畫插件,市面上的例如swpie.js已經很成熟了,也很流暢,但對於盒子環境而言仍是有些慢,還能夠作二次優化,因此我基本習慣本身去寫,但插件的源碼沒法放出,我會在下面章節的 「功能實現分析」 中寫出大概思路。


常見需求 & 功能實現思路 & 優化思路

根據工做上的業務需求,我會羅列出產品常常想要的功能,並分析相關的思路。

  1. 動畫

    這能夠說是項目中一定出現的需求,先說下視覺上動畫的實現方式

    • loading:採用多幀圖 配合 CSS的animation循環實現,而不是Gif,由於我發現Gif在加載初期的動畫效果會因JS腳本執行變得卡頓,而CSS3不會,而且GIF在出現透明動畫時的表現很是糟糕,邊緣會有雜色,固然能夠在導出GIF時選擇和背景色對應的雜色來達到欺騙視覺的效果,可若頁面背景色是未知的呢? 而CSS3配合PNG的多幀動畫很好的避免這個問題↓

    • 列表滾動:採用 transition 配合 transform: translateZ(0) 激活GPU動畫加速 再配合 will-change 屬性作動畫預加速,will-change 平時開發用的比較少,感興趣能夠了解下,也許你的環境會不支持,可是寫上去吧,指不定將來硬件設備升級,動畫會由於你而更流暢(但不要在同一個頁面大量使用,也會影響性能)

    注:毫不要使用jQuery的animate


    接下來是腳本上的列表滾動處理,就相對複雜些了,此處分析橫軸列表滾動

    如:

    • 固定寬度滾動:

      子元素寬度固定已知,只須要知道當前光標在第幾個元素,帶入index到固定的算法,便可獲取滾動距離,較爲簡單

    • 未知寬度滾動:

      元素寬度、個數、排序規則未知,可能有十幾種不一樣的寬度,排序的方式是後臺定義,若想從右向左滾動下一屏,滾動時機是什麼?怎樣的時機纔是好的體驗?

      如何計算?

      1.如非必要,請避免動態獲取參數,已知參數寫入DOM標籤或DOM內存,不要去動態獲取元素的寬/高/邊距/距離父級的位置,這對於盒子而言是很耗性能的事情,這些東西能夠放在初始化列表的時候輪詢計算。

      好比有一個列表寬度未知,可能知足滾動五屏的列表,列表還沒有滾動,left值則爲0

      子元素可能有50列,那麼第一列 col-01 寬100px 邊距18px 第二列 col-02 寬130 邊距 = 第一列邊距+第一列寬+第二列邊距

      實際代碼:

    <div class="listBox"><!--固定容器寬高 好比寬100%高100% 隨窗口大小固定-->
        <div class="list" data-width="266" data-left="0"><!--須要滾動的列表容器 寬度隨內部元素寬度改變-->
            <a class="col-01" data-width="100" data-left="18">第一列</a><!--left = 自身邊距 + 前面元素寬度+邊距 沒有則爲0-->
            <a class="col-02" data-width="130" data-left="136">第二列</a><!--left = 自身邊距+前面元素寬度+邊距-->
        </div>
    </div>
    複製代碼

    這樣想要計算元素的位置,直接讀取DOM屬性便可,不然每次動畫都要讀取元素寬、距離父級的寬、父級滾動距離、父級的寬等一些列動態計算的東西,觸發重繪機制,很是耗費性能,帶來交互上的卡頓,模擬按右鍵↓

    ↑此處能夠看到 滾動至第二屏的時候我專門讓左側留了一些像素,方便按左鍵時 盒子能檢測到左側有焦點

    由於遙控器的上下左右按鍵,盒子是檢測元素周圍是否存在A標籤進行移動的,因此若是恰巧滾動到右側的寬度正好致使看不到第二頁,那麼就觸發不到滾動,因此還要考慮一下邊界的判斷,簡單比喻下就是

    if( 滾動列表容器寬 > 父級固定容器寬 && 焦點元素寬+焦點元素left+下一個元素的邊距好比18 > 當前視口的寬){
            //存在第二頁 && 當前元素焦點看不到下一個元素 
            則在此處使列表稍微多滾動幾像素 露出至少1像素的邊緣 使盒子能夠檢測到右側有焦點能夠移動
        }
    複製代碼

    (實際要複雜一些 多不少判斷 好比是否可直接翻頁、是否翻頁到最後一頁、還能翻頁但盒子檢測不到下個元素)

    這樣算下去明顯很複雜,那麼有沒有簡單點的方法呢 ?

    將他們加載的時候都存入內存的數組中,監聽用戶按鍵並阻止默認行爲,計算當前焦點在數組的第幾個,用戶按右鍵就向後移動一次,查詢頁面上對應的DOM,再計算他將要去的位置便可,這樣獲取焦點的任務就交給了咱們本身,而不是機頂盒,可控性上升,未知性下降,但不變的是咱們仍是要預先存入DOM的data-width,data-left等值,爲何不存入內存中而寫入標籤?也能夠存,好比jQuery獲取DOM後的 $(dom).data("data-left",500),但產品這時候又提出了一個功能:我要後退時還原前一個頁面的狀態~

    插入小提示: focus到顯示器焦點外時會觸發盒子默認的scroll機制,致使box的scrollLeft自動改變(即使你已經overflow:hidden),因此每次focus後須要馬上把父元素、body的scrollLeft設爲0,這個問題我排查了小半天才發現,須要注意!

  2. 還原頁面狀態

    這個功能講起來就是:簡單的頁面就簡單,複雜的頁面就很麻煩

    但一般都是經過 storage實現的,那咱們就先講它


    簡單粗暴的方式

    其實產品想要的就是跳走前什麼樣,回來就什麼樣而已

    若是是一個簡單的頁面(沒有滾動,元素就那麼幾個)

    直接在跳走前點擊A標籤的時候,以當前頁面的url爲鍵值

    focusJson對象 寫入跳走前點擊A標籤它的ClassName、Href地址、Index值,返回此頁時根據這三個值匹配到後,得知跳走前的元素是哪兒個

    const seStorage = window.sessionStorage;
            const pageUrl = window.location.href.split("?")[0];
            const QUOTA_EXCEEDED_ERR_CODE = 22;
            
            $(a).click(function(){
                 const focusJson = {
                    className : $(this).attr("class"),
                    href : $(this).attr("href"),
                    index: $(this).index()
                }
                try {
                    seStorage.setItem(pageUrl, focusJson);
                } catch (e) {
                    //防止存不下報錯 理應不會出現 由於咱們要在頁面按返回,或初次進入頁面的時候清除掉當前頁Storage 防止溢出
                    //但仍是避免下用戶連續進超多頁面 不按返回的狀況
                    if (e.code === QUOTA_EXCEEDED_ERR_CODE) {
                        seStorage.clear();
                        seStorage.setItem(pageUrl, focusJson);
                    }
                }
            })
            
            ...
            
            $(function(){
                //頁面初始化時查詢是否存有storage 如有則尋找對應className 過濾出正確的元素還原焦點狀態 並清除本次storage
            })
    複製代碼

    動態數據頁面也能夠簡單粗暴

    產品開發項目時間緊,不夠你考慮優化的時候採用此方案,時間充裕不建議這樣,由於不優雅

    那就是直接CopyDOM節點轉字符串存入storage,回到此頁直接扔到頁面裏去 通常焦點都會有全局惟一的樣式名作焦點效果,直接Focus此樣式元素,就完整的還原了跳走前的狀態

    但須要注意如下幾點:

    1.頁碼記錄,跳走前把動態數據已經請求的頁碼也存入focusJson,還原的時候載入這個頁碼,這樣往下翻頁數據才正確

    2.若是這個動態數據頁面存在滾動列表,且滾動了不少屏,如何還原?

    若是採用上面在標籤中寫入data-left,data-width的方式,實際上還原後直接將這些參數做爲滾動插件初始化時的初始值便可,綁定到滾動插件就能夠正常使用了,只寫大概思路,實現具體方式須要本身研究哈,感受並不複雜
    複製代碼

    3.DOM數據量太大,存入會致使Storage溢出

    沒錯 這就是簡單粗暴的缺點,大量的data-left,data-width,DOM標籤都存入在storage,數據量大的狀況下確實會溢出,須要考慮項目使用場景,數據量的大小
    複製代碼

    符合編程思惟的頁面還原

    對於前端而言,能在內存中處理的就不要放在DOM上,靈活的操做數據纔是王道,好比React的虛擬DOM與Diff算法,咱們能夠在初始化頁面列表的時候就將這些元素的left,width等數據存入對象中,轉存入storage,在頁面初始化的時候讀取storage像Ajax請求JSON同樣解析它來還原列表,只記錄關鍵的屬性值,而不用像上面簡單粗暴的方式把那些根本不須要記錄的dom標籤、class、id等不屬於數據的垃圾字節存起來佔地方,能夠很大的節省storage的存儲空間,若是你的項目時間充裕建議以此方案進行開發,不要將就。

  3. 不走尋常路的頁面還原方案

    很不幸的是:並非全部瀏覽器環境都支持storage,有的環境支持sessionStorage 有的環境sessionStorage和localStorage都支持,而有的環境他們都不支持 …… 那咱們就沒辦法了嗎?

    • input type="text"標籤實現記錄

      使用此標籤須要注意的問題:

      1.不能使用type="hidden" 它記錄不到的,返回後數據會消失

      2.不能使用css的display:none 也會致使返回數據丟失

      那麼讓它這麼佔着位置也難看,能夠有兩種方法解決:

      1.positoin:absolute; 哪兒遠讓它去哪兒

      2.visibility:hidden; 佔位但隱藏,與display:none不一樣的是它不會使數據丟失


其餘需求…… 先寫這些吧,還有其餘的分析,不過有沒有人看還不知道 看狀況再寫,先跨過此部分


性能優化

因爲頁面都是A標籤元素的跳轉,咱們能夠經過監聽focus事件得知本次焦點的是哪兒個元素,給它一個焦點樣式,並取消上一個焦點樣式 例如

$(document).on("foocus","a",function(){
    $(".itemFocus").removeClass("itemFocus");
    $(this).addClass("itemFocus"); 
});
複製代碼

但這樣就會有一個問題 若是你的頁面是動態加載、翻頁,頁面有100個A標籤,你每次Focus都要查詢這些標籤是否存在itemFocus

假若還要計算元素的滾動等,那對於用戶而言,操做時明顯會有響應慢的問題,那麼咱們能夠簡單優化下:

let $focus = null;
$(document).on("foocus","a",function(){
    $focus === null? $(".itemFocus").removeClass("itemFocus") : $focus.removeClass("itemFocus");
    $focus = $(this).addClass("itemFocus"); 
});
複製代碼

這樣每次都把以前的焦點元素存入了內存,下次就不須要輪詢查找.itemFocus,效率獲得了顯著的提高


最後

先寫到這裏吧,原本想寫的不少,但發現寫起來是真的累,深感倒不如學些東西去

盒子WEB開發者比較少,先看看可否幫助部分人踩坑吧!

相關文章
相關標籤/搜索