1. 前言
这篇是下面这篇文章的续章。因为上一篇文章篇幅过长,所以我强行让大家伙们休息一下,贴心吧?上一篇文章的链接在下面了哈~
从零制作视频播放器------别人能搞得出来视频控件,难道我们不能搞?(第三章)
上一篇文章我们讲到了 js 实现渐现、渐隐对吧?前面我们介绍的几种方法都实现不了,所以只能靠 js 来实现了。另外各位看客的点赞与收藏就是我源源不断的动力,有兴趣的也可以关注一下该专栏,如果有合适的功能需求,专栏也会进行相应的更新。为了大家,奥利给~ヾ(≧▽≦*)o
2. 实现渐现、渐隐
废话不多说,我们下面将用 js 来实现元素的渐现和渐隐,具体的效果如下图所示:
下面我将为大家讲解具体的思路,因为需求是不断变化的,我们得提高自身的编程水平,以变化适应变化,这个世界唯一不变的就是万物都是变化的。
仔细观察上面图片的效果,我也画了一张简图来理解,我们大概能得出初步的结论:
- 鼠标移入触发元素,面板元素显示,移出则隐藏。
- 鼠标移入、移出触发元素或者是面板元素的效果,与只移入、移出触发元素的效果相同。
- 鼠标移入后移出,然后再快速移入时,透明度不会发生跳跃变化。
好了,初步的剖析就是上面这样,下面我们就开始发车了,老司机上路!
通常我们将渐现函数命名为 fadeIn
,渐隐函数命名为 fadeOut
。先写渐现,再写渐隐。
注意,下面的实现为了让大家好理解一些,删减了一些内容。所以如果想看完整的代码可以点击下面的链接。下面是一个效果预览地址。
2.1. 渐现函数实现
初步分析如果要实现 fadeIn 我们至少需要两个参数:要渐现的元素,过渡时间。
js
function fadeIn(element, duration) {
let computedStyle = window.getComputedStyle(element);
// 首先得让元素在渲染树中,确保 display 不为 none
if(computedStyle.display === "none") {
// display 可能有其他的取值, 如 flex
element.style.display = "";
element.style.opacity = "0";
element.style.transition = `opacity ${duration}ms ease-in 0s`;
// 回流、重绘,我们需要最新的样式
element.clientHeight;
computedStyle = window.getComputedStyle(element);
// 如果还为 none,只能上大招了,加上 block
if(computedStyle.display === "none") {
element.style.display = "block";
element.clientHeight;
}
} else {
// 让 opacity 不发生跳跃变化, 不一定从 0 开始变为 1
element.style.opacity = computedStyle.opacity;
}
// 变化至 1
element.style.opacity = "1";
// 过度结束后的操作,悄悄地我走了,正如我悄悄地来,我挥一挥衣袖,不带走一片云彩
let transitionEnd = () => {
element.style.transition = "";
element.style.opacity = "";
removeEvent();
};
let removeEvent = () => {
element.removeEventListener("transitionend", transitionEnd);
};
element.addEventListener("transitionend", transitionEnd);
}
代码应该也不算很难,就是有些细节不容易考虑到。是不是以为到这就结束了?如果点击了上面的链接的朋友应该知道这段代码不是最终的代码。上面代码有一个 bug,之后会给补上,具体的原因还得先把 fadeOut 的函数给写完才能说得清楚。
2.2. 渐隐函数的实现
相比渐现,渐隐不需要考虑太多, 因为元素必定显示, 这里不考虑元素的 display 含有 !important 的情况。
js
function fadeOut(element, duration) {
let computedStyle = window.getComputedStyle(element);
element.style.transition = `opacity ${duration}ms ease-in 0s`;
// 防止透明度突然的变化
element.style.opacity = computedStyle.opacity;
element.clientHeight;
element.style.opacity = "0";
let transitionEnd = () => {
element.style.display = "none";
element.style.transition = "";
element.style.opacity = "";
removeEvent();
};
let removeEvent = () => {
element.removeEventListener("transitionend", transitionEnd);
};
element.addEventListener("transitionend", transitionEnd);
});
}
2.3. 分析 bug
最苦恼的莫过于辛苦写了好久,出来一个未知的 bug,能解决还好,不能解决的话那不得 茶不思,饭不想,有女也肌无力😂
有没有拿着上面的代码试过的?算了,还是我来吧!
js
// board 是 container 的子元素
const container = document.querySelector(".container");
const board = document.querySelector(".board");
container.addEventListener("mouseenter", () => {
window.fadeIn(board, 1000);
});
container.addEventListener("mouseleave", () => {
window.fadeOut(board, 1000);
});
运行上面的代码我们能发现,当鼠标移入 container 时,在 board 还在过渡阶段快速将鼠标移入 board 时,board 会消失,即 display: none; 。
原因也很简单,就是鼠标移出时,由于 board 还在过渡,而 fadeOut 函数也在 board 上加了一个 transitionend 事件,使得 display 为 none。
解决方法也很简单,由于鼠标移出了 container 元素,然后又移入了 container 元素,所以我们可以在鼠标移入时,移除鼠标移出时添加的事件。
最终的代码就是大概下面这个样子,当然有些地方还是有点不同的,不过不影响理解。这里为了防止文章篇幅过长,就弄了个掘金的代码空间给大家查看。
3. 整合
不知大家阅读文章的感觉如何,有没有忘记我们最初的目的是为了什么?没有被我给搞晕吧,哈哈~ 要是给大家弄晕了,那真是罪过。
我们要实现的是下面的这个效果哈,就是控件面板的渐现和渐隐,而已,对吧?
相信和我一起走到这的小伙伴已经能将这个简单的效果实现了吧?
不知小伙伴们怎么看我给大家弄得这些分析,提供的这些思路,可能会有小伙伴觉得这会很啰嗦。其实我也不想写这么多,我只是不想随便敷衍大家,大家也不喜欢看没有深度的文章对吧?随便写的文章既敷衍了你们,又浪费了我的时间,所以我才尽可能地将好的内容呈现给大家。另外这也是正常的思路,为了实现一个功能,我们既要实现,也要思考有没有更好的解决办法,尝试各种奇怪的实现方式,力求完美,无可挑剔。
好了,前面我们说了实现该功能需要考虑三个方面:限流
,执行回调
,渐现、渐隐
。目前都已经解决了。
下面开始整合!(≧∇≦)ノ
注意哈,这里的代码只是成功的一个雏形,因为还有其他的要考虑,比如视频暂停时还要不要隐藏控件。
html
<!-- css 样式已省略 -->
<div class="video-container">
<div class="video-instance">
<video src="./spy.mp4" controls></video>
</div>
<div class="video-controls"></div>
</div>
<script>
"use strict";
// 节流函数、fadeIn、fadeOut 函数已省略
const VIDEO_CONTROL_DURATION = 500;
const videoContainer = document.querySelector(".video-container");
const videoControls = document.querySelector(".video-controls");
/**
* 显示控件后执行的回调
* @type {[function]}
*/
const showControlsCallbackArr = [];
/**
* 隐藏控件后执行的回调
* @type {[function]}
*/
const hideControlsCallbackArr = [];
/**
* 控件是否显示
* @type {boolean}
*/
let controlsShow;
/**
* 显示节流, 节流就是在指定的时间内执行一次
* @type {function(...[*]): *}
*/
let showControlsThrottle = window.getThrottle(window.showControls, 100);
let timer;
/**
* 直接显示控件, 并定时隐藏
*/
function showControls() {
window.showControlsNotScheduleHide();
if (!pause) {
window.scheduleHiddenControls();
}
}
/**
* 直接隐藏控件
*/
function hiddenControls() {
if (!enterControls) {
window.fadeOut(videoControls, VIDEO_CONTROL_DURATION, videoContainer);
controlsShow = false;
window.execCallback(hideControlsCallbackArr);
}
}
/**
* 定时隐藏控件
*/
function scheduleHiddenControls() {
if (enterControls) {
return;
}
// 这里我们后面可以用防抖来处理
timer = window.setTimeout(window.hiddenControls, 2000);
}
/**
* 鼠标在 videoContainer 元素中持续移动
*/
function mousemoveInVideoContainer() {
// 节流
showControlsThrottle();
}
/**
* 鼠标移入视频控件时触发, 清除隐藏控件的定时器
*/
function mouseenterVideoControls() {
enterControls = true;
window.clearTimeout(timer);
}
/**
* 鼠标移出视频控件时触发, 设置隐藏控件的定时器
*/
function mouseleaveVideoControls() {
enterControls = false;
window.scheduleHiddenControls();
}
videoContainer.addEventListener("mousemove", window.mousemoveInVideoContainer);
videoControls.addEventListener("mouseenter", window.mouseenterVideoControls);
videoControls.addEventListener("mouseleave", window.mouseleaveVideoControls);
</script>
下面我做了一个小 demo,大家可以点击下面这个链接去查看最终的效果。
控件面板整合渐现,限流等 - 码上掘金 (juejin.cn)
好了,这个简单的效果到这就暂时告一段落了,可以看到这个简单的效果也是涉及多方面的。下面就是我的自我推销时间了~
大家的点赞和收藏就是我源源不断的动力,有兴趣的可以关注该专栏,如果有合适的需求该专栏也会进行更新,对我感兴趣的也可以关注我鸭~ 😋
希望这系列的文章能对大家有所帮助,当然如果看的人如果过不了两千我就不更了,热度不够我也不更了,毕竟写文章还是比较累的,平均每篇文章都要花三四个小时以上,因为我要写各种 demo,还要检查 demo 结果是否准确,毕竟不能误人子弟呀。