关闭

如何实现沉浸式视频体验?

时间: 2019-03-26阅读: 2543标签: 视频

沉浸式视频体验

上周产品小哥哥丢过来一个需求,名曰:沉浸式视频体验,大致内容是一个页面里有几十个视频,用户点击其中一个视频时,该视频自动滑动到屏幕可视区域的顶部开始播放,并暂停其他视频,该视频滑出屏幕可视区域之后要自动暂停。

这个需求有两个关键的技术点:

  1. 如何将视频滑动到屏幕可视区域的顶部
  2. 如何判断视频滑出了屏幕可视区域

其实这两个技术点有一个共同点,就是需求计算出元素在页面中的绝对位置,也就是指该元素的左上角相对于整张网页左上角的坐标,有两种方法可以计算得到:


1.递归

利用offsetTopoffsetLeft可以取到当前元素左上角相对于其htmlElement.offsetParent节点的左边界偏移的像素值,然后再利用HTMLElement.offsetParent
可以得到一个指向最近的包含该元素的定位元素。

如果没有定位的元素,则offsetParent为最近的table,table cell或根元素(标准模式下为html;quirks模式下为body)。

利用以上三个属性,写一个递归函数就可以得到当前元素在页面中的绝对位置了:

const getElementLeft = element => {
    let actualLeft = element.offsetLeft;
    let current = element.offsetParent;
    while (current !== null){
        actualLeft += current.offsetLeft;
        current = current.offsetParent;
    }
    return actualLeft;
}
const getElementTop = element => {
    let actualTop = element.offsetTop;
    let current = element.offsetParent;
    while (current !== null){
        actualTop += current.offsetTop;
        current = current.offsetParent;
    }
    return actualTop;
}

注意:由于在表格和iframe中,offsetParent对象未必等于父容器,所以上面的函数对于表格和iframe中的元素不适用。


2. 你听说过getBoundingClientRect吗?

object.getBoundingClientRect()的返回值包含了一组只读属性,包括该元素相对于视口左上角位置的left、top、right和bottom,以及元素的width和height,单位为像素,具体含义可参见下图:

从上图可以看出,getBoundingClientRect得到的值是相对于视口的,而不是绝对的,当视口区域或其他可滚动元素内发生滚动操作时,top和left属性值就会随之立即发生变化。

要获得相对于整个网页左上角定位的属性值,只要给top、left属性值加上当前的滚动位置(通过window.scrollX和window.scrollY),这样就可以获取与当前的滚动位置无关的值。


如何将视频滑动到屏幕可视区域的顶部?

考虑到getBoundingClientRect的兼容性较好,且算法复杂度较低,最终我采用了getBoundingClientRect方法来实现“将视频滑动到屏幕可视区域的顶部”的功能,使用window.scroll来实现页面滚动,使用setTimeout增加滚动时的动画,具体实现滚动的函数如下:

const autoScroll = (offsetTop, needScrollTop, hasScrollTop) => {
    let _needScrollTop = needScrollTop; // 本次递归时,离终点的距离
    let _hasScrollTop = hasScrollTop; // 本次递归时,已经移动的距离总和
    const speed = 10;
    setTimeout(() => {
        const dist = needScrollTop > 0
            ? Math.max(Math.ceil(needScrollTop / speed), 5)
            : Math.min(Math.ceil(needScrollTop / speed), -5);
        _needScrollTop -= dist;
        _hasScrollTop += dist;
        window.scroll(0, offsetTop + _hasScrollTop);
        // 如果移动幅度小于十个像素,直接移动,否则递归调用,实现动画效果
        if (_needScrollTop > speed || _needScrollTop < -speed) {
            this.__onScroll(offsetTop, _needScrollTop, _hasScrollTop);
        } else {
            window.scroll(0, offsetTop + _hasScrollTop + _needScrollTop);
        }
    }, 1);
}

const rect = element.getBoundingClientRect();

const offsetTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || window.screenY;

autoScroll(offsetTop, rect.top, 0);


如何判断视频滑出了屏幕可视区域?

利用getBoundingClientRect的返回值同样可以判断元素在屏幕可视区域的曝光和滑出,通过监听页面的滚动事件,在滚动结束时,触发checkLeave和checkExpose函数,具体代码如下:

