前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue3.0+Vant3移动端短视频+聊天+直播实战

Vue3.0+Vant3移动端短视频+聊天+直播实战

原创
作者头像
andy2018
修改2021-02-03 14:30:32
4.3K1
修改2021-02-03 14:30:32
举报
文章被收录于专栏:h5h5h5

前言

随着人们生活品质的提高,5G及手机硬件的快速发展,短视频/直播快速的成为了很多人的娱乐方式。

前一段时间有分享一个vue3.0网页端聊天实例,今天继续给大家分享一个最新开发的vue3.0小视频+直播实战项目。

vue3.0-douyin 支持滑动切换小视频、点赞/评论/商品、聊天/弹幕/送礼物等功能。

使用技术

  • 编码工具:Vscode
  • 构建工具:vite.js
  • 技术框架:vue^3.0.5
  • 状态管理:vuex^4.0.0-rc.2
  • 页面路由:vue-Router^4.0.3
  • UI组件库:vant^3.0.4 (有赞手机端vue3组件库)
  • 弹框组件:v3popup(基于vue3自定义手机端弹出层组件)
  • 字体图标:阿里iconfont图标

项目结构

遵循了vue3标准式语法结构来编码开发。

有赞vue3移动端组件库

vant3 是有赞前端团队率先开发的一款vue3.0移动端UI组件库。

大家使用vue3开发项目的话,就安装vue3版本的组件库vant3。

# Vue 2 项目,安装 Vant 2:
npm i vant -S

# Vue 3 项目,安装 Vant 3:
npm i vant@next -S

在main.js中全局注册组件。

import { createApp } from 'vue';
import Vant from 'vant';
import 'vant/lib/index.css';

const app = createApp();
app.use(Vant);

大家也可以根据需要,按需引入组件。

如上图:使用vant3组件来实现下拉刷新+侧滑功能效果。

vue3.0自定义mobile弹窗组件

项目中使用到的弹窗场景,均是基于vue3自定义弹框来实现。之前有过相关分享文章,大家可以去看看。

vue3.x自定义mobile版弹窗组件|vue3对话框

vue3实现小视频/直播效果

项目中的小视频和直播页面有些功能是公用的,小视频页可以上下左右滑动切换,直播页上下滑动并新增弹幕/滚动消息/送礼物等功能。

<template>
    <div class="bg-161823">
        <!-- >>顶部NavBar -->
        <navbar :back="false" bgcolor="transparent" transparent>
            <template v-slot:title>
                ...
            </template>
            <template v-slot:right><div><i class="iconfont icon-search"></i></div></template>
        </navbar>

        <!-- >>主面板 -->
        <div class="vui__scrollview flex1">
            <div class="vui__swipeview">
                <!-- ///滑动切换区 -->
                <van-swipe ref="swipeHorizontalRef" :show-indicators="false" :loop="false" @change="handleSwipeHorizontal">
                    <van-swipe-item v-for="(item,index) in videoLs" :key="index">
                        <template v-if="item.category == 'nearby'">
                            <div class="swipe__nearLs">
                                ...
                            </div>
                        </template>
                        <template v-if="item.category == 'recommend' || item.category == 'follow'">
                            <van-swipe vertical lazy-render :show-indicators="false" :loop="false" @change="handleSwipeVertical">
                                <van-swipe-item v-for="(item2, index2) in item.list" :key="index2">
                                    <!-- ///视频模块 -->
                                    <div class="swipe__video">
                                        <video class="vdplayer" :id="'vd-'+index+'-'+index2" loop preload="auto"
                                            :src="item2.src"
                                            :poster="item2.poster"
                                            webkit-playsinline="true" 
                                            x5-video-player-type="h5-page"
                                            x5-video-player-fullscreen="true"
                                            playsinline
                                            @click="handleVideoClicked"
                                        >
                                        </video>
                                        <span v-show="!isPlay" class="btn__play" @click="handleVideoClicked"><i class="iconfont icon-bofang"></i></span>
                                    </div>
                                    <!-- ///信息模块 -->
                                    <div class="swipe__vdinfo flexbox flex-col">
                                        <div class="flexbox flex-alignb">
                                            <!-- ///底部信息栏 -->
                                            <div class="swipe__footbar flex1">
                                                <div v-if="item2.ads" class="item swipe__superlk ads" @click="handleOpenLink(item2)">
                                                    <i class="iconfont icon-copylink fs-28"></i>查看详情<i class="iconfont icon-arrR fs-24"></i>
                                                </div>
                                                <div v-if="item2.collectionLs&&item2.collectionLs.length>0" class="item swipe__superlk">
                                                    <i class="iconfont icon-copylink fs-24 mr-10"></i><div class="flex1">合集《小鬼当家》主演花絮</div><i class="iconfont icon-arrR fs-24"></i>
                                                </div>
                                                <div class="item uinfo flexbox flex-alignc">
                                                    <router-link to="/friend/uhome"><img class="avatar" :src="item2.avatar" /></router-link>
                                                    <router-link to="/friend/uhome"><em class="name">{{item2.author}}</em></router-link>
                                                    <button class="btn vui__btn vui__btn-primary" :class="item2.isFollow ? 'isfollow' : ''" @click="handleIsFollow(item.category, index2)">{{item2.isFollow ? '已关注' : '关注'}}</button>
                                                </div>
                                                <div class="item at">@{{item2.author}}</div>
                                                <div v-if="item2.topic" class="item kw"><em v-for="(kw,idx) in item2.topic" :key="idx">#{{kw}}</em></div>
                                                <div class="item desc">{{item2.desc}}</div>
                                            </div>
                                            <!-- ///右侧工具栏 -->
                                            <div class="swipe__toolbar">
                                                ...
                                            </div>
                                        </div>
                                    </div>
                                </van-swipe-item>
                            </van-swipe>
                        </template>
                    </van-swipe-item>
                </van-swipe>
                <!-- ///底部进度条 -->
                <div class="swipe__progress"><i class="bar" :style="{'width': vdProgress+'%'}"></i></div>
            </div>
        </div>

        <!-- >>底部TabBar -->
        <tabbar
            bgcolor="linear-gradient(to bottom, transparent, rgba(0,0,0,.6))"
            color="rgba(255,255,255,.6)"
            activeColor="#fff" 
            fixed
        />


        <!-- ……商品模板 -->
        <v3-popup v-model="isShowGoodsPopup" position="bottom" round xclose title="热销商品" @end="handlePopStateClose" opacity=".2">
            <div v-if="goodsLs" class="wrap_goodsList">
                ...
            </div>
        </v3-popup>

        <!-- ……评论列表模板 -->
        <v3-popup v-model="isShowReplyPopup" position="bottom" round xclose opacity=".2">
            <div class="nt__commentWrap">
                <!-- 评论列表 -->
                ...
            </div>
        </v3-popup>
        <!-- ……评论编辑器模板 -->
        <v3-popup v-model="isShowReplyEditor" position="bottom" opacity=".2">
            <div class="vui__footTool nt__commentWrap">
                ...
            </div>
        </v3-popup>

        <!-- ……分享模板 -->
        <v3-popup v-model="isShowSharePopup" anim="footer" type="actionsheet" round xclose opacity=".2"
            title="<div style='text-align:left;'>分享至</div>"
            :btns="[
                {text: '取消', style: 'color:#999;', click: () => isShowSharePopup=false},
            ]"
        >
            ...
        </v3-popup>
    </div>
