irpas技术客

vue3.0+vant3仿快手/抖音短视频|Vue3+Vite2聊天/直播实例_xiaoyan_2018_vue 快手

大大的周 4521

vue3.0-douyin 基于vue3.x开发仿抖音app界面小视频+直播聊天实例。

基于Vue3框架技术搭建一款仿抖音APP界面小视频+直播实战案例。运用到了vite2+vue3.0.5+vuex4+vue-router@4+vant3+v3popup等技术,实现了短视频滑动切换、暂停/迷你进度条、聊天评论/弹幕/送礼物等功能。

??

技术框架 技术/编码:vscode /?vue3.0+vue-router+vuex4UI组件库:vant3.x (有赞移动端vue3组件库)弹层组件:v3popup(移动端vue3弹框组件)字体图标:阿里iconfont图标导航条+Tab栏:自定义顶部导航/底部标签栏组件

项目结构

采用标准的vue3分层目录结构,让项目结构更加清晰。

所有的页面模板及逻辑部分统一使用vue3.x最新语法来编码。

vue3手机端弹框组件

整个项目所用到的弹框功能,都是使用vue3.0自定义组件实现。

v3-popup 一款轻量级的移动端vue3弹框组件。集合了msg、alert、dialog、modal、actionSheet、toast等多种弹框类型。支持超过20+中参数自定义配置。

大家感兴趣的话,可以去看看之前的这篇分享文章。

https://blog.csdn.net/yanxinyun1990/article/details/111975487

vite2配置文件

使用vite构建vue3项目后,会有一个vite.config.js配置文件。用来进行一些项目配置。

