基于vue3.0构建移动端仿抖音/快手短视频+直播实战项目Vue3-DouYin

5G时代已来,短视频也越来越成为新一代年轻人的娱乐方式,在这个特殊之年,又将再一次成为新年俗!

基于vue3.x+vite2+vuex4+vue-router+vant3+v3popup等技术搭建开发仿抖音App界面小视频/直播/聊天实例项目。实现短视频上下左右滑动切换、点赞/评论/聊天/红包及送礼物等功能。

一、运用技术

  • 编码器:VScode/Notepad++
  • 使用技术:Vue3.x+Vuex4.x+Vue-Router4
  • 组件库:Vant^3.0.4 (有赞移动端vue3组件库)
  • 弹层组件:V3Popup(基于vue3自定义弹层组件)
  • 字体图标:阿里iconfont图标库
  • 导航栏+标签栏:基于vue3自定义navbar/tabbar组件

二、项目目录结构

◆ 效果预览

◆ vue3.x自定义顶部导航+标签栏

项目中所有顶部导航及底部tabbar均是使用vue3自定义组件来实现效果,支持自定义插槽内容。

<navbar :back="false" bgcolor="transparent" transparent>
<template v-slot:title>
<div class="navbar__tab">
...
</div>
</template>
<template v-slot:right><div><i class="iconfont icon-search"></i></div></template>
</navbar> <tabbar
bgcolor="linear-gradient(to bottom, transparent, rgba(0,0,0,.6))"
color="rgba(255,255,255,.6)"
activeColor="#fff"
fixed
/>

◆ vue3.x全局弹出层组件

项目中所有的弹框应用场景均是之前开发的一款vue3自定义组件v3popup来实现功能。

vue3版的自定义弹框组件,拥有20+种自定义参数配置,多种弹框类型及动画效果。

https://www.cnblogs.com/xiaoyan2017/p/14210820.html

◆ vite.config.js配置文件

一些简单的vite2项目配置,可进行一些常用环境及alias路径别名设置。

/**
* Vite2项目配置
*/ 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')
}
}

◆ 引入公共组件

让项目代码更加整洁,在plugins.js中配置一些公共组件,然后在main.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

◆ vue3.x表单验证+60s倒计时

<!-- //注册表单模板 -->
<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>
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.x实现小视频功能

小视频页面使用了有赞组件库中的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>
<script>
import { onMounted, onUnmounted, ref, reactive, toRefs, inject, nextTick } from 'vue' import CmtEditor from '@components/cmtEditor.vue' // ... 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 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)
} // ... // 打开链接
const handleOpenLink = (item) => {
// 监听路由地址栈
handlePopStateOpen() data.isShowLinkPopup = true
data.linkSrc = item.ads
data.linkTitle = item.adstitle ? item.adstitle : '网址链接'
} return {
...toRefs(data),
swipeHorizontalRef,
editorRef, handleTabNav,
handleSwipeHorizontal,
handleSwipeVertical,
handlePlay,
handlePause,
handleVideoClicked, // ...
}
}
}
</script>

至于项目中的聊天模块就不详细介绍了,之前有分享过一篇vue3.0开发移动端聊天实例项目,感兴趣的可以去看看哈~~

https://www.cnblogs.com/xiaoyan2017/p/14250798.html

◆ vue3.x弹幕功能简单实现

直播页面在小视频页面功能基础上新增弹幕,滚动消息区、送礼物、充值弹窗等功能。

弹幕功能的简单实现,共有3条滚动路线。

const data = reactive({
// ... // 弹幕队列
idx: 2,
dmLs: [
...
],
// 正在执行的弹幕队列
dmActiveLs: []
}) onMounted(() => {
// ... // 装载弹幕
setInterval(() => {
starDanMu()
}, 1500)
}) const starDanMu = () => {
let query = null
if(!query) {
query = data.dmLs.shift()
}
if(query) {
query.row = data.idx
data.idx = (data.idx % 3 + 1)
data.dmActiveLs.push(query)
}
}
<div class="lv__wrap-danmu">
<div class="danmu__bx">
<div class="danmu__ls" v-for="item in dmActiveLs" :key="item.id" :data-row="item.row" @animationend="dmAnimationEnd">
<div class="item">
<img class="avatar" :src="item.avatar" />
<p class="name">{{item.name}}</p><p class="desc">{{item.desc}}</p>
</div>
</div>
</div>
</div>

OK,以上就是使用vue3.x+vite2开发仿抖音小视频/直播的一些分享,希望对大家有些帮助哈~~ ✍

