10分钟快速上车短视频风口:基于uniapp框架创建自己的仿抖音短视APP
在今年也就是第48次发布的《中国互联网络发展状况统计报告》有这样一个数据,21年的上半年以来,我国我国网民规模达10.11亿,其中短视频用户达8.88亿。碎片化的生活场景下,短视频成为人们获取信息的重要渠道之一。可以看到这10亿互联网用户中,有接近九成都是短视频APP的用户。同时,截止2021年3月,中国短视频人均单日使用时长超过了两小时。在这一数据支撑下,国民对于短视频的粘性还在进一步上升。而5G时代的逐步铺开也是另一个短视频利好的因素。
面对短视频的风口,不少人想要成为内容提供者,也有不少人跃跃欲试想要推出自己的短视频APP程序成为下一个抖音,今天就以uniapp框架下的短视频插件实例分享如何快速实现基础功能的仿抖音短视频插件。
技术实现
- 开发环境:HbuilderX + nodejs
- 技术框架:uniapp + vue2.x
- 测试环境:App端(Android + IOS)
- 代码:开源
- SDK: 智密原生仿抖音上下滑动插件仿抖音短视频插件-原生控制视频上下滑动-智密科技 - DCloud 插件市场
效果预览
项目实践
首先需要开发者提前准备好“技术实现”部分的环境并登录到DCloud中。此时打开智密原生仿抖音上下滑动插件的详情页(仿抖音短视频插件-原生控制视频上下滑动-智密科技 - DCloud 插件市场)
点击导入之后,系统将会自动打开hbx,并且提示新建导入项目,导入成功之后开发者将会看到这样的一个目录结构,这我们就创建完成基础项目。
试用插件
创建完成项目之后,根据uniapp的官方要求,我们并不能直接使用插件,我们还需要先申请试用,然后打包自定义基座才可以使用。
点击确认申请试用之后,我们还需要回到hbx中选择云端插件并且打包自定义基座,运行的时候我们也需要选择自定义基座,具体操作如下:
至此我们完成了打包自定义基座以及以自定义基座运行的方式了,接下来我们开始进入代码实战阶段。
代码实战
首先我们先看一下demo,demo中提供的/pages/ui/index.nvue是ui展示界面,这里我们可以来分析一下代码情况,笔者划分几个部分给大家分析一下。
界面控件
<view>
<asv_list_player ref="listPlayer" class="player"></asv_list_player>
<bottom-popover v-if="showBottomPopover" ref="popover" @close="onClosePopover">
<text>此处可以展示评论内容</text>
</bottom-popover>
</view>
在这里asv_list_player是展示短视频的主体,用于接受界面控件配置数据以及承载视频播放,上下滑动视频的控件,而bottom-popover是demo内部自带的底部弹出覆盖层,用于展示弹窗业务逻辑。
初始化控件
mounted() {
uni.$on('pause-video', () => {
this.asvListPlayer.pause()
this.showBottomPopover = false
})
this.$nextTick(e => {
// 创建jssdk示例
this.asvListPlayer = new asvListPlayer(this.$refs.listPlayer)
this.$refs.listPlayer.setScaleMode(0) let screenWidth = uni.getSystemInfoSync().screenWidth
// 这里开始是初始化界面布局信息
let views = [
asvListPlayer.getView('rightBox').isLayer().position(['right', 'bottom']).width(60).height('auto').marginRight(15).marginBottom(15)
.children([
asvListPlayer.getView('head').isImage().position(['right', 'bottom']).width(50).height(50).marginBottom(245).radius(30).toJSON(),
asvListPlayer.getView('like').isImage().position(['right', 'bottom']).width(50).height(45).marginBottom(185).radius(0).toJSON(),
asvListPlayer.getView('likeText').isText().position(['right', 'bottom']).width(50).height(20).marginBottom(165).textAlign('center').fontSize(14).toJSON(),
asvListPlayer.getView('commit').isImage().position(['right', 'bottom']).width(50).height(50).marginBottom(111).radius(0).toJSON(),
asvListPlayer.getView('commitText').isText().position(['right', 'bottom']).width(50).height(20).marginBottom(90).textAlign('center').fontSize(14).toJSON(),
asvListPlayer.getView('share').isImage().position(['right', 'bottom']).width(50).height(50).marginBottom(38).radius(0).toJSON(),
asvListPlayer.getView('shareText').isText().position(['right', 'bottom']).width(50).height(20).marginBottom(15).textAlign('center').fontSize(14).toJSON(),
])
.toJSON(),
asvListPlayer.getView('titleBox').isLayer().position(['left', 'bottom']).width(screenWidth * 0.6).height(100).bgc('#55000000').marginLeft(15).marginBottom(15).radius(10)
.children([
asvListPlayer.getView('userBox').isLayer().position(['left']).width('100%').height('auto').marginLeft(10).marginTop(10)
.children([
asvListPlayer.getView('userIcon').isImage().position('left').width(15).height(15).marginTop(3).radius(10).toJSON(),
asvListPlayer.getView('userName').isText().position('left').width('100%').height(20).lines(2).color('#ffffff').marginLeft(20).toJSON(),
])
.toJSON(),
asvListPlayer.getView('title').isText().position('left').width('100%').height('auto').color('#ffffff').marginLeft(10).marginTop(35).marginBottom(10).fontSize(14).marginRight(10).toJSON(),
])
.toJSON(),
]
this.asvListPlayer.setViewConfig({ views })
// 这是初始化视频数据
this.onRefresh();
// 这是初始化控件的监听器
this.asvListPlayer.on('onClick', this.onClick)
this.asvListPlayer.on('onLoadMore', this.onLoadMore)
this.asvListPlayer.on('onRefresh', this.onRefresh)
})
}