import vue from '@vitejs/plugin-vue' import path from 'path' /** * @type {import('vite').UserConfig} */ export default { plugins: [vue()], build: { // 基本目录 // base: '/', /** * 输出文件目录 * @default dist(默认) */ // outDir: 'target', }, // 环境配置 server: { // 自定义接口 port: 3000, // 是否自动浏览器打开 open: false, // 是否开启https https: false, // 服务端渲染 ssr: false, // 代理配置 proxy: { // ... } }, // 设置路径别名 alias: { '@': path.resolve(__dirname, './src'), '@components': path.resolve(__dirname, './src/components'), '@views': path.resolve(__dirname, './src/views') } } vue3公共组件配置

新建一个plugins.js文件,用来引入一些常用的公共组件。

// 引入Vant3.x组件库 import Vant from 'vant' import 'vant/lib/index.css' // 引入Vue3.x移动端弹层组件 import V3Popup from '@components/v3popup' import NavBar from '@components/navBar.vue' import TabBar from '@components/tabBar.vue' import Utils from './utils' import Storage from './storage' const Plugins = (app) => { app.use(Vant) app.use(V3Popup) // 注册公用组件 app.component('navbar', NavBar) app.component('tabbar', TabBar) app.provide('utils', Utils) app.provide('storage', Storage) } export default Plugins

然后在main.js中全局引入注册即可。

// 引入公用组件 import Plugins from './plugins' const app = createApp(App) app.use(Plugins) // ... vue3注册表单验证 | 60s倒计时

vue3.0中实现表单模板注册功能及倒计时控制。getCurrentInstance来获取上下文操作store或router。

<!-- //注册表单模板 --> <template> <div> <div class="vui__scrollview vui__scrollview-lgreg flex1"> <div class="nt__lgregPanel"> <div class="lgreg-header"> <div class="slogan"> <img class="logo" src="/static/logo.png" /> <p class="text ff-gg">Vue3.0-DouYin</p> </div> <div class="forms"> <form @submit.prevent="handleSubmit"> <div class="item flexbox flex_alignc"> <input class="iptxt flex1" type="text" v-model="formObj.tel" placeholder="请输入手机号" maxlength="11" /> </div> <div class="item flexbox flex_alignc"> <input class="iptxt flex1" type="password" v-model="formObj.pwd" placeholder="请输入密码" /> </div> <div class="item flexbox flex_alignc"> <input class="iptxt flex1" type="text" v-model="formObj.vcode" placeholder="验证码" /> <button class="btn-getcode" @click.prevent="handleVcode" :disabled="disabled">{{vcodeText}}</button> </div> <div class="item btns"> <button class="flex-c" type="submit"><i class="iconfont icon-go c-fff"></i></button> </div> <div class="item lgreg-lk"> <router-link class="navigator" to="/login">已有账号,去登录</router-link> </div> </form> </div> </div> </div> </div> </div> </template> <script> /** * @Desc vue3.0表单验证|60s倒计时 * @Time andy by 2021-02 * @About Q:282310962 wx:xy190310 */ import { reactive, toRefs, inject, getCurrentInstance } from 'vue' export default { components: {}, setup() { const { ctx } = getCurrentInstance() const v3popup = inject('v3popup') const utils = inject('utils') const formObj = reactive({}) const data = reactive({ vcodeText: '获取验证码', disabled: false, time: 0, }) const VTMsg = (content) => { v3popup({ content: `<div style='text-align:center;'><i class='iconfont icon-error'></i> ${content}</div>`, popupStyle: 'background:#ffefe6;color:#fe2c55;', position: 'top', time: 2 }) } const handleSubmit = () => { if(!formObj.tel){ VTMsg('手机号不能为空!') }else if(!utils.checkTel(formObj.tel)){ VTMsg('手机号格式不正确!') }else if(!formObj.pwd){ VTMsg('密码不能为空!') }else if(!formObj.vcode){ VTMsg('验证码不能为空!') }else{ // ... } } // 倒计时 const handleVcode = () => { if(!formObj.tel) { VTMsg('手机号不能为空!') }else if(!utils.checkTel(formObj.tel)) { VTMsg('手机号格式不正确!') }else { data.time = 60 data.disabled = true countDown() } } const countDown = () => { if(data.time > 0) { data.vcodeText = '获取验证码('+ data.time +')' data.time-- setTimeout(countDown, 1000) }else{ data.vcodeText = '获取验证码' data.time = 0 data.disabled = false } } return { formObj, ...toRefs(data), handleSubmit, handleVcode } } } </script> vue3小视频 | 直播模块

项目中小视频及直播页面使用了vant3组件库中的swipe轮播组件实现滑动切换效果。

开启lazy-render功能,可延迟渲染内容,让滑动效果更加流畅。

<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 v-if="item2.goods&&item2.goods.length>0" class="item ball flexbox" @click="handleOpenGoods(item2.goods)"><i class="ico iconfont icon-cart"></i></div> <div class="item" @click="handleIsLike(item.category, index2)"><i class="ico iconfont icon-like" :class="item2.isLike ? 'islike' : ''"></i><p class="num">{{item2.likeNum+(item2.isLike ? 1 : 0)}}</p></div> <div class="item" @click="isShowReplyPopup=true"><i class="ico iconfont icon-liuyan"></i><p class="num">{{item2.replyNum}}</p></div> <div class="item" @click="isShowSharePopup=true"><i class="ico iconfont icon-fenxiang"></i><p class="num">{{item2.shareNum}}</p></div> </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>

直播页面送礼物模块,使用了v3popup弹窗实现功能。

<!-- ……送礼物模板 --> <v3-popup v-model="isShowGiftPopup" position="bottom" round popupStyle="background:#36384a;"> <div class="wrap_giftList"> <div class="gt__hdtit flex-c"> <i class="back iconfont icon-close" @click="isShowGiftPopup=false"></i> <div class="flex1">赠送礼物</div> <div class="num" @click="isShowRechargePopup=true"><i class="iconfont icon-douzi fs-24"></i> 0 <i class="iconfont icon-arrR fs-24"></i></div> </div> <div class="gt__swipe"> <div class="gtitem" :class="giftCur == index ? 'on' : ''" v-for="(item,index) in giftLs" :key="index" @click="handleGiftClicked(item, index)"> <div class="inner flex-c flex-col"> <img class="gtimg" :src="item.giftPic" /> <p class="gtlbl">{{item.giftLabel}}</p> <p class="gtnum"><i class="iconfont icon-douzi"></i> {{item.giftCoins}}</p> </div> </div> </div> </div> </v3-popup> <!-- ……充值模板(微信豆) --> <v3-popup v-model="isShowRechargePopup" position="bottom" round popupStyle="background:#36384a;" opacity="0"> <div class="wrap_giftList"> <div class="gt__hdtit flex-c"> <i class="back iconfont icon-arrD" @click="isShowRechargePopup=false"></i> <div class="flex1">选择充值金额</div> <div class="num"><i class="iconfont icon-douzi fs-24"></i> 0</div> </div> <div class="gt__swipe gt__recharge"> <div class="gtitem" :class="rechargeIdx == index ? 'cur' : ''" v-for="(item,index) in rechargeLs" :key="index" @click="handleRecharge(index)"> <div class="inner flex-c flex-col"> <p class="gtcoins"><i class="iconfont icon-douzi"></i> {{item.gtcoins}}</p> <p class="gtmoney">售价{{item.gtmoney}}元</p> </div> </div> <div class="pad10"><button class="vui__btn vui__btn-primary" style="border-radius:.1rem;height:40px;" @click="isShowSubmitRecharge=true">确认支付(¥{{rechargeLs[rechargeIdx].gtmoney}})</button></div> </div> </div> </v3-popup>

判断视频单击/双击,单击就播放/暂停视频,双击可以进行一些诸如点赞操作。

<script> import { onMounted, onUnmounted, ref, reactive, toRefs, inject, nextTick } from 'vue' // ... export default { setup() { // 定时器 const vdTimer = ref(null) const tapTimer = ref(null) const swipeHorizontalRef = ref(null) const editorRef = ref(null) const v3popup = inject('v3popup') // ... // 垂直切换页面事件 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) } return { ...toRefs(data), // ... } } } </script>

另外聊天模块这里就不作介绍了,之前有过一篇分享,大家可以去看看。

vue3.0+vant3搭建移动端聊天实战项目

https://blog.csdn.net/yanxinyun1990/article/details/112355981

好了,基于vue3开发仿制抖音短视频实例项目就分享到这里。希望大家能喜欢哈~~💪🏻

最后附上一个vue3桌面端pc聊天实例项目

https://blog.csdn.net/yanxinyun1990/article/details/112975740

?


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Vue #快手 #vue30douyin #技术框架技术编码vscode