炫酷H5中序列图片视频化播放的高性能实现

这篇文章发布于 2018年05月1日,星期二,19:12,归类于 JS实例。 阅读 63839 次, 今日 36 次 44 条评论

 

一、爆款H5中的炫酷场景的技术实现

每年都会迸出一些爆款H5,这些H5通常会有一些酷酷的场景变换。

例如网易Julia H5。创意很棒,传播效果很广,其技术实现就相当简单粗暴,直接一个3分多钟的不带停顿的视频

zhiding

类似使用视频的爆款H5还有很多,确实是一个非常不错的解决方案。

但是,视频的实现也有局限:

  1. iOS下不能自动播放,需要至少touch一次屏幕,这个有时候还挺烦人的,例如我们想做一个H5 app闪屏的时候就蛋疼了。
  2. 不能在中间穿插棒棒的交互效果,例如,需要视频某一帧暂停,鼠标hover或者touch的时候,当前画面有交互效果,就很不好处理。
  3. 播放的速率不能随心所欲控制,视频完成也就定死了。
  4. 如果有些信息是动态的,需要与用户信息关联,则视频方案也会面临很大的调整,因为总不可能每一个用户生成一个不一样的视频,需要辅助额外手段满足需求(例如CSS覆盖定位)。

此时又当如何处理呢?

我们可以使用序列图片,通过JS脚本,来模拟视频播放效果,以上所有局限将通通可以规避。

二、图片列表播放实现方法很多

序列图片视频化技术实现方法很多,例如:

  1. 合在一张大图上,使用CSS3 animation控制background-position实现播放效果。此方法优点是方便快捷,但是,background-position定位性能不怎么样,只适用于小元素小动画,例如一些loading效果,如果是全屏的大图切换,则在移动设备上可以明显感觉到卡顿;
  2. 合在一张大图上,transform定位。性能还是撑不住;
  3. 图片一次性在页面上,依次控制显隐。性能有所提高,但如果图片序列上百帧,图片尺寸较大,性能还是撑不住,客户端说不定会直接闪退。
  4. 页面上一个<img>元素,然后不断改变src地址,不好意思,也不是很快。

总而言之,上面各种方法都是理论上可行,但实践下来的性能总不尽如人意。

那有没有什么高性能的实现方法呢?

有,一种是使用canvas绘图,但如果想要动态插入其他UI丰富的DOM结构(如登录模块),就比较麻烦;还有一种就是下面这个方法,实践下来性能可以,体验比较好,实现成本也不高。

三、图片播放性能提升的实现方法

实现原理如下:

  1. 图片DOM对象预加载,放在内存中;
  2. 播放开始,页面append当前图片DOM,同时移除上一帧DOM图片(如果有),保证页面中仅有一个图片序列元素;

对,很简单,没什么高超的技巧,但就是这种实现策略,对页面的开销是上面几种方法中最小的,最终运行体验是最好的。

眼见为实,您可以狠狠地点击这里:序列图片实现视频播放效果demo

loading完毕,就可以看到一段播放效果——本人飞吻一枚:

张鑫旭的飒爽英姿

效果之流畅,体验之良好,十有八九都会认为是视频,其实不是,就是图片,不断的图片DOM增删实现的类似视频效果。

核心JS代码如下(完整代码见demo),假设container是容器元素,我们的图片已经预加载到store对象中,结构如下:

var store = {
    length: 47,
    1: img1,
    2: img2,
    ...
    47: img47    
};

则有:

var index = 1;
container.innerHTML = '';
// 依次append图片对象
var step = function () {
    if (store[index - 1]) {
        container.removeChild(store[index - 1]);
    }    
    container.appendChild(store[index]);
    // 序列增加
    index++;
    // 如果不超过最大限制,播放下一帧
    if (index <= 47) {
        // 42是按照每秒24帧计算的值
        setTimeout(step, 42);
    }
};
step();