</template>

<script>
/**
 * @Desc     Vue3.0实现小视频功能
 * @Time     andy by 2021-02
 * @About    Q:282310962  wx:xy190310
 */
import { onMounted, onUnmounted, ref, reactive, toRefs, inject, nextTick } from 'vue'

import CmtEditor from '@components/cmtEditor.vue'

import videoJSON from '@/mock/videolist.js'
import emojJSON from '@/mock/cmt-emoj.js'

export default {
    components: {
        CmtEditor,
    },
    setup() {
        // 定时器
        const vdTimer = ref(null)
        const tapTimer = ref(null)
        const swipeHorizontalRef = ref(null)

        const editorRef = ref(null)

        const v3popup = inject('v3popup')

        const data = reactive({
            // ...
        })

        onMounted(() => {
            swipeHorizontalRef.value.swipeTo(data.activeNav, {immediate: true})

            // ...
        })

        // ...

        // 垂直切换页面事件
        const handleSwipeVertical = (index) => {
            if(data.activeNav == 0) {
                // 附近页
                data.activeOneIdx = index
            }else if(data.activeNav == 1) {
                // 关注页
                data.activeTwoIdx = index
                // console.log('关注页索引:' + index)
            }else if(data.activeNav == 2) {
                // 推荐页
                data.activeThreeIdx = index
                // console.log('推荐页索引:' + index)
            }

            vdTimer.value && clearInterval(vdTimer.value)
            data.vdProgress = 0
            data.isPlay = false
            let video = getVideoContext()
            if(!video) return
            video.pause()
            // 重新开始
            video.currentTime = 0

            data.activeSwipeIndex = index

            // 自动播放下一个
            handlePlay()
        }

        // 播放
        const handlePlay = () => {
            console.log('播放视频...')

            let video = getVideoContext()
            if(!video) return
            video.play()
            data.isPlay = true

            // 设置进度条
            vdTimer.value = setInterval(() => {
                handleProgress()
            }, 16)
        }

        // 暂停
        const handlePause = () => {
            console.log('暂停视频...')

            let video = getVideoContext()
            if(!video) return
            video.pause()
            data.isPlay = false
            vdTimer.value && clearInterval(vdTimer.value)
        }

        // 视频点击事件(判断单/双击)
        const handleVideoClicked = () => {
            console.log('触发视频点击事件...')

            tapTimer.value && clearTimeout(tapTimer.value)
            data.clickNum++
            tapTimer.value = setTimeout(() => {
                if(data.clickNum >= 2) {
                    console.log('双击事件')
                }else {
                    console.log('单击事件')
                    if(data.isPlay) {
                        handlePause()
                    }else {
                        handlePlay()
                    }
                }
                data.clickNum = 0
            }, 300)
        }

        // 播放进度条
        const handleProgress = () => {
            let video = getVideoContext()
            if(!video) return
            let curTime = video.currentTime.toFixed(1)
            let duration = video.duration.toFixed(1)
            data.vdProgress = parseInt((curTime / duration).toFixed(2) * 100)
        }

        // ...

        return {
            ...toRefs(data),
            swipeHorizontalRef,
            editorRef,

            // ...
        }
    }
}
</script>

为了保证滑动的流畅性,设置lazy-render属性会延迟加载下一页,大幅度提升滑动效果。

小视频页面底部迷你播放进度条,使用当前播放进度和时长转换,使用css3控制动画效果。

// 迷你视频播放进度条
const handleProgress = () => {
    let video = getVideoContext()
    if(!video) return
    let curTime = video.currentTime.toFixed(1)
    let duration = video.duration.toFixed(1)
    data.vdProgress = parseInt((curTime / duration).toFixed(2) * 100)
}

ok,基于vue3+vite2开发短视频/直播实例就分享到这里。希望大家喜欢哈!!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 使用技术
  • 项目结构
  • 有赞vue3移动端组件库
  • vue3.0自定义mobile弹窗组件
  • vue3实现小视频/直播效果
相关产品与服务
云直播
云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档