uniLive:基于uni-app多端直播+短视频+聊天实战|uniApp仿抖音

2021年9月28日 423点热度 0人点赞 0条评论

图片


uniLive 一款基于uniapp开发的跨平台小视频直播项目。

使用uniapp+uview-ui+uapopup等技术开发的一款多设备短视频/直播案例。支持上下滑动切换短视频、点赞/评论/送礼物、聊天等功能。


图片


  • 整屏沉浸式透明悬浮模式;
  • 丝滑般的上下滑动切换体验;
  • mini短视频时间进度条;
  • 支持小视频播放/暂停,上下滑动切换;
  • 支持Nvue页面的全端自定义组件;


图片


采用了HbuilderX3.1.21编码器开发,Nvue原生页面解决video层级问题,自定义uaNavbar uaTabbar uaPopup 组件全面支持Nvue页面。搭配uview-uiuni-ui组件库。可编译至h5+小程序+APP端

图片


项目结构目录


图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

配置入口文件

import Vue from 'vue'import App from './App'
import uView from 'uview-ui'Vue.use(uView)
// 引入状态管理import Store from './store'Vue.prototype.$store = Store
const app = new Vue({ ...App})app.$mount()

考虑到Nvue页面不支持prototype全局挂载模式,改用globalData方式定义全局变量。设置顶部导航条及状态栏高度