上面代码红色高亮是实现的关键,container.removeChild(store[index - 1])移除之前一帧图片DOM,container.append(store[index])则是插入当前一帧DOM,人的肉眼习惯连续性感知事物,因此,这种删除和添加,用户是无感知的,于是一个流程的播放效果即达成,根据实践,就算每帧图片在几百K大小主流设备也能hold住。

由于本质上播放的是DOM对象,因此,我们不仅可以播放图片DOM,还可以是有着丰富HTML结构的<div>元素,于是,什么样的交互实现都不在话下,比方说视频中要出现用户的姓名,怎么办,很简单啊,<div>元素中定位下就好了。

现在,技术实现已经对设计没有任何限制啦,剩下的就是产品和设计的创意,下一个爆款H5就是你了!

三、番外技能:如何把视频变成序列图片?

如果贵厂有设计师,让设计师处理,基本上,AE之类软件应该都了解。

如果没有设计师,要前端小伙伴自己处理,怎么办?

拿本文的序列图片举例,我是这么处理的:

  1. 手机视频QQ传到自己电脑;
  2. 打开Photoshop,然后:文件-导入-视频帧到图层,会出现一个窗口,可以剪切需要导入的视频范围;

    Photoshop中导入视频导入范围剪辑

    如果导入视频很长,建议先剪辑,再导入,除非你的电脑是iMac Pro这种级别的。

  3. 不做任何处理,直接:“文件-脚本-将图层导出到文件”;
  4. node.js小工具批量重命名了下(导出图片名称后面序号是准确序号),工具使用的JavaScript代码如下:
    var fs = require("fs");
    fs.readdir('./', function(err, files) {
        files.forEach(function(filename) {
            if (/jpg$/.test(filename) == false) { return; }
            // 确定新旧文件名称
            var oldPath = './' + filename, newPath = './' + filename.split(' ')[1];
            // 重命名走起
            fs.rename(oldPath, newPath, function() {
                console.log(filename + '重命名成功!');
            })
        });
    })

四、关于性能其他需要注意的

人眼的跟踪能力要比大猩猩之类要弱的,因此,实际开发,并不一定需要每秒24帧的播放速率,你每秒18帧,对于一个H5运营活动而言,用户是无感知的。每秒18帧的播放可以节约不少请求和加载数据量,性能上也能有所提高,权衡来看,是推荐的,毕竟我们不是去参加动画比赛,是一个在线的web产品。

设计师喜欢使用非常高清的图片,实际上,没有必要,注意度,2倍尺寸,30%~40%的图片质量足够了,效果也非常好,这也是经过实践的,大家如果和设计师意见不一致,就可以让她看我写的这段话。有效降低不必要的图片尺寸,可以大大节约内存的开销,也是可以提高播放的性能和品质的。

于是,三管齐下:高性能技术实现策略,适当降低帧率,优化图片尺寸,必定助你H5炫酷效果流畅至极,好评如潮,boss交口称赞!

以上就是本文内容,感谢阅读,如果文中有表述不准确的地方,欢迎指正!

(本篇完)

分享到:


