uniapp-welive仿微信/抖音直播帶貨|uni-app+vue3+pinia短視頻直播商城
基于uniapp+vue3+uv-ui跨端H5+小程序+App短視頻+直播帶貨商城Uniapp-WeLive。
uni-welive一款全新基于uniapp+vue3+pinia+vk-uview等技術跨端仿制抖音/微信直播帶貨商城項目。支持沉浸式全屏上下滑動短視頻直播,Nvue多視頻層級覆蓋,支持編譯到兼容H5+小程序+App端。
預覽效果
編譯H5+小程序+App端效果如下

使用技術
- 編輯器:HbuilderX 3.98
- 框架技術:Uniapp+Vue3+Vite4+Nvue+Pinia
- UI組件庫:uv-ui+vk-uview
- 彈框組件:uaPopup(uniapp封裝多端彈框組件)
- 自定義組件:uaNavbar+uaTabbar組件
- 本地緩存:pinia-plugin-unistorage
- 編譯支持:H5+小程序+APP端

大家如果對uniapp+vue3搭建跨端項目感興趣,可以去看看之前的分享文章。
http://www.rzrgm.cn/xiaoyan2017/p/17487018.html

uniapp-welive擁有絲滑般滑動體驗,全屏沉浸式tabbar切換,微型迷你播放時間進度條等功能。


項目結構目錄




















目前uniapp-dylive項目,已經同步到我的原創作品集,有需要的可以去看看。
uniapp跨端自定義navbar+tabbar組件




在components目錄下自定義navbar、tabbar組件。

之前有過一篇分享文章,感興趣的話也可以去看看,這里就不詳細的介紹實現過程了。
http://www.rzrgm.cn/xiaoyan2017/p/14978408.html
uniapp+vue3小視頻模塊

uni-welive項目短視頻模塊采用全屏沉浸式上下無縫銜接滑動效果。整體分為頂部固定tabs+視頻區+底部視頻信息浮層三大模塊。
<ua-layout> <view class="ua__swipervideo flex1"> <swiper class="ua__swipervideo-wrap flex1" :current="currentVideo" vertical :circular="true" :duration="200" @change="handleChange" @transition="handleTransition" > <swiper-item v-for="(item, index) in videoList" :key="index"> <video class="ua__swipervideo-player flex1" :id="'uplayer' + index" :src="item.src" :danmu-list="item.danmu" :enable-danmu="true" :controls="false" :loop="true" :autoplay="index == currentVideo" :show-center-play-btn="false" object-fit="contain" @click="handleClickVideo" @play="isPlaying=true" @timeupdate="handleTimeUpdate" :style="{'width': `${winWidth}px`, 'height': `${winHeight}px`}" > </video> <!-- 浮層模塊 --> <view class="ulive__video-float__info flexbox flex-col"> <view class="flexbox flex-row flex-alignb"> <!-- 左側 --> <view class="vdinfo__left flex1 flexbox flex-col"> <view class="ltrow danmu flexbox" @click="handleOpenDanmu"><text class="danmu-txt">彈</text><uv-icon class="ico" name="edit-pen" color="#fff" size="14" /></view> <view class="ltrow"><text class="ait">@{{item.author}}</text></view> <view class="ltrow"><text class="desc">{{item.desc}}</text></view> </view> <!-- 右側操作欄 --> <view class="vdinfo__right flexbox flex-col"> <view class="rtbtn avatar flexbox flex-col"> <view class="ubox"><image class="uimg" :src="item.avatar" mode="aspectFill" /></view> <view class="btn flexbox" :class="{'active': item.isFollow}" @click="handleFollow(index)"><uv-icon :name="item.isFollow ? 'checkmark' : 'plus'" :color="item.isFollow ? '#ff007f' : '#fff'" size="11" /></view> </view> <view class="rtbtn flexbox flex-col" @click="handleLiked(index)"><uv-icon name="heart-fill" :color="item.isLike ? '#ff007f' : '#fff'" size="40" /><text class="num">{{item.likeNum+(item.isLike ? 1 : 0)}}</text></view> <view class="rtbtn flexbox flex-col" @click="handleOpenComment(index)"><uv-icon name="chat-fill" color="#fff" size="40" /><text class="num">{{item.replyNum}}</text></view> <view class="rtbtn flexbox flex-col"><uv-icon name="star-fill" color="#fff" size="40" /><text class="num">{{item.starNum}}</text></view> <view class="rtbtn flexbox flex-col" @click="handleOpenShare(index)"><uv-icon name="share-fill" color="#fff" size="40" /><text class="num">{{item.shareNum}}</text></view> </view> </view> </view> </swiper-item> </swiper> <!-- 固定tabs(脫離滑動區) --> <view class="ulive__video-header__tabs" :style="{'margin-top': `${menuBarT}px`}"> <uv-tabs :current="tabsCurrent" :list="tabsList" /> </view> <!-- 播放暫停按鈕 --> <view v-if="!isPlaying" class="ua__swipervideo-playbtn" :style="{'left': `${winWidth/2}px`, 'top': `${winHeight/2}px`}" @click="handleClickVideo"> <text class="ua__swipervideo-playico welive-icon welive-icon-play nvueicon"></text> </view> <!-- 播放mini進度條 --> <view class="ua__swipervideo-progress" :style="{'width': `${winWidth}px`}"><view class="ua__swipervideo-progressbar" :style="{'width': `${progressBar}px`}"></view></view> </view> <template #footer> <ua-tabbar bgcolor="transparent" color="rgba(255,255,255,.7)" :border="false" :dock="false" transparent z-index="1000" /> </template> </ua-layout>
uni-welive支持自動播放、發送視頻彈幕、底部Mini視頻播放進度條等功能。

