uni-app跨端开发实战:仿抖音短视频直播聊天应用实现

Viewed 0

uni-app+vue3+pinia2+uv-ui实战:仿抖音app小视频+直播+聊天系统

本项目基于uni-app与Vue3技术栈,结合Pinia2状态管理和uv-ui组件库,从零开始搭建了一个仿抖音的跨端应用。该应用集成了短视频浏览、实时聊天与直播带货功能,支持全屏沉浸式体验,并能够编译运行到H5、小程序及App端。

技术栈概览

  • 开发工具:HbuilderX 4.66
  • 核心框架:Uniapp + Vue3 + Vite5 + Nvue
  • 状态管理:Pinia2,配合pinia-plugin-unistorage进行本地缓存持久化
  • UI组件:uv-ui与vk-uview组件库
  • 自定义组件:封装了uaNavbar导航栏与uaTabbar底部栏组件
  • 弹框组件:使用uaPopup实现多端统一的弹框
  • 多端支持:可编译至H5、各平台小程序及App端

项目框架结构

项目采用uni-app结合Vue3的Composition API与setup语法进行开发,确保了代码的模块化与可维护性。整体架构清晰,便于功能扩展与多端适配。

短视频模块实现

短视频模块模仿了抖音的全屏沉浸式上下滑动切换效果。界面主要分为顶部固定标签栏、视频播放区以及底部视频信息浮层三大部分。

核心实现利用了uni-app的swiper组件进行垂直滑动管理,每个swiper-item内嵌video组件用于播放。视频播放器支持弹幕、循环播放与自动播放,并实现了自定义的迷你进度条拖拽控制。

以下为部分关键代码示例:

<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"
                >
                </video>
                <!-- 浮层信息展示区,包含作者、描述、点赞、评论等交互操作 -->
                <view class="ulive__video-float__info flexbox flex-col">
                    <!-- 信息展示布局 -->
                </view>
            </swiper-item>
        </swiper>
        <!-- 顶部固定标签栏 -->
        <view class="ulive__video-header__tabs">
            <uv-tabs :current="tabsCurrent" :list="tabsList" />
        </view>
        <!-- 播放控制与进度条 -->
        <view v-if="!isPlaying" class="ua__swipervideo-playbtn" @click="handleClickVideo">
            <text class="ua__swipervideo-playico welive-icon welive-icon-play nvueicon"></text>
        </view>
        <view v-if="sliderValue > 0" class="ua__swipervideo-slider">
            <slider
                :value="sliderValue"
                :step="sliderStep"
                :max="sliderMaxValue"
                activeColor="#fff"
                backgroundColor="rgba(255,255,255,.2)"
                @changing="onSliderChanging"
                @change="onSliderChange"
            />
        </view>
    </view>
</ua-layout>

进度条交互通过监听视频的timeupdate事件,并绑定slider组件的changing和change事件来实现:

// 监听播放进度
const handleTimeUpdate = (e) => {
    let { currentTime, duration } = e.detail
    sliderValue.value = currentTime
    sliderMaxValue.value = duration
    sliderStep.value = currentTime / duration
}

// 滑动条拖动处理
const onSliderChanging = (e) => {
    sliderValue.value = e.detail.value
    handlePause()
}

const onSliderChange = (e) => {
    sliderValue.value = e.detail.value
    let video = getVideoContext()
    if(!video) return
    video.seek(sliderValue.value)
    handlePlay()
}

聊天模块

聊天功能模块迁移自之前已实现的uni-app仿微信聊天项目。它提供了完整的实时消息交互界面,支持文本、语音等多种消息类型,并集成了朋友圈动态等社交功能。

直播模块详解

直播模块模拟了电商直播场景,界面分为顶部主播信息区、直播视频流、滚动消息区(展示用户加入、送礼、商品讲解)以及底部互动工具栏。

实现上,同样使用swiper管理多个直播流,并在浮层中通过嵌套swiper实现“清屏”与“互动层”的切换。互动层包含:

  1. 顶部区域:展示主播头像、关注状态、直播间标签(如小时榜)以及红包、福袋等互动入口。
  2. 底部区域
    • 商品提示栏:显示正在讲解的热卖商品。
    • 动态消息区:使用scroll-view展示用户加入、送礼等滚动消息。
    • 商品讲解浮层:以卡片形式突出显示当前讲解的商品详情。
    • 工具栏:提供输入框、购物车、礼物菜单等互动入口。

核心结构代码如下:

<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"
                    object-fit="contain"
                >
                </video>
                <!-- 直播互动浮层 -->
                <swiper class="ulive__swiperscreen flex1" :current="1">
                    <swiper-item>清屏视图</swiper-item>
                    <swiper-item>
                        <view class="ulive__headlayer"><!-- 顶部信息区 --></view>
                        <view class="ulive__footlayer">
                            <!-- 商品提示 -->
                            <view class="ulive__ft-livewrap-placeholder">
                                <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>
                            <!-- 聊天与商品讲解区 -->
                            <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>
                                        <!-- 消息列表,区分通知、礼物和普通聊天 -->
                                        <block v-for="(msgitem, msgidx) in item.message" :key="msgidx">
                                            <view v-if="msgitem.type == 'notice'" class="notice"><text class="noticetext">{{msgitem.content}}</text></view>
                                            <view v-else-if="msgitem.type == 'gift'" class="gift">
                                                <text class="giftuser">{{msgitem.user}}</text>
                                                <text class="gifttext">送出了{{msgitem.content}}</text>
                                            </view>
                                            <view v-else class="msg">
                                                <text v-if="msgitem.tag" class="tag">{{msgitem.tag}}</text>
                                                <text class="user">{{msgitem.user}}</text>
                                                <text class="text">{{msgitem.isbuy ? '正在购买' : msgitem.content}}</text>
                                            </view>
                                        </block>
                                    </scroll-view>
                                </view>
                                <!-- 商品讲解侧边栏 -->
                                <view v-if="isVisibleGoodsTalk" class="ulive__ft-livewrap-activegoods">
                                    <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>
                                        <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>
                                </view>
                            </view>
                            <!-- 底部工具栏 -->
                            <view class="ulive__ft-livewrap-toolbar flexbox flex-row">
                                <view class="editorwrap flex1" @click="handleOpenChatbox"><text class="editorwrap-text">说点什么...</text></view>
                                <view class="btnwrap flexbox flex-row">
                                    <view class="btn" @click="handleOpenMenus"><uv-icon name="grid" color="#3c9cff" size="22" /></view>
                                    <view class="btn" @click="handleOpenGoods(item)"><uv-icon name="shopping-cart-fill" color="#ffaa00" size="24" /></view>
                                    <view class="btn" @click="handleOpenGifts"><uv-icon name="gift" color="#ff0ad3" size="22" /></view>
                                </view>
                            </view>
                        </view>
                    </swiper-item>
                </swiper>
            </swiper-item>
        </swiper>
    </view>
</ua-layout>

总结

本项目展示了利用uni-app和Vue3生态系统构建功能复杂的跨端移动应用的可行性。通过封装自定义组件(如导航栏、底部栏)、集成状态管理以及优化多端兼容性,成功实现了仿抖音的核心交互体验,包括丝滑的短视频切换、实时聊天互动与完整的直播带货流程。该实践为开发者提供了一个可参考的跨端开发范例。

0 Answers