发表评论(目前44 条评论)

  1. aosika说道:

    图片序列预加载的逻辑单独开启一个线程(比如:web worker)去加载是不是更快

  2. kira说道:

    大佬你好 探讨几个问题
    1.性能问题:
    CSS3和canvas吊打通过img标签的方式(实测),为啥说这种方式 好?CSS3确实只适合做小范围动画
    2.体积问题 视频弄成帧 体积大了很多

  3. 小恒说道:

    旭神您好,这个方法有一些问题,IE浏览器及火狐浏览器加载时会闪烁很久,这个问题应该怎样解决

  4. 对于摄像头的实时视频播放说道:

    在pc网页中,需要做到摄像头的实时视频播放。我太难了

  5. Twinkleee说道:

    如果是一个10几M的视频,图片需要先预加载吗,预加载多少张比较合适

  6. 滴滴说道:

    利用这个方法,15s以上的视频播放,不能正常显示

  7. xoyimi说道:

    赞!

  8. sweet-kk说道:

    大神,加载进度条bgsize的改变看不到效果呀

  9. bobby说道:

    可以看看我写的这个,完全满足上面需求
    backgroundPosition渲染
    img标签渲染
    backgroundImage渲染

    https://github.com/bobby169/cssSprite

    • Harry说道:

      你好~我已经在使用你的cssSprite,我有个疑问想咨询一下:请问如果我使用background-position渲染时,该如何适配宽高呢?好像frame数组里只能填写固定的px值,无法随着视窗高度更改呢?

  10. 课程说道:

    请问声音怎么处理呢,

  11. 大东说道:

    作为一个小白,想请教一下大神,视频短的可以用gif(2-3s),长的广告图片又有太多(比如15s),这个方法的适用场景有哪些,优势在哪。

  12. xxx说道:

    示例页面在IE下报错。

    SCRIPT438: 对象不支持“append”属性或方法

    文件: image-sequence-frame-play.html,行: 221,列: 4

    if (store[index – 1]) {
    eleContainer.removeChild(store[index – 1]);
    }
    //HERE!!!
    eleContainer.append(store[index]);
    // 序列增加
    index++;

  13. linkstar说道:

    序列图片实现视频播放效果demo
    这个点开都是undefined

  14. cocody说道:

    张大神可以详细说说为什么增删 DOM 元素比切换 src 更快么?

    • Twinkleee说道:

      因为元素已经在内存里面生成好了 而且用过img.onload方法将图片加载好了,如果是改变src的话是在修改的时候才开始下载图片,必然会有闪屏的感觉

  15. 读心说道:

    挺好玩的呢!!!!!

  16. nicholasurey说道:

    思路很棒

  17. 滴滴答答嘟说道:

    julia 为什么我先想到了一个女演员

  18. Plus.Zhang说道:

    对于有声音的视频来说似乎方案不是那么可行啊

  19. 沉江、小鱼说道:

    拜读了大神的《CSS世界》,受益匪浅,写得很好,真的,糅合了自己的思考和想法,这是最难能可贵的,读书就像和作者交流,就是要把自己的想法和个性表达出来,写出来,不拘泥于规范和冷冰冰的教程,思想才能碰撞出火花嘛

  20. firstBlood说道:

    我一般是用canvas, 很多逻辑都会写到render里面, 主要是参考的threejs里面的一些做法吧

  21. DH说道:

    相见恨晚,感谢提供思路

  22. 码农说道:

    后面的经验总结很赞!节省调试时间,避免了踩坑!

  23. zhenhappy说道:

    感谢旭哥提供思路, 我简单封装了个播放器, 献丑了
    https://github.com/zhenhappy/images-player

  24. 很多时候都会遇到这些问题,学习了

  25. Tong说道:

    这和上面的第3、4种方法其实差别不大,性能上更好可能只是你的感觉

  26. magicbing说道:

    看云风博客说做梦幻系列时候, 鸣谢名单就是按帧播放图片做的视频效果

  27. 小媒体说道:

    思路不错,如下功能如果有就更好了:
    1. 边下载边播放
    2. 快进快退
    3. 声音,关键和快进快退要同步

    不过这三个问题,业务开发者可以自己去实现。

    • riophae说道:

      1. 边下载边播放也可以做,主要是担心播到一半突然卡住,体验会很不好,倒不如预先都加载完再播
      2. 和 3. 都能做

      • 小媒体说道:

        小视频 网速好 完全可以全部加载完再播放,反之 如果没有边下边播,体验会更差。
        3 具体怎么做,说下思路。

  28. lmfyeee说道:

    学习了学习了,前俩天刚好了解到这种原理。今天就刚好看到鑫哥已经写出来了

  29. lucas_chuan说道:

    突如其来的吻让我猝不及防惹

  30. trance说道:

    视频看笑了 代码学习了 效果达到了

  31. twoer说道:

    为啥不考虑直接用 canvas ?

  32. 老白狼说道:

    绝对学习了了,谢谢