Vue3.0短视频+直播|vue3+vite2+vant3仿抖音界面|vue3.x小视频实例的更多相关文章

  1. Vite2+Electron仿抖音|vite2.x+electron12+vant3短视频|直播|聊天

    整合vite2+electron12跨平台仿抖音电脑版实战Vite2-ElectronDouYin. 基于vite2.0+electron12+vant3+swiper6+v3popup等技术跨端仿制 ...

  2. iOS视频直播初窥:高仿<喵播APP>

    视频直播初窥 视频直播,可以分为 采集,前处理,编码,传输, 服务器处理,解码,渲染 采集: iOS系统因为软硬件种类不多, 硬件适配性比较好, 所以比较简单. 而Android端市面上机型众多, 要 ...

  3. 10分钟快速上车短视频风口:基于uniapp框架创建自己的仿抖音短视APP

    在今年也就是第48次发布的<中国互联网络发展状况统计报告>有这样一个数据,21年的上半年以来,我国我国网民规模达10.11亿,其中短视频用户达8.88亿.碎片化的生活场景下,短视频成为人们 ...

  4. 基于vue+uniapp直播项目|uni-app仿抖音/陌陌直播室

    一.项目简介 uni-liveShow是一个基于vue+uni-app技术开发的集小视频/IM聊天/直播等功能于一体的微直播项目.界面仿制抖音|火山小视频/陌陌直播,支持编译到多端(H5.小程序.Ap ...

  5. iOS多种刷新样式、音乐播放器、仿抖音视频、旅游App等源码

    iOS精选源码 企业级开源项目,模仿艺龙旅行App 3D立体相册,可以旋转的立方体 横竖屏切换工具,使用陀螺仪检测手机设备方向,锁屏状... Swift版Refresh(可以自定义多种样式)架构方面有 ...

  6. uni-app仿抖音APP短视频+直播+聊天实例|uniapp全屏滑动小视频+直播

    基于uniapp+uView-ui跨端H5+小程序+APP短视频|直播项目uni-ttLive. uni-ttLive一款全新基于uni-app技术开发的仿制抖音/快手短视频直播项目.支持全屏丝滑般上 ...

  7. Vue3.0网页版聊天|Vue3.x+ElementPlus仿微信/QQ界面|vue3聊天实例

    一.项目简介 基于vue3.x+vuex+vue-router+element-plus+v3layer+v3scroll等技术构建的仿微信web桌面端聊天实战项目Vue3-Webchat.基本上实现 ...

  8. iOS 仿抖音 视频裁剪

    1.最近做短视频拍摄.其中的裁剪界面要做得和抖音的视频裁剪效果一样 需求:  裁剪有一个最大裁剪时间.最小裁剪时间.左右拖动可以实时查看对应的视频画面.拖动进度条也能查看对应的画面 .拖动底部视图也能 ...

  9. 关于个人项目(臻美MV【仿抖音App】)滑动切换视频的分析(前端角度)

    我们知道你天天刷抖音的时候可以上滑切换视频,互不影响.那么我们站在前端的角度能否可以实现这种效果呢?这是我的个人项目:臻美MV 下面我是用Vue写的,现在我把它开源. Vue: 初始界面 <te ...

随机推荐

  1. 使用代码管理工具(git)管理代码的常用指令合集

    create a new repository on the command line echo "# test" >> README.md git init git ...

  2. LVS之1---工作原理

    LVS之1---工作原理 LVS 介绍 LVS:Linux Virtual Server,Linux虚拟服务器,负载调度器,是一个由章文嵩(花名 正明)博士从1998年开始发起的自由软件项目. 软件作 ...

  3. JS function 是函数也是对象, 浅谈原型链

    JS function 是函数也是对象, 浅谈原型链 JS 唯一支持的继承方式是通过原型链继承, 理解好原型链非常重要, 我记录下我的理解 1. 前言 new 出来的实例有 _proto_ 属性, 并 ...

  4. java类的主动使用/被动使用

    对类的使用方式分为:主动使用.被动使用 所有的java虚拟机实现必须在每个类或接口被java程序"首次主动使用"时才初始化他们 ps:被动使用不会初始化类,但是有可能会加载类(JV ...

  5. 推荐一款自研的Java版开源博客系统OneBlog

    OneBlog 一款超好用的Java版开源博客      Introduction 简介 OneBlog 一个简洁美观.功能强大并且自适应的Java博客.使用springboot开发,前端使用Boot ...

  6. Java中jna的用法

    (1)jna是对jni的封装,让java使用者能更好的使用本地的动态库 (2)使用jna需要下载jna的jar包,该jar包就是对jni的封装,所以在调用效率上来讲,jna是要比jni低一点的,不过对 ...

  7. 安装Linux Deploy和Termux之后,再安装ftp服务软件都是多余的!

    之前以为Debian 9 running via Linux Deploy或者Termux在安卓系统部署之后,一定要安装vsftpd或者pure-ftpd这些专门的ftp服务器软件,才能提供ftp服务 ...

  8. 用 Flutter 搭建标签+导航框架

    前言 在 Flutter 这个分类的第一篇文章总结了下最新的 Mac 搭建 Flutter 开发环境和对声明式UI这个理解的东西,前面也有提过,准备像在 SwiftUI 分类中那样花一些功夫来写一个 ...

  9. docker基础总结

    搜索镜像docker search ubuntu 搜索ubuntu的Docker镜像 搜索结果单个单词ubuntu这样的镜像,被称为基础镜像或根镜像,这些基础镜像由 Docker 公司创建搜索结果ti ...

  10. Log4j配置按照文件大小和日期分割日志文件

    目录 Log4j 下载地址 文件大小分割日志文件 以日期分割每天产生一个日志文件 自定义信息输出到日志文件 Log4j 下载地址 Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控 ...