// 检测滑出可视区域
checkLeave() {
    const element = document.querySelector(`[data-action-id="${this.__domId}"]`);
    if (!element) {
        console.error(`Action: element [data-action-id="${this.__domId}"] not found`);
        return;
    }
    const { top, bottom, left, right } = element.getBoundingClientRect();
    if ((top > getWindowHeight() || bottom < 0
        || left > getWindowWidth() || right < 0)) {
        this.onLeave(); //onLeave函数中实现具体业务逻辑
    }
}

// 检测真实曝光
checkExpose() {
    const element = document.querySelector(`[data-action-id="${this.__domId}"]`);
    if (!element) {
        console.error(`Action: element [data-action-id="${this.__domId}"] not found`);
        return;
    }
    const { top, bottom, left, right } = element.getBoundingClientRect();
    if (Math.max(0, top) <= Math.min(getWindowHeight(), bottom)
        && Math.max(0, left) <= Math.min(getWindowWidth(), right)) {
        this.onExpose(); //onExpose函数中实现具体业务逻辑
    }
}

可以将这两个事件封装成了一个<Action>组件的两个props参数,使用时只需要在需要的元素外包一层<Action>父元素,并传入特定的回调函数即可。

BTW,多说一句我实现document.querySelector的原理,直接看代码:

render() {
    const { children } = this.props;
    return cloneElement(Children.only(children), {
        'data-action-id': this.__domId,
    });
}

以上。

作者:TNFE 二小

来自:https://segmentfault.com/a/1190000018657822


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/2545

关闭

video.js切换视频源问题

什么要再重新load一下什么替换一下src,没有啥作用,简单粗暴又有效的方法,video的问题,单单替换source的src是不行的,简单明了重新替换整个video标签,将video用js重新替换一遍,更改src

js实现截取视频帧图片作为封面预览图

前端需要把视频文件的第一帧图像截取出来,并做为缩略图显示在页面上,这里需要利用HTML5中强大的画布canvas来实现该功能

html网页添加音乐视频方式

声音无法自动播放这个在 IOS/Android 上面一直是个惯例,桌面版的 Safari 在 2017 年的 11 版本也宣布禁掉带有声音的多媒体自动播放功能,紧接着在 2018 年 4 月份发布的 Chrome 66 也正式关掉了声音自动播放

聊聊 Web 视频播放的画中画模式

Chrome 在升级到 70 的时候开始对视频播放的画中画模式开始支持了,用户在浏览器 Web 页面可以通过 画中画的形式强当前画面停留在屏幕正上方,无论你在使用其他软件还是切到其他 Tab ,都可以看到当前画面

h5视频播放踩坑记录

随着抖音、快手这类的视频类app的火爆,移动端h5视频类应用也随之兴起,使用video播放的场景也越来越多,本篇文章主要例举了移动端视频播放的一些场景和个人在开发过程中遇到的一些问题

使用 multipart/x-mixed-replace 实现 http 实时视频流

关于实时视频传输,业界已经有非常多成熟方案,分别应用在不同需求场景。本文介绍一种基于 HTTP ,非常简单、易理解的方案,实用性不强,但有助于理解 HTTP 协议。

视频全屏弹幕实现

这个问题如果思路错了就是个死胡同。所以我在找问题的时候趟了很多坑。思路错在我不应该使用视频的全屏,敲黑板!划重点啦!不要使用视频的全屏!(解决了问题的时候才幡然醒悟)

Web视频格式

网络视频一直都很火。虽然在页面上嵌入 Instagram 和 Youtube 视频非常简单,但是有越来越多的需求 —— 比如许多电子商务的场景 —— 要求定制的视频传输方法。

vue实现自定义H5视频播放器

前段时间基于vue写了一个自定义的video播放器组件,踩了一些小坑, 这里做一下复盘分享出来,避免日后重复踩坑,这里就直接放几张完成后的播放状态图吧,界面布局基本就是flex+vw适配一把梭

移动端视频h5表现问题汇总

同屏播放视频、移动端视频预加载:由于移动端不能预加载视频,所以hack一种方案:监听WXJSBridge WeixinJSBridgeReady、微信安卓环境下需要在touchmove事件中阻止掉默认事件,否则不能触发视频播放 、 由于微信安卓版本基于x5内核,视频会出现全屏按钮,而且去不掉,会误导用户点击

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!