<script setup> import { ref, computed, getCurrentInstance } from 'vue' import { onShow, onHide } from '@dcloudio/uni-app' import { getRandomColor } from '@/utils' // ... const { globalData } = getApp() const menuBarT = ref(globalData.menu?.top || globalData.statusBarH) const winWidth = ref(globalData.screenWidth) const winHeight = ref(globalData.screenHeight) const tabsList = ref([ { name: '推薦', count: 5 }, { name: '關注' }, { name: '同城' } ]) const tabsCurrent = ref(0) // 視頻參數 const currentVideo = ref(0) const isPlaying = ref(false) const clickNum = ref(0) const clickTimer = ref(null) const progressBar = ref(0) // 視頻源 const videoList = ref(videoJson) const danmuEditor = ref('') const isVisibleDanmu = ref(false) const commentRef = ref(null) const shareRef = ref(null) // ... /** * ====================== 視頻播放模塊 ====================== */ // 創建并返回 video 上下文 videoContext 對象 const getVideoContext = () => { // return uni.createVideoContext(`uplayer${currentVideo.value}`, this) return uni.createVideoContext(`uplayer${currentVideo.value}`, getCurrentInstance()) } // 垂直滑動視頻,滑動改變時會觸發 change 事件 const handleChange = (e) => { const index = e.detail.current progressBar.value = 0 handleReset() currentVideo.value = index // 播放 handlePlay() } // 播放 const handlePlay = () => { console.log('video play') let video = getVideoContext() if(!video) return video.play() isPlaying.value = true } // 暫停 const handlePause = () => { console.log('video pause') let video = getVideoContext() if(!video) return video.pause() isPlaying.value = false } // 重置播放 const handleReset = () => { console.log('video reset') let video = getVideoContext() if(!video) return video.pause() video.seek(0) video.stop() isPlaying.value = false } // 監聽播放進度條 const handleTimeUpdate = (e) => { let { currentTime, duration } = e.detail progressBar.value = parseInt((currentTime / duration).toFixed(2) * parseInt(winWidth.value)) } // 點擊視頻(監聽單雙擊) const handleClickVideo = () => { console.log('video click') clearTimeout(clickTimer.value) clickNum.value++ clickTimer.value = setTimeout(() => { if(clickNum.value >= 2) { console.log('double click') }else { if(isPlaying.value) { handlePause() }else { handlePlay() } } clickNum.value = 0 }, 200) } /** * ====================== 其它功能模塊 ====================== */ // 打開彈幕彈框 const handleOpenDanmu = () => { isVisibleDanmu.value = true } // 關閉彈幕彈框 const handleCloseDanmu = () => { uni.hideKeyboard() isVisibleDanmu.value = false danmuEditor.value = '' } // 發送彈幕 const handleSendDanmu = () => { let video = getVideoContext() if(!video) return video.sendDanmu({ text: danmuEditor.value, color: getRandomColor() }) handleCloseDanmu() } // 打開評論框 const handleOpenComment = (index) => { commentRef.value.open() } // ... </script>
注意:uniapp+vue3獲取創建視頻上下文實例,由于vue3沒有this,只能通過getCurrentInstance獲取上下文實例。
uni.createVideoContext(`uplayer${currentVideo.value}`, getCurrentInstance())
uniapp+vue3直播模塊

