In 2020, Kuaishou won the gala’s title cooperation, while this year, Douyin became the gala’s exclusive red envelope interactive partner.

The gala’s sponsors have gradually shifted from traditional industries to Internet companies. Social short video and live broadcast have become a way of interaction for many new generations. With the maturity of 5G technology, live video will become more popular in the near future.

Vue3.0-douyin is an imitation of Douyin/Kuaishou short live video chat based on Vite2 +vue3.0+ VUex4 + VANT3 + V3Popup technology. Realized the short video sliding effect, chat comments/bullet screen/send gifts and other functions.

The framework

  • Technical framework: VUe3.0 + VUUE-Router +vuex4
  • Component library: Vant3. x (Vue3 component library on mobile)
  • Shell component: V3Popup (mobile vue3 shell component)
  • Font icon: Ali iconfont icon
  • Navigation bar +Tab bar: Customize top navigation and bottom Tab bar components

The directory structure

Vue3.0 User-defined Mobile pop-up components

V3Popup a versatile vuE3. X mobile popup component. Support component + functional call mode, more than 6+ pop-up types, 5+ animation effects and 20+ custom configuration parameters.

If you’re interested in how this works, check out the previous shared post.

Vue3. X series of custom mobile global popbox components

Vite. Config. js project configuration

A project created based on the Vite scaffold builder will have a vite.config.js configuration file. Some simple environment and path alias configurations are available.

