0
点赞
收藏
分享

微信扫一扫

Vue2实现Vue3中的Teleport传送门,【应用场景:实现B站视频播放效果】

先看下效果 

Vue2实现Vue3中的Teleport传送门,【应用场景:实现B站视频播放效果】_前端  

在Vue3中​​<Teleport>​​ 是一个内置组件,使我们可以将一个组件的一部分模板“传送”到该组件的 DOM 层次结构之外的 DOM 节点中

官网:​​Teleport传送门​​

咱们可以使用Teleport实现一件有趣的事情 ,就是B站的视频播放效果。也就是当咱们播放视频,向下滚动至视频离开可视区,会在右下方出现一个小窗口继续咱们的视频播放,不会中断。

其实用Vue3还是蛮好实现的,通过Teleport传送门和useIntersectionObserver API可以轻松实现。这是我之前用vue3写的一个demo,感兴趣可以去瞅瞅喽 ​​使用Vue3中的Teleport API实现B站视频播放效果​​

接下正题:用Vue2实现同样的效果 

咱们首先手写一个Teleport组件 ,能满足咱们的需求就可以。

Teleport组件 

<script>
export default {
props: {
to: {
type: String,
required: true
},
disabled: {
type: Boolean,
default: true
}
},
inject: ['parent'],
watch: {
disabled() {
if(this.disabled) { // 把当前的DOM元素 向目标元素传送过去
document.querySelector(this.to).appendChild(this.$el)
}else { // 调用父组件的方法, 再将当前的DOM元素还原的原来的位置
this.parent.toSourceDom(this.$el)
}
}
},
mounted(){
if(this.disabled) document.querySelector(this.to).appendChild(this.$el)
},
/**
* render方法中,如果有变量得使用{}
* this.$scopedSlots.default() 表示预留一个插槽
*/
render() {
return <div class="Teleport">{this.$scopedSlots.default()}</div>
}
}
</script>
<style scoped>
.Teleport {
width: 100%;
height: 100%;
}
</style>

接受一个to属性和disabled属性,to属性表示传送到的目标元素dom id或者class类名。disabled表示什么时候要进行传送,true表示传送,false表示不传送

咱们监听 disabled变化就可以,this.$el是当前这个Teleport组件的所有dom节点,通过render函数渲染出dom并预留一个插槽

然后是父组件的逻辑编写: 

<template>
<div id="main">

<!-- 要播放的大视频窗口 -->
<div id="sourceBox">
<Teleport v-if="isShow" :disabled="isTeleport" :to="'#targetBox'">
<VideoModel :id="'VideoModel'" />
</Teleport>
</div>

<!-- 模拟滚动 -->
<div style="width: 100%;height: 2000px;"></div>

<!-- 被传送的小视频窗口 -->
<div id="targetBox"></div>

</div>
</template>
<script>
import Teleport from '@/components/Teleport/Teleport.vue'
import VideoModel from '@/components/video-model/videoModel'
export default {
data() {
return {
isShow: false, // 控制 Teleport 组件挂载时机
isTeleport: false // 控制什么时候被传送
}
},
provide() {
return {
parent: this
}
},
components: { Teleport, VideoModel },
mounted(){
this.isShow = true // 确保传送到的目标元素挂载在 Teleport 组件之前
document.getElementById('main').addEventListener('scroll', this.intersectionObserver)
},
destroyed() { // 离开页面移除监听
document.getElementById('main').removeEventListener('scroll', this.intersectionObserver)
},
methods: {
// 监听 sourceBox 是否在可视区域内
intersectionObserver() {
let main = document.getElementById('main')
let sourceBox = document.getElementById('sourceBox')
if(main.scrollTop >= sourceBox.getBoundingClientRect().height) { // 传送
console.log('传-->>')
this.isTeleport = true
}else { // 不传
this.isTeleport = false
}
},
// 当不需要传送的时候,再挂载到原来的位置
toSourceDom(dom) {
document.getElementById('sourceBox').appendChild(dom)
}
}
}
</script>
<style scoped>
#main {
width: 100%;
height: 100%;
position: relative;
overflow: auto;
}
#sourceBox {
position: absolute;
width: 640px;
height: 320px;
left: 0;
top: 0;
border: 1px solid red;
box-sizing: border-box;
}
#targetBox {
position: fixed;
right: 50px;
bottom: 20px;
width: 320px;
height: 180px;
border: 1px solid red;
box-sizing: border-box;
}
</style>

视频我用的是 videojs,在我​​vue使用video.js​​

这篇博客有写,或者把视频去掉,改成文字,看看下传送的效果也是可以的,注意 #main 一定要有宽高,我这里是100% * 100% 根据实际情况稍作修改就好 

其实Teleport 本质上也是,appendChild 去目标元素追加dom节点,根据 disabled 判断是向目标元素追加,还是再挂载到原来的位置


举报

相关推荐

0 条评论