在demo中提供的mounted函数中,主要划分为3个阶段,对于3个阶段的代码注释我已经写在其中了。主要的流程是先使用用new asvListPlayer(this.$refs.listPlayer)的方式初始化仿抖音控件,然后通过提供的asvListPlayer.getView的方法构建界面布局对象,this.asvListPlayer.setViewConfig将布局信息绑定到控件中,然后通过初始化视频数据和初始化控件监听器完成业务逻辑。
初始化视频数据
上面我们提到demo用的是this.onRefresh()初始化视频数据,现在我们上俩段代码解析一下控件初始化视频数据都需要执行那些操作。
onRefresh() {
this.list = []
this.presetCur = 0
var datas = []
this.genData().forEach(item => {
let data = asvListPlayer.getItem(item.i)
.video(item.v)
.cover(item.c)
.bindImage('head', 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3533321036,2623280788&fm=15&gp=0.jpg', true)
.bindImage('like', item.like ? 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/c94c1a75-094a-482a-9acf-f1eefbd24792.png':'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/5a17a78c-f923-41f3-8916-271b4d5d528f.png')
.bindText('likeText', parseInt((item.i * 1 + 1) * (new Date().getTime()) / 1000000000000) + '.4w')
.bindImage('commit', 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/003910f2-92cf-40c5-9d8a-870401be6e41.png')
.bindText('commitText', parseInt((item.i * 1 + 1) * (new Date().getTime()) / 10000000000) + '')
.bindImage('share', 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/353edf1a-c2f1-481a-87fc-b9d5df98fb9e.png')
.bindText('shareText', '分享')
.bindText('userName', `UserName`)
.bindImage('userIcon', 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/89898f94-c3b8-4e14-9d06-7ccb1f27d3ab.png')
.bindText('title', `这是第${item.i * 1}个视频,悄悄是离别的笙箫,沉默是今晚的康桥,再别康桥,再见我终将逝去的青春,愿一切安好,愿你永远都在。`)
.toJSON()
datas.push(data)
})
this.asvListPlayer.loadDatas(datas)
}
点击并拖拽以移动
genData () {
let len = this.list.length
let presetDatas = [
{ v: 'http://txfile-30121.sz.gfp.tencent-cloud.com/1603991410685_8240447_29b1d0770d6cdf43a1dd1fd3a992f96f.mp4', c: 'http://txfile-30121.sz.gfp.tencent-cloud.com/1604043258739_635757_8fd725d85d2b42ad1a8d878ef286d0bf.png' },
{ v: 'http://txfile-30121.sz.gfp.tencent-cloud.com/1611758544058_4702548_5047449b104091e5dd3acfa00ed7eb99.mp4', c: 'http://txfile-30121.sz.gfp.tencent-cloud.com/1611758623279_1481920_89d5f27064f39fee56e663b050d28d8c.png' },
{ v: 'http://txfile-30121.sz.gfp.tencent-cloud.com/1604048716240_10046019_6566a337a503919c68f36a9fad9537b0.mp4', c: 'http://txfile-30121.sz.gfp.tencent-cloud.com/1604048732088_557815_c24e7f6276e650174494aa805fd7e45f.jpg' },
{ v: 'http://txfile-30121.sz.gfp.tencent-cloud.com/1604048722437_2185711_6da27ea482ecb28c549250d09c5abdf1.mp4', c: 'http://txfile-30121.sz.gfp.tencent-cloud.com/1604048734024_824230_198eb706d2052ddea6c2814adfe8d798.jpg' },
]
let newDatas = []
for (let i = len; i < len + 10; i++) {
let item = JSON.parse(JSON.stringify(presetDatas[this.presetCur]))
item.i = i + ''
item.like = item.i % 3 === 0
newDatas.push(item)
this.presetCur = this.presetCur + 1
if (this.presetCur >= presetDatas.length) {
this.presetCur = 0
}
}
this.list = this.list.concat(newDatas)
return newDatas
},
结合俩段代码我们可以看到,onRefresh函数通过getItem的方法,将获取到的ajax数据构建成为视频对象,然后通过loadDatas方法传入给控件,使得控件可以正常渲染视频,这里我们主要就来看看genData方法。
genData方法采用一个非常简单的方式构造假的ajax数据,这样我们改造起来方便很多,我们只需要把他改成promise的形式,然后通过axios异步获取数据之后重新构造即可,废话不多说,我们直接po出来俩个函数的改造结果。
async onRefresh() {
uni.showLoading()
this.list = []
this.presetCur = 0
var datas = []
// 这里注意我没有用try catch,因为我直接用我本地服务器测试的
let ajaxDatas = await this.genData()
ajaxDatas.forEach(item => {
let data = asvListPlayer.getItem(item.i)
.video(item.v)
.cover(item.c)
.bindImage('head', 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3533321036,2623280788&fm=15&gp=0.jpg', true)
.bindImage('like', item.like ? 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/c94c1a75-094a-482a-9acf-f1eefbd24792.png':'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/5a17a78c-f923-41f3-8916-271b4d5d528f.png')
.bindText('likeText', parseInt((item.i * 1 + 1) * (new Date().getTime()) / 1000000000000) + '.4w')
.bindImage('commit', 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/003910f2-92cf-40c5-9d8a-870401be6e41.png')
.bindText('commitText', parseInt((item.i * 1 + 1) * (new Date().getTime()) / 10000000000) + '')
.bindImage('share', 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/353edf1a-c2f1-481a-87fc-b9d5df98fb9e.png')
.bindText('shareText', '分享')
.bindText('userName', `UserName`)
.bindImage('userIcon', 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/89898f94-c3b8-4e14-9d06-7ccb1f27d3ab.png')
.bindText('title', `这是第${item.i * 1}个视频,悄悄是离别的笙箫,沉默是今晚的康桥,再别康桥,再见我终将逝去的青春,愿一切安好,愿你永远都在。`)
.toJSON()
datas.push(data)
})
this.asvListPlayer.loadDatas(datas)
uni.hideLoading()
}
点击并拖拽以移动
async genData () {
return new Promise(resolve => {
let list = await new axios({
url: 'http://192.168.0.25:8080/api/getVideoList',
type: 'post'
})
// 这里axios会包一层data,所以需要这样处理
list = list.data
let retList = []
list.forEach(item => {
retList.push({
v: item.videoUrl,
c: item.coverUrl,
i: item.id,
like: item.likes
})
})
resolve(retList)
})
},

这里通过给俩个方法加上async/await,以及给genData加上Promise返回,这样我们就可以无痛的改造让demo支持ajax获取视频数据,对于分页获取,也就是onLoadMore的改造也是如此。
监听控件点击
mounted () {
this.asvListPlayer.on('onClick', this.onClick)
}
点击并拖拽以移动
onClick({ type, data }) {
uni.showToast({
icon: 'none',
position: 'bottom',
title: '您点击了第' + (data.position + 1) + '个视频的控件,控件名为:' + data.id
})
let index = data.position
let [item] = this.list.filter((R,I) => I === index)
switch (data.id) {
case 'like':
item.like = !item.like
this.asvListPlayer.setItemData(index,asvListPlayer.getItem(item.i)
.video(item.v)
.cover(item.c)
.bindImage('head', 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3533321036,2623280788&fm=15&gp=0.jpg', true)
.bindImage('like', item.like ? 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/c94c1a75-094a-482a-9acf-f1eefbd24792.png':'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/5a17a78c-f923-41f3-8916-271b4d5d528f.png')
.bindText('likeText', parseInt((item.i * 1 + 1) * (new Date().getTime()) / 1000000000000) + '.4w')
.bindImage('commit', 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/003910f2-92cf-40c5-9d8a-870401be6e41.png')
.bindText('commitText', parseInt((item.i * 1 + 1) * (new Date().getTime()) / 10000000000) + '')
.bindImage('share', 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/353edf1a-c2f1-481a-87fc-b9d5df98fb9e.png')
.bindText('shareText', '分享66')
.bindText('userName', `UserName`)
.bindImage('userIcon', 'https://files.qiadoo.com/CareBoBo/Common/2021/05/17/89898f94-c3b8-4e14-9d06-7ccb1f27d3ab.png')
.bindText('title', `这是第${item.i * 1}个视频,悄悄是离别的笙箫,沉默是今晚的康桥,再别康桥,再见我终将逝去的青春,愿一切安好,愿你永远都在。`)
.toJSON())
break
case 'commit':
this.showBottomPopover = true
break
case 'share':
uni.showActionSheet({
itemList: ['分享到微信']
})
break
}
}
在这里的话,控件用的是事件回调的方式,通过onClick返回事件信息,data.id就是我们setViewConfig的时候传入的asvListPlayer.getView('head')这里的字符串,也就是唯一id,我们可以通过id判断用户点击了什么,从而响应对应的事件,甚至是通过setItemData刷新视频的控件布局视频,比如点赞成功之类的。
为了方便我这里本地接口还是复制黏贴官方demo提供的链接地址,大家有需要的可以自己用实际数据测试,测试下感觉流畅度还是蛮ok的,但是要注意以下几点。
- 因为这用了自定义原生控件,因此必须使用nvue界面布局
- 在使用.bindText('commitText', '666')绑定文字的时候,传入的必须是String类型,一开始我传入Number就直接渲染不出来了
- 视频不能使用m3u8这种,最好是使用mp4,毕竟mp4才是标准的移动视频格式。
10分钟快速上车短视频风口:基于uniapp框架创建自己的仿抖音短视APP的更多相关文章
- Vue3.0短视频+直播|vue3+vite2+vant3仿抖音界面|vue3.x小视频实例
基于vue3.0构建移动端仿抖音/快手短视频+直播实战项目Vue3-DouYin. 5G时代已来,短视频也越来越成为新一代年轻人的娱乐方式,在这个特殊之年,又将再一次成为新年俗! 基于vue3.x+v ...
- 5分钟快速打造WebRTC视频聊天<转>
原文地址: 5分钟快速打造WebRTC视频聊天 百度一下WebRTC,我想也是一堆.本以为用这位朋友( 搭建WebRtc环境 )的SkyRTC-demo 就可以一马平川的实现聊天,结果折腾了半天,文本 ...
- Vite2+Electron仿抖音|vite2.x+electron12+vant3短视频|直播|聊天
整合vite2+electron12跨平台仿抖音电脑版实战Vite2-ElectronDouYin. 基于vite2.0+electron12+vant3+swiper6+v3popup等技术跨端仿制 ...
- 基于vue+uniapp直播项目|uni-app仿抖音/陌陌直播室
一.项目简介 uni-liveShow是一个基于vue+uni-app技术开发的集小视频/IM聊天/直播等功能于一体的微直播项目.界面仿制抖音|火山小视频/陌陌直播,支持编译到多端(H5.小程序.Ap ...
- iOS多种刷新样式、音乐播放器、仿抖音视频、旅游App等源码
iOS精选源码 企业级开源项目,模仿艺龙旅行App 3D立体相册,可以旋转的立方体 横竖屏切换工具,使用陀螺仪检测手机设备方向,锁屏状... Swift版Refresh(可以自定义多种样式)架构方面有 ...
- rtvue-lowcode:一款基于uniapp框架和uview组件库的开源低代码开发平台
rtvue-lowcode低代码开发平台 rtvue-lowcode一款基于uniapp框架和uview组件库的低代码开发平台,项目提供可视化拖拽编辑器,采用MIT开源协议,适用于app.小程序等项目 ...
- 【Istio实际操作篇】Istio入门,10分钟快速安装
@ 目录 前言 本文说明 请大家务必查看 环境准备 详细版 入门:搭建步骤 Istio软件包下载 下载Istio 卸载 简洁版 安装 卸载 学习不走弯路,gz号「yeTechLog」 前言 上一篇讲了 ...
- iOS 快速集成ijkplayer视频直播与录播框架
最近由于需求的变动,项目内把最初最简单的原生直播框架变成了B站开源的ijkplayer框架,下面把具体的过程总结一下整个过程都比较简单,重要的是理解的过程,集成完毕之后,视频的用户体验比苹果原生好了很 ...
- uni-app仿抖音APP短视频+直播+聊天实例|uniapp全屏滑动小视频+直播
基于uniapp+uView-ui跨端H5+小程序+APP短视频|直播项目uni-ttLive. uni-ttLive一款全新基于uni-app技术开发的仿制抖音/快手短视频直播项目.支持全屏丝滑般上 ...
随机推荐
- Codeforces 1236F - Alice and the Cactus(期望+分类讨论)
Codeforces 题面传送门 & 洛谷题面传送门 期望好题. 首先拆方差: \[\begin{aligned} &E((x-E(x))^2)\\ =&E(x^2)-2E(x ...
- spring-boot -配置文件值注入
/** * 将配置文件中配置的每一个属性的值,映射到这个组件中 * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定: 默认在 ...
- 【R】调整ggplot图例大小
图例太多时,会挤压正图,显得正图展示区域很小,这时有必要缩小图例. ################# # 减小ggplot图例 ################# library(ggplot2) ...
- python-django-自定义查询Q函数和F函数
数据库: def page_q(request): """Q函数的使用""" #查询username和nickname都是zhangsan ...
- python17进程
import os import time from multiprocessing.dummy import Process def so_sth(name): print("进程名称{} ...
- Docker初试
1. docker是啥? 自行Google或百度去... https://yeasy.gitbooks.io/docker_practice/introduction/what.html 重要概念: ...
- 数据分析体系 — 用户粘性的两个计算指标(DAU/MAU和月人均活跃天数)
很多运营都了解DAU(日活跃用户数)和MAU(月活跃用户数)的重要性,但在某些情况下这两个数值本身并不能反映出太多问题,这个时候就要引用到[DAU/MAU]的概念,即[日活/月活] 用户粘性的两个计算 ...
- day04 查找关键字
day04 查找关键字 昨日内容回顾 基本数据类型之日期相关类型 date :年月日 time :时分秒 datetime:年月日时分秒 year :年 基本数据类型之枚举与集合类型 # 枚举 多选一 ...
- VSCode+Maven+Hadoop开发环境搭建
在Maven插件的帮助下,VSCode写Java其实非常方便.这一讲我们介绍如何借助maven用VScode搭建Hadoop开发环境. 1.Java环境安装 首先我们需要搭建好Java开发环境.我们需 ...
- flink-----实时项目---day04-------1. 案例:统计点击、参与某个活动的人数和次数 2. 活动指标多维度统计(自定义redisSink)
1. 案例 用户ID,活动ID,时间,事件类型,省份 u001,A1,2019-09-02 10:10:11,1,北京市 u001,A1,2019-09-02 14:10:11,1,北京市 u001, ...