import vue from '@vitejs/plugin-vue'import path from 'path'/** * @type {import('vite').UserConfig} */export default { Plugins: [vue()], build: {// base directory // base: '/', /** * output file directory * @default dist (default) */ / outDir: 'target',}, // Environment configuration server: {// Custom interface port: 3000, // Whether automatic browser open: false, // whether HTTPS HTTPS: false, // server render SSR: False, // proxy configuration proxy: {//... }}, / / set the path alias alias: {' @ ': path. Resolve (__dirname,'. / SRC '), '@ components: path.resolve(__dirname, './src/components'), '@views': path.resolve(__dirname, './src/views') }}Copy the code

Introducing common components

Create a plugins.js file to introduce common components that are commonly used.

// Import Vant from 'Vant 'import' Vant /lib/index.css'// import vue3. x mobile shell 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) // Register public components app.component('navbar', NavBar) app.component('tabbar', TabBar) app.provide('utils', Utils) app.provide('storage', Storage)}Copy the code

Home Page Layout Template

The home page is divided into three parts: Navbar + video area + Tabbar at the bottom. Transparent Fixed property on the bottom and bottom components will be hollowed out and fixed on the video area.

<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" -- /// 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"> <! - / / / video module -- -- > < 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=" 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-arrr Fs-28 "></ I >  </div> <div v-if="item2.collectionLs&&item2.collectionLs.length>0" class="item swipe__superlk"> <i class="iconfont </div>< I class="iconfont icon-arrr FS-24 "></ I ><div class="iconfont icon-arrr FS-24"  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 ? 'Already concerned' : 'attention'}} < / 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> <! /// right toolbar --> <div class="swipe__toolbar">... </div> </div> </div> </van-swipe-item> </van-swipe> </template> </van-swipe-item> </van-swipe> <! - / / / the progress bar at the bottom of the - > < 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, 6)) "color =" rgba (255255255, 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 =".2"> <div class="nt__commentWrap"> <! -- Comment list -->... </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 =".2" title="<div style='text-align:left; Share to '> < / div > ": the BTNS =" [{text:' cancel ', style: 'color: # 999;', click: () = > isShowSharePopup = false},] ">... </v3-popup> </div> </template>Copy the code

Vue3.0 registration form validation | countdown function

<! <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 =" please input password "/> </div> <div class="item flexbox flex_alignc"> <input class="iptxt flex1" type="text" /> <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"> To log in < / router - the link > < / div > < / form > < / div > < / div > < / div > < / div > < / div > < / template > < script > / * * * @ Desc vue3.0 form validation | s countdown 60 * @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: reactive, 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(' Mobile phone number cannot be empty! ') }else if(! Utils. CheckTel (formobj.tel)){VTMsg(' Phone number format is not correct! ') }else if(! Formobj.pwd){VTMsg(' Password cannot be empty! ') }else if(! Formobj.vcode){VTMsg(' Verification code cannot be empty! ') }else{ // ... }} // countdown const handleVcode = () => {if(! Formobj.tel) {VTMsg(' Mobile phone number cannot be empty! ') }else if(! Utils. CheckTel (formobj.tel)) {VTMsg(' Phone number format is not correct! ') }else { data.time = 60 data.disabled = true countDown() } } const countDown = () => { if(data.time > 0) { Data. vcodeText = 'Get verification code ('+ data.time +')' data.time-- setTimeout(countDown, 100)}else{data.vcodeText = 'data.time = 0 data.disabled = false}} return {formObj,... toRefs(data), handleSubmit, handleVcode } } } </script>Copy the code

Vue3.0 short video + live broadcast module

Swipe component vant3 is used to achieve the effect of swiping left and right up and down.

Slide up and down to switch video and stop small video playing.

Const handleSwipeVertical = (index) => {if(data.activenav == 0) {// nearby page data.activeOneidx = index}else ActiveTwoIdx = index}else if(data.activenav == 2) {// Recommend page data.activeThreeidX = index } vdTimer.value && clearInterval(vdTimer.value) data.vdProgress = 0 data.isPlay = false let video = getVideoContext() if(! Video.currenttime = 0 data.activesWipeIndex = index // Next handlePlay()}Copy the code

Control click video area play/pause function.

// Const handleVideoClicked = () => {console.log('... ') tapTimer.value && clearTimeout(tapTimer.value) data.clickNum++ tapTimer.value = setTimeout(() => { if(data.clickNum >= 2) {console.log(' Double-click event ')}else {console.log(' click event ') if(data.isplay) {handlePause()}else {handlePlay()}} Data.clicknum = 0}, 300)} // Play const handlePlay = () => {console.log(' Play video... ') let video = getVideoContext() if(! Video) return video.play() data.isplay = true // Set progress bar vdtimer.value = setInterval(() => {handleProgress()}, 16)} // Pause const handlePause = () => {console.log(' Pause video... ') let video = getVideoContext() if(! video) return video.pause() data.isPlay = false vdTimer.value && clearInterval(vdTimer.value) }Copy the code

A mini progress bar is displayed at the bottom of the video area. Convert the small video length and current playing time to a percentage, and then control the animation with CSS3 Transition.

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) }Copy the code

The gifting/topping up template is implemented using the V3Popup component.

<! -... "> < p style =" margin-top: 1em; margin-bottom: 1em; > <div class="wrap_giftList"> <div class="gt__hdtit flex-c"> <i class="back iconfont icon-close" @click="isShowGiftPopup=false"></ I >< div class="flex1"> <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"> <div class="inner flex-c flex-col"> <img class="gtimg" src="/static/gift/gift-img22.png" /> <p Clap GTLBL class = "" > < / p > < p class =" gtnum "> < I class =" iconfont icon - douzi "> < / I > 166 < / p > < / div > < / div > -- > < 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> <! -... "> < p style =" margin-top: 1em; margin-bottom: 1em; 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"> Select the recharge amount </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="gtcoins"> </div> </div> <div class="pad10"><button class="vui__btn vui__btn-primary" style="border-radius:.1rem; height:40px;" {{rechargeLs[rechargeIdx].gtmoney}} </button></div> </div> </v3-popup>Copy the code

In addition, the link shown in the small video will be displayed after clicking.

Ok, the short video of developing fake Douyin/Kuaishou interface based on VUe3.0 is shared here. 💪

Finally, paste a Vue3 PC example project

Vue3. X + element – plus web chat instance | vue3 imitation WeChat web side