在今年也就是第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 = 0var 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.lengthlet 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 === 0newDatas.push(item)this.presetCur = this.presetCur + 1if (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 = 0var 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.datalet 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.positionlet [item] = this.list.filter((R,I) => I === index)switch (data.id) {case 'like':item.like = !item.likethis.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())breakcase 'commit':this.showBottomPopover = truebreakcase 'share':uni.showActionSheet({itemList: ['分享到微信']})break}
}

在这里的话,控件用的是事件回调的方式,通过onClick返回事件信息,data.id就是我们setViewConfig的时候传入的asvListPlayer.getView('head')这里的字符串,也就是唯一id,我们可以通过id判断用户点击了什么,从而响应对应的事件,甚至是通过setItemData刷新视频的控件布局视频,比如点赞成功之类的。

为了方便我这里本地接口还是复制黏贴官方demo提供的链接地址,大家有需要的可以自己用实际数据测试,测试下感觉流畅度还是蛮ok的,但是要注意以下几点。

  1. 因为这用了自定义原生控件,因此必须使用nvue界面布局
  2. 在使用.bindText('commitText', '666')绑定文字的时候,传入的必须是String类型,一开始我传入Number就直接渲染不出来了
  3. 视频不能使用m3u8这种,最好是使用mp4,毕竟mp4才是标准的移动视频格式。

上车短视频赛道:基于uniapp框架快速搭建自己的仿抖音短视频APP相关推荐

  1. 短视频源码仿抖音短视频APP源码短视频平台源码短视频源码

    [WoShop仿抖音短视频源码的主要功能] 1.短视频带货:关联商品的短视频封面会有商品标识,短视频内容中会弹出商品链接 2.直播带货:短视频源码支持直播功能,直播间内可开启带货功能 3.邀请赚钱:用 ...

  2. uniapp - 仿抖音短视频项目

    仿抖音短视频阅读手册 特殊通知 1.请用户认真阅读以下说明,千万不能混淆页面随意引入,如果你发现运行后页面样式排版错乱,大概率是引入错误喔. 2.请App端用户将HbuilderX版本调整到3.3.9 ...

  3. 基于Hexo框架快速搭建个人博客--文章一键发布(五)

    基于Hexo框架快速搭建个人博客--文章一键发布 一.文章对比 二.发布到Github 三.一键发布 四.总结 博客链接: 会思想的苇草i 文章链接: 基于Hexo框架快速搭建个人博客–搭建(一) 基 ...

  4. 从零快速搭建仿抖音短视频APP-后端开发粉丝业务模块(4)

    项目持续更新中: 仿抖音短视频APP专栏 目录 视频页点赞总数 用户页点赞视频列表展示 我的关注视频列表展示 互粉朋友视频瀑布列表展示 视频页点赞总数 在我们的点赞下面会有一个数字,代表点赞总数.我们 ...

  5. 一对一交友源码,仿抖音短视频源码,搭建的秘密你了解多少?

    一对一交友源码,仿抖音短视频源码,搭建的秘密你了解多少? 5G技术马上到来,现在直播遇到的一些问题,比如延迟.卡顿.掉线等,很有可能这些情况就不复存在了.而且,其他的一些高科技产品会运用到直播过程中, ...

  6. 如何开发仿抖音短视频APP源码?

    如何开发仿抖音短视频APP源码? 流程列表 开发一个短视频最主要的流程分为 3 个,下面我将分步教你实现这 3 个流程下的各个功能点,功能点 API 可按需调用: 视频拍摄 a.启动拍摄 b.给拍摄添 ...

  7. WordPress仿抖音短视频主题插件

    WordPress仿抖音短视频主题插件 安装包大小:9.13MB 出品方名称:WordPress组织 已包含服务:短视频上传.短视频标签.自动添加水印.带水印短视频下载.作者主页.点赞.评论.聊天 服 ...

  8. 仿抖音短视频h5单页版htnl上传即可使用源码文件

    仿抖音短视频的模板 项目是前后端分离,前端采用uniapp开发,后端目前是用frphp临时搭建的接口,后续为了方便管理内容,会移植到极致cms上.现在也可丢到网站里当一个单页使用,无聊时可以刷一刷短视 ...

  9. 从零开始搭建仿抖音短视频APP-后端开发消息业务模块(1)

    项目持续更新中: 仿抖音短视频APP专栏 目录 保存系统消息到MongoDB 系统消息入库保存-关注 系统消息入库保存-点赞短视频 系统消息入库保存-评论与回复 保存系统消息到MongoDB 我们把m ...

最新文章

  1. C语言字符串大小写转换_只愿与一人十指紧扣_新浪博客
  2. 为了给你们讲清楚数据库中间件有哪些,我也是拼了!
  3. MATLAB报错“Exception in thread FileDecorationCache request queue java.lang.OutOfMemoryError: Java “
  4. vs无法写入量的大数据_一个每天服务数万人的企业食堂:自助餐按重计价,大数据支持食材预备量...
  5. java过去配置文件的值_java对.properties配置文件操作
  6. arm linux漏洞,GitHub - armjirawat/linux-kernel-exploits: linux-kernel-exploits Linux平台提权漏洞集合...
  7. python语言与c语言相比在分支结构上有什么不同,python 基础教程之语法篇章——一小时入门python__对比python与C语言的语法异同...
  8. 专注NLP、推荐等AI算法招聘群,慢者无,包括几乎所有公司最新信息
  9. GDB调试字符数组时指针和数组区别的体现
  10. 软件项目开发流程以及人员职责,软件工程中五种常用的软件开发模型整理
  11. 小米5刷android p6,小米5刷recovery教程 小米5第三方recovery下载
  12. 国标高数教材搞乱了微积分学界
  13. 误格式化硬盘数据怎么恢复好
  14. abc能否构成三角形c语言,编写程序输入三角形三边a.b.c 判断abc能否构成三角形...
  15. 什么是软件验收测试?如何获取软件验收测试报告
  16. scrapy——抓取知乎
  17. 汉字动图动态图gif格式,无水印 4500个汉字
  18. 利达主机联网接线端子_利达主机怎么编辑中文 利达主机接线端子说明
  19. 基于JAVA的停车场管理系统
  20. ubuntu16.04安装ros kinetic及遇到的问题

热门文章

  1. 怎么批量从NCBI上下载基因序列
  2. 关系数据库设计---练习题
  3. stm32呼吸灯实验
  4. 霍尔效应传感器的典型应用场合解析
  5. 计算机视觉相关词汇翻译
  6. .NetCore微信支付+服务商模式(saas)
  7. 来自未来的交互设计。当电影中的一切变为现实,设计师要如何进化?
  8. SpringBoot配置swagger-ui可视化接口文档
  9. 【LeetCode】309. Best Time to Buy and Sell Stock with Cooldown 解题报告(Python C++)
  10. https://isux.tencent.com/svg-animate.html(svg动画)