<script>  export default {    globalData: {      // 全局设置状态栏和导航栏高度      statusBarH: 0,      customBarH: 0,    },    onLaunch: function() {      console.log('App Launch')
let token = uni.getStorageSync('token') if(!token) { uni.navigateTo({ url: '/pages/auth/login' }) }
uni.getSystemInfo({ success: (e) => { // 获取手机状态栏高度 let statusBar = e.statusBarHeight let customBar
// #ifndef MP customBar = statusBar + (e.platform == 'android' ? 50 : 45) // #endif
// #ifdef MP-WEIXIN // 获取胶囊按钮的布局位置信息 let menu = wx.getMenuButtonBoundingClientRect() // 导航栏高度 = 胶囊下距离 + 胶囊上距离 - 状态栏高度 customBar = menu.bottom + menu.top - statusBar // #endif
// #ifdef MP-ALIPAY customBar = statusBar + e.titleBarHeight // #endif
// 兼容nvue写法(H5/小程序/APP/APP-Nvue) this.globalData.statusBarH = statusBar this.globalData.customBarH = customBar } }) }, onShow: function() { console.log('App Show') }, onHide: function() { console.log('App Hide') } }</script>

uniapp自定义组件


项目中的顶部导航条、底部tabbar菜单及全局uaPopup弹窗均是自定义组件实现功能。

图片

图片

图片

图片

其中uaPopup弹窗支持函数+组件式混合调用。

<ua-popup v-model="isVisibleConfirm" shadeClose="false" title="标题" xclose z-index="1001"    content="<div style='color:#ff557f;padding:20px 40px;'>预测未来的最好办法是自己亲手创造未来!</div>"    :btns="[        {text: '取消', click: handleCancel},        {text: '确定', style: 'color:#00aa00;', click: handleOk},    ]"/>
<script>export default {    methods: {        handleOk() {            let $ua = this.$refs.uapopup            $ua.open({                content: '人生漫漫,且行且珍惜',                customStyle: {'background-color': 'rgba(170, 0, 127, 0.6)', 'color': '#fff'},                time: 3,                onClose() {                    $ua.open({                        type: 'android',                        content: '<div style="color:#aa007f">一切都将一去杳然,任何人都无法将其捕获。</div>',                        customStyle: {'width': '210px'},                        btns: [                            {                                text: '关闭',                click() {                                    $ua.close()                                }                            },                            {                                text: '确定',                                style: 'color:#00aa00;',                                click() {                                    // ...                                }                            }                        ]                    })                }            })        }    }}</script>

具体的实现方式,这里就不详细介绍了。感兴趣的话可以去看看之前的分享文章。组件已经开源到插件市场,有需要的可以一次性拿走使用。
uniapp实现全端自定义导航条+Tabbar组件
uniapp跨端弹窗组件[支持h5+小程序+APP]

uniapp短视频|直播


项目中短视频和直播页面分为顶部导航条、视频区域及底部菜单栏三个模块。

并且均是沉浸式悬浮在video视频上面。

图片

视频底部有一条mini播放进度条,显示当前播放进度。

<view v-if="currentTab == 2" class="ua__tabcnt-recommend">    <swiper class="ua__vdplayer-swiper flex1" :current="currentVideo" vertical @change="handleSwipeVertical">        <swiper-item v-for="(item, index) in videoList" :key="index">            <!-- 视频模块 -->            <view class="ua__vdplayer-video flex1">                <video class="vdplayer" :id="'vdplayer' + index" :ref="'vdplayer' + index"                     :src="item.src"                    :controls="false" :loop="true" :show-center-play-btn="false" object-fit="fill"                    :autoplay="index == currentVideo"                    @play="isPlaying=true" @timeupdate="handleTimeUpdate"                    :style="{'width': winWidth, 'height': winHeight}"                >                </video>                <view class="ua__vdplayer-playwrap" @click="handleVideoClicked"><view v-if="!isPlaying" class="ua__vdplayer-playbtn"><text class="iconfont">{{`\ue607`}}</text></view></view>            </view>            <!-- 信息模块 -->            <view class="ua__vdplayer-info flexbox flex-col">                <view class="flexbox flex-row flex-alignb">                    <!-- //左侧信息 -->                    <view class="vdinfo__left flex1">                        <view class="ltitem uavatar flexbox flex-row">                            <navigator url="#" class="flexbox flex-alignc flex-row"><image class="uimg" :src="item.avatar" /><text class="uname">{{item.author}}</text></navigator>                            <view class="flexbox btn" :class="{'actived': item.isFollow}" @click="handleFollow(index)"><text class="btn-text">{{item.isFollow ? '已关注' : '关注'}}</text></view>                        </view>                        <view v-if="item.topic" class="ltitem flexbox flex-row">                            <view class="kw" v-for="(kw, index2) in item.topic" :key="index2"><text class="lbl">#{{kw}}</text></view>                        </view>                        <view class="ltitem"><text class="desc">{{item.desc}}</text></view>                    </view>                    <!-- //右侧按钮 -->                    <view class="vdinfo__right flexbox flex-col">                        <view class="rtitem ball" v-if="item.goods&&item.goods.length > 0" @click="handleShowGoodsPopup(item.goods)"><text class="icon iconfont">{{`\ue734`}}</text></view>                        <view class="rtitem" :class="{'isliked': item.isLike}" @click="handleLiked(index)"><text class="icon iconfont">{{`\ue635`}}</text><text class="num">{{item.likeNum+(item.isLike ? 1 : 0)}}</text></view>                        <view class="rtitem" @click="showReplyPopup = true"><text class="icon iconfont">{{`\ue632`}}</text><text class="num">{{item.replyNum}}</text></view>                        <view class="rtitem" @click="showSharePopup = true"><text class="icon iconfont">{{`\ue63b`}}</text><text class="num">{{item.shareNum}}</text></view>                    </view>                </view>            </view>        </swiper-item>    </swiper>    <!-- 底部播放进度条 -->    <view class="ua__vdplayer-progress"><view class="bar" :style="{'width': progressBar+'px'}"></view></view></view>
<script>  const app = getApp()  import videoJSON from '@/mock/videolist.js'
export default { data() { return { // 导航栏高度 customBarHeight: app.globalData.customBarH, navbarBgcolor: '#21252b', tabbarBgcolor: '#21252b',
tabNavLs: [ {label: '附近动态', badge: 5, lists: []}, {label: '关注', lists: []}, {label: '推荐', dot: true, lists: []}, ], // 当前选项卡 currentTab: 0,
// 当前视频索引 currentVideo: 0, // 视频数据 videoList: videoJSON, // 视频是否播放中 isPlaying: false, // 点击次数 clickNum: 0, // 视频播放进度条 progressBar: 0, clickTimer: null,
// 屏幕宽高 winWidth: '', winHeight: '',
popupGoodsList: [], showGoodsPopup: false, showReplyPopup: false, showSharePopup: false, } }, watch: { currentTab(val) { this.changeTabPanel(val) } }, computed:{ customBarMargin() { return `margin-top: ${this.customBarHeight}px` } }, created() { // 引入iconfont字体 // #ifdef APP-NVUE const domModule = weex.requireModule('dom') domModule.addRule('fontFace', { fontFamily: "nvueIcon", 'src': "url('/static/fonts/iconfont.ttf')" }); // #endif
let wW = uni.getSystemInfoSync().windowWidth let wH = uni.getSystemInfoSync().windowHeight this.winWidth = `${wW}px` this.winHeight = `${wH}px` }, methods: {
// 长按动态 handleDynamicMenu(e) { let points // #ifndef APP-NVUE points = [e.touches[0].clientX, e.touches[0].clientY] // #endif // #ifdef APP-NVUE points = [e.touches[0].screenX, e.touches[0].screenY] // #endif
this.$refs.uapopup.open({ type: 'contextmenu', follow: points, btns: [ {text: '不感兴趣'}, {text: '复制'}, { text: '举报', style: 'color:#f00;', click: () => { this.$refs.uapopup.close() } }, ], }) },
/* ++++++++++ { 视频播放模块 } ++++++++++ */ getVideoCtx() { // return this.$refs['vdplayer' + this.currentVideo][0] return uni.createVideoContext('vdplayer'+ this.currentVideo, this) },
// 垂直滑动视频 handleSwipeVertical(e) { let index = e.detail.current this.progressBar = 0 this.isPlaying = false let video = this.getVideoCtx() if(!video) return video.pause() // 重新开始 video.seek(0)
this.currentVideo = index
// 自动播放 this.handlePlay() },
handlePlay() { let video = this.getVideoCtx() if(!video) return video.play() this.isPlaying = true },
handlePause() { let video = this.getVideoCtx() if(!video) return video.pause() this.isPlaying = false },
// 点击视频(单击/双击) handleVideoClicked() { this.clickTimer && clearTimeout(this.clickTimer) this.clickNum++ this.clickTimer = setTimeout(() => { if(this.clickNum >= 2) { console.log('你双击了') }else { console.log('你单击了') if(this.isPlaying) { this.handlePause() }else { this.handlePlay() } } this.clickNum = 0 }, 250) },
... } }</script>

小视频支持上下滑动切换、播放/暂停、点赞/取消点赞、评论等功能。

? 以上就是基于uni-app+uview开发短视频的一些分享,希望大家喜欢。。

juejin.cn/post/697729…
来源:https://juejin.cn/post/7010195797174124557


点个在看你最好看

46530uniLive:基于uni-app多端直播+短视频+聊天实战|uniApp仿抖音

这个人很懒,什么都没留下

文章评论