直播模塊整體分為頂部信息+直播流視頻區+滾動消息(加入直播間+送禮物+講解商品)+底部toolbar欄
<ua-layout> <view class="ua__swipervideo flex1"> <swiper class="ua__swipervideo-wrap flex1" :current="currentLive" vertical @change="handleChange" > <swiper-item v-for="(item, index) in liveList" :key="index"> <video class="ua__swipervideo-player flex1" :id="'uplayer' + index" :src="item.src" :controls="false" :loop="true" :autoplay="index == currentLive" :show-center-play-btn="false" object-fit="contain" :style="{'width': `${winWidth}px`, 'height': `${winHeight}px`}" > </video> <!-- 浮層模塊 --> <swiper class="ulive__swiperscreen flex1" :current="1"> <!-- 清屏 --> <swiper-item> 第一屏 </swiper-item> <swiper-item> <!-- 頂部區域 --> <view class="ulive__headlayer" :style="{'top': menuBarT+'px'}"> <!-- logo+關注 --> <view class="ulive__hd-liveinfo flexbox flex-row flex-alignc"> <view class="ulive__hd-avatar ulive__mask flex-alignc"> <image class="logo" :src="item.logo" mode="widthFix" /> <view class="flex1 flexbox flex-col ml-10"> <text class="name">{{item.name}}</text> <text class="zan">{{item.likeNum}}本場點贊</text> </view> <view class="btn flexbox flex-row flex-alignc" :class="{'active': item.isFollow}" @click="handleFollow(index)"><text class="btntext" :class="{'active': item.isFollow}">{{item.isFollow ? '已關注' : '關注'}}</text></view> </view> <view class="ulive__hd-onlineuser flex1"> <uv-icon name="close" color="#fff" @click="handleLiveQuit" /> </view> </view> <view class="ulive__hd-livewrap flexbox flex-row"> <view class="ulive__hd-livewrap__left flex1 flexbox flex-col"> <view class="ulive__hd-livewrap__tags flexbox flex-row"> <view class="ulive__roundwrap ulive__mask"> <uv-icon name="shopping-cart" color="#ffdd1a" /><text class="ulive__roundtext">服飾鞋包榜第1名</text> </view> <view class="ulive__roundwrap ulive__mask ml-10"> <uv-icon name="level" color="#ffdd1a" /><text class="ulive__roundtext">小時榜</text> </view> </view> <!-- 紅包+福袋倒計時 --> <view class="ulive__hd-livewrap__redpacket flexbox flex-row"> <view class="ulive__redpacket-item ulive__mask" @click="handleOpenRedpacket(1)"> <image class="ulive__redpacket-image" src="/static/icon-fudai.png" mode="widthFix" /><text class="ulive__redpacket-time">04:49</text> </view> <view class="ulive__redpacket-item ulive__mask" @click="handleOpenRedpacket(2)"> <image class="ulive__redpacket-image" src="/static/icon-hb.png" mode="widthFix" /><text class="ulive__redpacket-time">04:49</text> </view> <view class="ulive__redpacket-item ulive__mask center"> <image class="ulive__redpacket-image" src="/static/icon-rotate.png" mode="widthFix" /><text class="ulive__redpacket-time">04:49</text> </view> </view> </view> <view class="ulive__hd-livewrap__right flexbox flex-col"> <view class="ulive__roundwrap ulive__mask mr-20"> <uv-icon name="kefu-ermai" color="#fff" /><text class="ulive__roundtext ml-5">后臺</text> </view> </view> </view> </view> <!-- 底部區域 --> <view class="ulive__footlayer"> <!-- 商品提示層 --> <view class="ulive__ft-livewrap-placeholder animated fadeIn"> <view class="ulive__ft-livewrap-hotbuy flexbox flex-row"> <image class="gimg" :src="item.poster" mode="aspectFill" /> <view class="ginfo flex1"> <view class="flexbox flex-row"><text class="user c-ffdd1a">Andy</text><text class="c-fff">等{{item.saleNum}}人在購買</text></view> <text class="gdesc clamp1">{{item.desc}}</text> </view> <view class="btn"><text class="btntext">去購買</text></view> </view> </view> <!-- 加入直播間/送禮物提示 --> <view class="ulive__ft-livewrap-animateview flexbox flex-col"> <view class="ulive__ft-livewrap-animatejoin ulive__ft-livewrap-placeholder"> <view v-if="joinRoomData" class="ulive__ft-livewrap-joinroom"><text class="ulive__ft-livewrap-joinroom__text">歡迎{{joinRoomData}}加入了直播間</text></view> </view> <!-- 送禮物 --> <view class="ulive__ft-livewrap-animategift ulive__ft-livewrap-placeholder"> <view v-if="!isEmpty(sendGiftData)" class="ulive__ft-livewrap-activegift flexbox flex-row flex-alignc"> <image class="avatar" :src="sendGiftData.avatar" /> <view class="info flex1"><text class="name">{{sendGiftData.user}}</text><text class="desc">送出</text></view> <image class="gift" :src="sendGiftData.pic" /> </view> </view> </view> <!-- 聊天浮層+商品講解 --> <view class="ulive__ft-livewrap-mixinview flexbox flex-row"> <!-- 聊天消息 --> <view class="ulive__ft-livewrap-chats flex1"> <scroll-view class="ulive__ft-livewrap-chats__scrollview flex1" scroll-y show-scrollbar="false" :scroll-into-view="scrollToView" :lower-threshold="5" @scroll="handleMsgScroll" @scrolltolower="handleMsgScrollLower"> <block v-for="(msgitem, msgidx) in item.message" :key="msgidx"> <view v-if="msgitem.type == 'notice'" class="notice" :id="`msg-${msgitem.id}`"><view class="item"><text class="noticetext">{{msgitem.content}}</text></view></view> <view v-else-if="msgitem.type == 'gift'" class="gift" :id="`msg-${msgitem.id}`"> <view class="item"> <text class="giftuser">{{msgitem.user}}</text> <text class="gifttext">送出了{{msgitem.content}}</text> <image class="giftimg" :src="msgitem.img" mode="widthFix" /> <text class="giftnum">x{{msgitem.num}}</text> </view> </view> <view v-else class="msg" :id="`msg-${msgitem.id}`"> <view class="item"> <text v-if="msgitem.tag" class="tag">{{msgitem.tag}}</text> <text class="user">{{msgitem.user}}</text> <text class="text" :style="[fixTextStyle]">{{msgitem.isbuy ? '正在購買' : msgitem.content}}</text> <text v-if="msgitem.isbuy" class="tag tag-buy">去購買</text> </view> </view> </block> </scroll-view> <view v-if="!isEmpty(msgUnread)" class="ulive__ft-livewrap-chats__unread" @click="handleMsgIsRead"><text class="c-eb4868 fs-24">{{msgUnread.length}}條新消息</text></view> </view> <!-- 商品講解 --> <view v-if="isVisibleGoodsTalk" class="ulive__ft-livewrap-activegoods animated fadeInRight" id="goodsTalkID"> <view class="ulive__ft-livewrap-activegoods__hotsale flexbox flex-row"> <image class="fimg" src="/static/icon-hot.png" mode="widthFix" /><text class="c-fff fs-32">熱賣 x{{item.saleNum}}</text> </view> <swiper class="ulive__ft-livewrap-activegoods__swiper"> <swiper-item> <view class="ulive__ft-livewrap-activegoods__card"> <view class="gwrap" @click="toGoodsDetail"> <image class="gimg" :src="item.poster" mode="aspectFill" /> <view class="waves"><text class="c-fff fs-24">講解中</text></view> <view class="close" @click.stop="isVisibleGoodsTalk=false"><uv-icon name="close-circle-fill" color="rgba(0, 0, 0, .3)" size="14" /></view> </view> <view class="ginfo flexbox flex-col"> <text class="clamp1 fs-24">{{item.desc}}</text> <text class="clamp1 fs-24 c-eb4868">7天無理由退貨</text> </view> <view class="btn flexbox flex-row"><text class="flex1 c-fff fs-28">¥79.00</text><text class="qiang">搶</text></view> </view> </swiper-item> </swiper> </view> </view> <!-- 工具欄 --> <view class="ulive__ft-livewrap-toolbar flexbox flex-row"> <view class="editorwrap flex1 flexbox flex-row flex-alignc"> <view class="flex1" @click="handleOpenChatbox"><text class="editorwrap-text">說點什么...</text></view> </view> <view class="btnwrap flexbox flex-row"> <view class="btn flexbox" @click="handleOpenMenus"><uv-icon name="grid" color="#3c9cff" size="22" /></view> <view class="btn flexbox" @click="handleOpenGoods(item)"><uv-icon name="shopping-cart-fill" color="#ffaa00" size="24" /></view> <view class="btn flexbox" @click="handleOpenGifts"><uv-icon name="gift" color="#ff0ad3" size="22" /></view> <view class="btn flexbox"><uv-icon name="more-dot-fill" color="#efe9ff" size="18" /></view> </view> </view> </view> </swiper-item> </swiper> </swiper-item> </swiper> </view> </ua-layout>
Okay,以上就是uniapp+vue3開發仿微信/抖音短視頻+直播商城的一些知識分享,希望對大家有所幫助哈~~??
最后附上兩個最近實例項目
Electron27+React18跨平臺macos桌面系統管理:http://www.rzrgm.cn/xiaoyan2017/p/17850653.html
Uniapp+Vue3仿制chatgpt會話實例:http://www.rzrgm.cn/xiaoyan2017/p/17507581.html


浙公網安備 33010602011771號