电视机顶盒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开发者比较少,先看看可否帮助部分人踩坑吧!

相关文章
相关标签/搜索