一、我的项目概述

uniapp-ttlive 一款基于uni-app+uview-ui+vue.js+uapopup等技术混合开发的多端仿造抖音短视频/直播/聊天我的项目。反对全屏沉迷式、高低滑动切换视频等性能。

二、预览成果

如下图:在h5、小程序、APP端编译成果

三、编码技术

  • 编码器/技术:HbuilderX3.1.21+Uniapp+Nvue+Vuex+Uapopup
  • UI组件库:uView-ui / uni-ui
  • 矢量图标库:iconfont字体图标
  • 弹窗组件:UApopup 基于uni-app封装跨端弹窗组件
  • 自定义导航条+底部菜单栏
  • 编译反对:H5+小程序+APP端

四、性能个性

✅ 反对全屏沉迷式通明模式
✅ 顺滑的高低滑动体验
✅ 迷你工夫进度条
✅ 自定义组件反对Nvue页面

五、目录构造/编译

我的项目中应用的组件库是uview-ui,多平台疾速开发UI框架。

main.js配置

import Vue from 'vue'import App from './App'import uView from 'uview-ui'Vue.use(uView)import API from '@/common/request'Vue.prototype.$api = API// 引入状态治理import Store from './store'Vue.prototype.$store = StoreVue.config.productionTip = falseApp.mpType = 'app'// #ifdef APP-PLUSplus.navigator.closeSplashscreen()// #endifconst app = new Vue({    ...App})app.$mount()

思考到Nvue页面不反对prototype原型全局挂载,改为应用globalData来管制。

/** * 主入口配置 * @author xiaoyan */<script>    export default {        globalData: {            // 全局设置状态栏和导航栏高度            statusBarH: 0,            customBarH: 0,        },        onLaunch: function() {            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自定义navbar+tabbar组件

大家看到的顶部导航条及底部菜单栏,是全新开发的反对nvue组件。

<!-- 导航条模板 --><template>    <view class="ua__navbar">        <view class="ua__navbar-wrap" :class="{'custom': custom, 'fixed': fixed || transparent}"            :style="{'height': customBarH + 'px', 'padding-top': (custom ? statusBarH : 0) + 'px', 'background': bgcolor, 'color': color, 'z-index': zIndex}">            <!-- //左侧 (返回) -->            <view class="action navbar-action__left" v-if="back && back!='false'" @click="onBack">                <template v-if="$slots.back">                    <slot name="back" />                </template>                <template v-else><text class="iconfont nvuefont"                        :style="{'color': color}">{{'\ue84c'}}</text></template>                <slot name="backText" />            </view>            <slot name="left" />            <!-- //题目 -->            <view v-if="!search" class="navbar-title" :class="{'center': center}">                <template v-if="$slots.title">                    <slot name="title" />                </template>                <template v-else><text :style="{'color': color}">{{title}}</text></template>            </view>            <!-- //搜寻框 -->            <view v-if="search" class="action navbar-action__search">                <slot name="search" />            </view>            <!-- //右侧 -->            <view class="action navbar-action__right">                <slot name="right" />            </view>        </view>    </view></template>

更多具体介绍,大家能够去参看之前的分享文章。
uniapp自定义导航栏+菜单栏组件

uniapp封装自定义弹框组件

uaPopup全新封装的一款多端弹窗组件,反对nvue页面。

反对20+自定义参数,反对标签式+函数式两种调用形式。

<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>

如果感兴趣的话,能够参看之前的这篇分享文章。
uaPopup弹窗:一款uniapp的平台弹窗组件

uniapp短视频/直播

我的项目中小视频/直播页面整体分为顶部导航区+视频区+底部区三个局部。
视频能够高低滑动切换,反对播放/暂停,点赞评论

<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>

短视频底部新增了一条工夫播放进度条。

// 播放进度变动时触发handleTimeUpdate(e) {    let { currentTime, duration } = e.detail        this.progressBar = parseInt((currentTime / duration).toFixed(2) * parseInt(this.winWidth))},
<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>

OKey,基于Uniapp开发仿造抖音短视频/直播实例就分享到这里。

最初附上一个最新我的项目案例
https://segmentfault.com/a/11...