仿腾讯视频-微信小程序
腾讯视频是一个让我们都喜爱的视频观看平台,用户群体也相当庞大。小编也非常喜欢使用腾讯视频播放软件,在娱乐的时间之中,也给本人来许多快乐。
【仿腾讯视频-微信小程序】前言
在学习了小程序之后,为了巩固自身的学习知识和提高实战能力。小编也非常的喜欢写一个属于自己的小程序,而且也发现有些人写的视频类小程序不是很细节,所以小编选了‘腾讯视频’小程序,也开始走上一条“踩坑”的不归路。写下这边文章也是为了纪念自己的痛苦之路,同时也希望给学习小程序的你带来丁点帮助。
项目部分gif演示
1. 前期准备
微信开发者工具(必须)
VScode编辑器(可选)
阿里巴巴矢量图标库-提供一些图标icon
腾讯视频-获取视频数据Vid
腾讯视频插件-配置
2. tabBar设计
在设计小程序的tabBar时,直接使用微信小程序官方提供给我们的tabBar。那如何使用微信小程序提供的tabBar来设计腾讯视频小程序的tabBar呢?
a.首先,使用微信开发者工具(或者VScode)打开你新建的小程序项目,找到你的小程序中的app.json文件。在app.json文件中的pages项,新增如下配置:
>need-to-insert-img
b.接着,按(ctrl+s)进行保存。此时,微信开发者工具会帮你新建四个页面文件夹,你在pages文件夹打开即可看到这四个文件夹。
c.然后,在pages同级目录下,新建images用来放置程序图片资源。紧接着我们去阿里巴巴矢量图标库搜索自己需要的tabBar对应的图标,把它们下载放置到imgages中去。
d.开始配置tabBar,找到你的小程序中的app.json文件。在app.json文件中的tabBar项,新增如下配置:
"tabBar": {"color":"#000000","selectedColor":"#FF4500","list": [{"pagePath":"pages/main/main","text":"首页","iconPath":"images/shouye.png","selectedIconPath":"images/shouye-action.png"},{"pagePath":"pages/shortVideo/index","text":"短视频","iconPath":"images/duanshiping.png","selectedIconPath":"images/duanshiping-action.png"},{"pagePath":"pages/brush/brush","text":"刷一刷","iconPath":"images/shuayishua.png","selectedIconPath":"images/shuayishua-action.png"},{"pagePath":"pages/mine/mine","text":"我的","iconPath":"images/mine.png","selectedIconPath":"images/mine-action.png"}]}复制代码
e.效果图如下:
>need-to-insert-img
3. 数据请求
日常小程序开发过程中基本时通过微信小程序开发工具提供的wx.request来做数据请求,那么怎么可以让自己定义的数据库呢?我们这里采用云开发的微信提供的免费云数据库,做数据请求。在项目的cloudfunctions文件夹下新建几个自己需要的云函数请求响应的数据请求。
以获取搜索建议为例:
1. 云函数部分:
// 云函数入口文件const cloud = require('wx-server-sdk')const env ='dhyuan'cloud.init()// 获取数据库句柄suggestVideoconst db = cloud.database({ env })// 云函数入口函数exports.main = async (event, context) => {// cloud.getWXContext()直接拿到用户信息console.log(event.key)// 查询建议的 模糊查询const _ = db.commandletsuggestVideo = await db.collection('suggestVideo').where(_.or([{keyword: db.RegExp({regexp:'.*'+ event.key,options:'i',})}])).get({success: res => {console.log(res)},fail: err => {console.log(err)}})letreturnResult = [];
for(leti = 0;
i < suggestVideo.data.length;
i++) {returnResult.push(suggestVideo.data[i])}returnreturnResult.sort((a,b) => a.createTime < b.createTime ? 1 : -1)}复制代码
2. 搜索页中的 数据请求调用 与函数部分:
// 搜索建议searchSuggest() {const self = this;
//展示标题栏的lodingwx.showNavigationBarLoading();
//调用云函数wx.cloud.callFunction({name:'search',data:{ key: self.data.searchKey },success(res){// console.log(res);
self.setData({showvideoresult:true,searchsuggest: res.result})},fail(err){// console.log(err);
self.setData({showvideoresult:false})},complete(){//让 loding消失wx.hideNavigationBarLoading();
}})},复制代码
4. 视频搜索
在小程序开发中,搜索功能是一个比较难实现的功能,尤其涉及数据求以及动态化的实时搜索。下面小编进行一步一步搜索功能的解析
搜索的样式设计
以头部查询为例:(其他样式请见github: 传送门)
在设计搜索栏的头部时,我们采用原生的样式渲染方式,这样大家也可以理解其实现的原理,所以就不采用UI框架了,当然如果你想使用UI小编也可以推荐你使用WeUI(微信原生视觉体验一致的基础样式库)。
不多说废话啦,开始动手了。
1.实现样式设计思路:使用view包裹search的icon和input,让包裹的view边框角变成圆角,在使用行内块级元素使其在一行显示,并使用vertical-align: middle;
居中对齐;
2.搜索头部基本结构
取消复制代码
3. 样式渲染
/* 搜索bar */.page__bd {position: fixed;
top:0;
width:100%;
background-color:#FF4500;
z-index:1;
}.search-bar {width:100%;
display: flex;
background-color:#FF4500;
border: 1pxsolid#DC4238;
}.search-bar__box{vertical-align: middle;
height: 65.52rpx;
margin: 20rpx 10rpx 20rpx 25rpx;
background-color:#DE655C;
border-radius: 20rpx;
width: 75%;
padding-left: 30rpx;
padding-right: 30rpx;
display: inline-block;
align-items: center;
}.icon-search_in-box{width: 32.76rpx;
height: 32.76rpx;
vertical-align: middle;
display: inline-block;
}.icon-clear{width: 32.76rpx;
height: 32px 0.76rpx;
vertical-align: middle;
display: inline-block;
margin-left: 80rpx;
}.search-bar__input{vertical-align: middle;
display: inline-block;
}.search-bar__cancel-btn {color:#ffffff;
display: inline-block;
font-size:32rpx;
}复制代码
搜索功能部分
1. 实现思路:a. 关键字搜索建议:绑定input输入框使其每输入一个值触发关键字搜索建议操作,并展示给用户观看,此时展示你的搜索建议view设置z-index;b. 关键字搜索结果:当你输完关键字回车时,触发搜索结果操作,云函数去查询云数据库并放回相关数据;c.取消:当你点击取消时,此时小程序会放回到首页;d.搜索历史:当你每次输完关键字点击回车时,使用wx.setStorageSync保存数据到本地,当回到搜索主页时,从本次内存取出你查询过的关键字即可。
2. 实现关键字搜索建议
页面js求
searchResult() {// console.log(this.data.searchKey)const self = this;
//展示标题栏的lodingwx.showNavigationBarLoading();
//调用云函数wx.cloud.callFunction({name:'searchResult',data:{ key: self.data.searchKey },success(res){// console.log(res);
self.setData({showvideoresult:false,searchresult: res.result})},fail(err){// console.log(err);
self.setData({showvideoresult:false})},complete(){//让 loding消失wx.hideNavigationBarLoading();
}})}复制代码
云函数:
// 云函数入口文件const cloud = require('wx-server-sdk')const env ='dhyuan'cloud.init()// 获取数据库句柄suggestVideoconst db = cloud.database({ env })// 云函数入口函数exports.main = async (event, context) => {// cloud.getWXContext()直接拿到用户信息console.log(event.key)// 查询建议的 模糊查询const _ = db.commandletsuggestVideo = await db.collection('suggestVideo').where(_.or([{keyword: db.RegExp({regexp:'.*'+ event.key,options:'i',})}])).get({success: res => {console.log(res)},fail: err => {console.log(err)}})letreturnResult = [];
for(leti = 0;
i < suggestVideo.data.length;
i++) {returnResult.push(suggestVideo.data[i])}returnreturnResult.sort((a,b) => a.createTime < b.createTime ? 1 : -1)}复制代码
3. 关键字搜索结果
js请求
// 搜索结果searchResult() {// console.log(this.data.searchKey)const self = this;
//展示标题栏的lodingwx.showNavigationBarLoading();
//调用云函数wx.cloud.callFunction({name:'searchResult',data:{ key: self.data.searchKey },success(res){// console.log(res);
self.setData({showvideoresult:false,searchresult: res.result})},fail(err){// console.log(err);
self.setData({showvideoresult:false})},complete(){//让 loding消失wx.hideNavigationBarLoading();
}})}复制代码
云函数
// 云函数入口文件const cloud = require('wx-server-sdk')const env ='dhyuan'cloud.init()// 获取数据库句柄suggestVideoconst db = cloud.database({ env })// 云函数入口函数exports.main = async (event, context) => {// cloud.getWXContext()直接拿到用户信息console.log(event.key)// 查询建议的 模糊查询const _ = db.commandletserultVideo = await db.collection('searchResult').where(_.or([{title: db.RegExp({regexp:'.*'+ event.key,options:'i',})},{artists: db.RegExp({regexp:'.*'+ event.key,options:'i',})}])).get({success: res => {console.log(res)},fail: err => {console.log(err)}})letreturnResult = [];
for(leti = 0;
i < serultVideo.data.length;
i++) {returnResult.push(serultVideo.data[i])}returnreturnResult.sort((a,b) => a.createTime < b.createTime ? 1 : -1)}复制代码
特别注意:
搜索中有可能出现“抖动现象”,那么如何解决该现象呢?此时,你需要采用debounce来解决,防止用户多次输入抖动触发搜索,从而导致多次不必要的数据请求。
具体解决如下:
//获取input文本并且实时搜索,动态隐藏组件getsearchKey:function(e) {// console.log(e.detail.value) //打印出输入框的值letthat = this;
if(e.detail.cursor != that.data.cursor) { //实时获取输入框的值that.setData({searchKey: e.detail.value})}if(e.value !="") { //组件的显示与隐藏that.setData({showView:false,share:true})}else{that.setData({showView:""})}if(e.detail.value !="") { //解决 如果输入框的值为空时,传值给搜索建议,会报错的bugthat.debounce(that.searchSuggest(), 300)}},// 去除输入抖动debounce (func, delay) {lettimerletself = thisreturnfunction(...args) {if(timer) {clearTimeout(timer)}timer =setTimeout(() => {func.apply(self, args)}, delay)}},复制代码
4. 取消
js操作
//实现取消功能,停止搜索,返回首页cancel:function() {wx.switchTab({url:'/pages/main/main'})},复制代码
5. 搜索历史
js操作
routeSearchResPage:function(e) {// console.log(e.detail.value)// 将数据存入本地if(this.data.searchKey) {lethistory= wx.getStorageSync("history") || [];
history.push(this.data.searchKey)wx.setStorageSync("history",history);
}},//每次显示变动就去获取缓存,给history,并for出来。onShow:function() {this.setData({history: wx.getStorageSync("history") || []})}复制代码
wxml对应部分
搜索历史{{item}}复制代码
5. 首页部分
首页基本是结构的设计,以及轮播和菜单切换,主要时考验我们wxss的功底和js交互功底。
1.样式结构设计
结构设计基本没什么大的难度,小编就不多废话了,详情见github项目(传送门)。结果如下图:
2.滑动菜单切换&轮播
a. 对于菜单的滑动切换,其实实现非常简单。
在实现之前,你需要了解的几个标签:swiper,swiper-item,scroll-view;
滑块视图容器。swiper:其中只可放置swiper-item组件,否则会导致未定义的行为;
scroll-view可滚动视图区域。使用竖向滚动时,需要给scroll-view一个固定高度,通过 WXSS 设置 height。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)。
b. 菜单滑动切换实现思路:给swiper绑定一个bindchange='swiperChange'事件,每当用户滑动页面时,触发'swiperChange'事件,并且在js中定义一个数据变量为curentIndex,通过监听if(e.detail.source == 'touch')其中的touch事件,从而让curentIndex用来记录每次滑动切换的swiper-item,并通过wx:if="{{curentIndex == 0}}来判断当前的swiper-item是否显示,从而达到滑动切换菜单的效果。并且,菜单栏的index也与curentIndex进行判断,从而让指定的菜单高亮显示。
c. 实现过程
1. wxml部分:
{{item.name}}复制代码
2. js 部分
//改变swiperswiperChange:function(e) {//切换if(e.detail.source =='touch') {letcurentIndex = e.detail.current;
this.setData({curentIndex})}},switchTab(e){this.setData({curentIndex:e.currentTarget.dataset.index,toView: e.currentTarget.dataset.id})}复制代码
d. 你可能会踩的“坑”
在你使用 swiper 和scroll-view时,会出现swiper-item中的内容超出可视范围时,无法上下滑动问题。这是你要第一时间想到“swiper高度自适应”这个关键词。小编在这提供几种解决方式。
方案一:
swiper高度固定,swiper-item默认绝对定位且宽高100%,每个swiper-item中内容由固定高度的child组成,然后根据child数量动态计算swiper高度,初始方案(由于rpx针对屏幕宽度进行自适应,child_height使用rpx方便child正方形情况下自适应):
swiper_height = child_height * child_num
屏幕效果仅在宽度375的设备(ip6、ipⅩ)完美契合,其他设备都底部会出现多余空隙,并且在上拉加载过程中,随着内容增加,底部空隙也逐渐变大。
方案二:
swiper_height = child_height * child_num * ( window_width / 750 )复制代码
然后并无变化,我们可以看到child_height在不同宽度屏幕下,显示的宽高尺寸是不一样的(px单位),那就尝试使用box在各个屏幕的实际高度进行计算swiper高度,box的高度可以单独在页面中增加一个固定标签,该标签样式和box宽高保持一致并且隐藏起来,然后在page的onload中通过wx.createSelectorQuery()获取标签实际高度baseItemHeight(px单位):
swiper_height = baseItemHeight * child_num复制代码
结果显示原本的ip6、ipⅩ没有问题,另外宽带小于375的ip5上也ok,但是在大于375的设备上还是出现空隙,比如ip的plus系列
方案三:
swiper底部有一个load标签显示“加载更多”,该标签紧贴box其后,通过wx.createSelectorQuery()来获取bottom,然而你会发现bottom是标签的height加top的和。计算底部空隙(暂时忽略“加载更多”标签高度)space_height = swiper_height - load_top刚计算完可以看到在静止状态下,计算出space_height拿去修改swiper_height显示空隙刚好被清掉了,但是接着就发现在动过程中获取到的bottom是不固定的,也就是说数值可能不准确导致space_height计算错误,显示效果达不到要求
小编采用的是方案一
思路:给swiper一个外部view装载swiper,并给它设置style="height:{{ch}}rpx;
",这里的ch为js中的数据变量方便动态修改view的高度。并且在js中的钩子函数中的onLoad函数中编写如下代码:
onLoad:function(options) {wx.getSystemInfo({success: res => {//转为rpxletch = (750 / res.screenWidth) * res.windowHeight - 180;
this.setData({ch})},})}复制代码
式子中减 180 ,是小编自己调试的数据,具体减多少,可以根据具体情况而定。其实说白了,该方案的设计,基本是与better-scoll的滑动策略基本雷同。
6. 视频播放
1. 主体设计
a. 主体结构设计
{{showModalStatus ==true?'stopScroll':''}}{{entitie.duration}}-->{{entitie.header}}简介8.6分·VIP·视频·全36集·8.8亿
剧集每周一二三20点更新2集,会员多看6集{{item.num}}精彩片花{{entitie.header}}每周一二三20点更新2集,会员多看6集{{entitie.score}}分·VIP·{{entitie.type}}·全{{entitie.universe}}集·8.8亿简介{{entitie.original_description}}复制代码
b. js交互
// pages/videoDetail/index.jsconst entities = require('../../data/entities.js')const txvContext = requirePlugin("tencentvideo")const config = require('../../modules/config')const episodes = require('../../data/episodes.js')letcurrentVideo;
Page({/*** 页面的初始数据*/data: {entitie: null,id: null,entities,clips: null,currentVid:null,episodes: null,tvphide:false,vid: null,title:"电视剧",defn:"超清",changingvid:'',controls: !!config.get('controls'),autoplay: !!config.get('autoplay'),playState:'',showProgress1:true,width:"100%",height:"auto",showModalStatus:false,car:{},detailOn:true,ch: 0,currentIndex: 0,top: 0,currVideo:{}},play(event){const target = event.target;
const currentVid = target.dataset.vid;
if(this.data.currentVid!=null){currentVideo.pause();
}if(currentVid){currentVideo = wx.createVideoContext(`${currentVid}`);
this.txvContext.pause();
currentVideo.play();
}this.setData({currentVid})},select(e){const target = e.target;
const currentVid = target.dataset.vid;
const num = target.dataset.num;
console.log(currentVid, num);
this.setData({vid: currentVid,clips: this.data.episodes[num-1].clips})this.txvContext = txvContext.getTxvContext('txv0');
this.txvContext.play();
},/*** 生命周期函数--监听页面加载*/onLoad:function(options) {//动态设置 详情的高度防止滑动失效wx.getSystemInfo({success: res => {//转为rpxletch = (750 / res.screenWidth) * res.windowHeight -478;
this.setData({ch})},})const id= options.id;
console.log('id', id);
letepisode = episodes.find(function(item){returnitem.id == id;
})letentitie = entities.find(function(item){returnitem.id == id;
})this.setData({entitie})//改变page里面的datathis.setData({id: id,episodes: episode.episodes,vid: episode.episodes[0].vid,clips: episode.episodes[0].clips})// console.log('vid', this.data.vid);
this.setData({controls: !!config.get('controls'),autoplay: !!config.get('autoplay')})this.txvContext = txvContext.getTxvContext('txv0');
this.txvContext.play();
this.videoContext = wx.createVideoContext('tvp');
},onTvpPlay:function() {// console.log('play')},onStateChange:function(e) {this.setData({playState: e.detail.newstate})},onTvpContentChange:function() {},onTimeUpdate:function(e) {},requestFullScreen:function() {this.txvContext.requestFullScreen();
},onFullScreenChange:function() {// console.log('onFullScreenChange!!!')},onTvpTimeupdate:function(){},onTvpPause:function() {},onTvpStateChanage:function() {},onPicClick(e) {letdataset = e.currentTarget.dataset;
this.currIndex=dataset.indexthis.setData({"currVideo.vid":dataset.vid})// console.log(this.data.currVideo)this.getTop()},getTop(){letquery = this.createSelectorQuery();
query.selectViewport().scrollOffset();
query.selectAll(`.mod_poster`).boundingClientRect().exec(res => {letoriginTop = 0;
this.setData({top: originTop + this.currIndex * 224.5})});
},/*** 生命周期函数--监听页面初次渲染完成*/onReady:function() {},/*** 生命周期函数--监听页面显示*/onShow:function() {},/*** 生命周期函数--监听页面隐藏*/onHide:function() {},/*** 生命周期函数--监听页面卸载*/onUnload:function() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh:function() {},/*** 页面上拉触底事件的处理函数*/onReachBottom:function() {},/*** 用户点击右上角分享*/onShareAppMessage:function() {console.log('share success!')},//显示对话框showModal:function() {// 显示遮罩层var animation = wx.createAnimation({duration: 200,timingFunction:"linear",delay: 0})this.animation = animationanimation.translateY(300).step()this.setData({animationData: animation.export(),showModalStatus:true,detailOn:false})setTimeout(function() {animation.translateY(0).step()this.setData({animationData: animation.export()})}.bind(this), 200)},//隐藏对话框hideModal:function() {// 隐藏遮罩层var animation = wx.createAnimation({duration: 200,timingFunction:"linear",delay: 0})this.animation = animation;
animation.translateY(300).step();
this.setData({animationData: animation.export(),})setTimeout(function() {animation.translateY(0).step()this.setData({animationData: animation.export(),showModalStatus:false,detailOn:true})}.bind(this), 200)},// 默认阻止滚动stopScroll() {returnfalse;
}})复制代码
c. 你可能会遇到的‘坑’
当你在设计简介的时候,你会发现自己设计的弹出框的内部滑动事件与 当前页的滑动事件一起触发了,那这是为啥呢?仔细想一下,你会发现是冒泡和捕获(详解参考该博文)在搞鬼,相信写过web项目的人对冒泡和捕获非常的熟悉。那么在小程序中也是有的,所以这里你就需要了解滑动穿透这个东西了。那么如何来解决这个问题呐?
解决办法:在简介中需要滑动的view中 加上catchtouchmove="stopScroll",并且在js中定义stopScroll方法并放回false即可解决。具体如下:
1. wxml:
{{entitie.header}}每周一二三20点更新2集,会员多看6集{{entitie.score}}分·VIP·{{entitie.type}}·全{{entitie.universe}}集·8.8亿
简介{{entitie.original_description}}复制代码
2. js部分
// 默认阻止滚动stopScroll() {returnfalse;
}复制代码
2.切换电视剧剧集
a. 实现电视剧的剧集切换思路:拿到需要播放视频的vid,将vid替换掉当前的vid,然后执行播放操作。
b.实现步骤:
1. 在.json文件中,配置腾讯视频插件组件。如下:
{"usingComponents": {"txv-video":"plugin://tencentvideo/video"}}复制代码
2. 在wxml中使用,如下:
复制代码
其中,在txv-video中的属性配置含义:
vid: 腾讯视频的vid,用于拿到该视频资源(必须)
playerid:playerid必须要全局唯一,可以设置为vid,否则导致视频播放错乱(必须)
autoplay:是否自动播放;true|false
controls: 是否显示控制栏(播放,暂停,全屏的按钮显示)
title:视频标题
defn:视频清晰度,默认auto,可选值:流畅,标清,高清,超清,蓝光,4K,杜比
其他属性见:腾讯视频插件官方文档
3. js交互
select(e){const target = e.target;
const currentVid = target.dataset.vid;
const num = target.dataset.num;
console.log(currentVid, num);
this.setData({vid: currentVid,clips: this.data.episodes[num-1].clips})this.txvContext = txvContext.getTxvContext('txv0');
this.txvContext.play();
}复制代码
3. 简介实现
a. 简介部分主要是wxcss的渲染,没有什么逻辑,需要注意的时,点击下拉可以使简介下拉隐藏,并有下拉的过程出现。
b. 主要代码如下:
1. wxml部分:
{{entitie.header}}每周一二三20点更新2集,会员多看6集{{entitie.score}}分·VIP·{{entitie.type}}·全{{entitie.universe}}集·8.8亿
简介{{entitie.original_description}}复制代码
2. wxss部分:
.commodity_attr_box {width: 100%;
height: 100%;
color:#fff;
overflow: hidden;
position: fixed;
bottom: 0;
top: 420rpx;
left: 0;
z-index: 998;
background-color: #1f1e1e;
padding-top: 20rpx;
}.commodity_movableView{width: 100%;
height: 2024rpx;
}.commodity_hide{position: relative;
height: 50rpx;
}.commodity_hide .title{margin-left: 30rpx;
font-size: 35rpx;
line-height: 35rpx;
font-weight: 40;
}.commodity_hide .commodity_hide__on{width: 50rpx;
height: 50rpx;
position: absolute;
display: inline-block;
right: 20rpx;
}.commodity_hide .commodity_hide__on::after{position: absolute;
top: 10rpx;
content: '';
color: #fff;
width: 20rpx;
height: 20rpx;
border-top: 4rpx solid #ece3e3;
border-right: 4rpx solid #ece3e3;
-webkit-transform: rotate(135deg);
transform: rotate(135deg);
}.commodity_attr_box .hightDataView{width: 100%;
}.commodity_attr_box .hightDataView .top{background-color:#1f1e1e;
color: #fff;
height: 140rpx;
box-sizing: border-box;
border-bottom: 4rpx solid #8b8989;
}.commodity_attr_box .hightDataView .top .top-text{font-size: 12px;
margin-top: 35rpx;
margin-left: 30rpx;
margin-right: 50rpx;
color: #C0C0C0;
line-height: 25px;
}.commodity_attr_box .hightDataView .top .top-descrese{margin-left: 30rpx;
font-size: 12px;
line-height: 25px;
color: #C0C0C0;
}.commodity_attr_box .hightDataView .center{border-bottom: 4rpx solid #8b8989;
}.commodity_attr_box .hightDataView .center .star-list {width: 100%;
margin-top: 30rpx;
margin-left: 20rpx;
margin-bottom: 50rpx;
white-space: nowrap;
box-sizing: border-box;
}.commodity_attr_box .hightDataView .center .star-list .item{text-align: center;
display: inline-block;
padding:4rpx;
}.commodity_attr_box .hightDataView .center .star-list .item image{width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin: 10rpx;
}.commodity_attr_box .hightDataView .center .star-list .item .name{font-size: 10px;
font-weight: normal;
}.commodity_attr_box .hightDataView .bottom{width: 100%;
}.commodity_attr_box .hightDataView .bottom .title{margin-left: 30rpx;
font-size: 35rpx;
line-height: 35rpx;
font-weight: 40;
margin-top: 30rpx;
}.commodity_attr_box .hightDataView .bottom .text{font-size: 12px;
font-weight: normal;
text-indent: 34rpx;
margin-top: 20rpx;
color: #C0C0C0;
margin-left: 30rpx;
}复制代码
4. 片花部分
在设计片花部分,最主要的是采用什么方式去解决,一次页面渲染加载多个视频问题,很多人直接用for循环放置,多个视频video标签;其实这是非常笨拙的办法;小编在这做了一个比较高级的办法,那就是:页面放置的都是view来存放该视频的vid,当点击相应图片时,触发一个onPicClick事件,此时拿到需要播放的vid,并通知页面我需要播放某个视频了,请给我一个video去播放视频;
此外,你需要注意的是,你这个video出现的位置,必须是你点击的图标位置,这样就不会造成页面图片与视频位置不符的问题了。而且,采用这种办法,页可以减缓你的手机的cpu消耗,该办法算是非常高明的手法了。下面来看下怎么具体实现这种高明的手法吧。
a. wxml部分
精彩片花
复制代码
b.js交互部分
onPicClick(e) {letdataset = e.currentTarget.dataset;
this.currIndex=dataset.indexthis.setData({"currVideo.vid":dataset.vid})// console.log(this.data.currVideo)this.getTop()},getTop(){letquery = this.createSelectorQuery();
query.selectViewport().scrollOffset();
query.selectAll(`.mod_poster`).boundingClientRect().exec(res => {letoriginTop = 0;
this.setData({top: originTop + this.currIndex * 224.5})});
}复制代码
c. 特别注意:
在getTop()方法中的逻辑,此处有些费解,为啥要去设置top值。其目的就是,为去矫正你点击某个图片之后,视频可以在相应位置出现,也就达到点击图片播放的效果。
7. 短视频
该模块实现逻辑,基本与首页差不多,直接看源码即可复制代码
实现基本思路:使用swiper,scroll-view实现左右滑动菜单联动,播放视频思路与播放片花思路基本一致。
1. json配置
为啥要配置,因为我们这里使用了腾讯视频插件,以及自己定义的视频尾部的组件,该尾部用于视频分享操作,以及评论操作。配置如下:
{"usingComponents": {"txv-video":"plugin://tencentvideo/video","footer":"/components/footer/footer"}}复制代码
2. wxml部分
{{item.name}}
复制代码
3. js部分
// pages/shortVideo/index.jsconst config = require('../../modules/config')const txvContext = requirePlugin("tencentvideo");
const sysInfo =wx.getSystemInfoSync()const shortCategory = require('../../data/shortCategory.js')const videoUrl = require('../../data/videoUrl.js')Page({/*** 页面的初始数据*/data: {curentIndex: 0,shortCategory: shortCategory,videos: videoUrl,ch: 0,top: 0,currVideo:{}},//改变swiperswiperChange:function(e) {//切换if(e.detail.source =='touch') {letcurentIndex = e.detail.current;
this.setData({curentIndex})}},switchTab(e){this.setData({curentIndex:e.currentTarget.dataset.index,toView: e.currentTarget.dataset.id})},onTvpTimeupdate:function(){},onTvpPlay:function() {},onTvpPause:function() {},onTvpContentChange:function() {},onTvpStateChanage:function() {},onPicClick(e) {letdataset = e.currentTarget.dataset;
this.currIndex=dataset.indexthis.setData({"currVideo.vid":dataset.vid})console.log(this.data.currVideo)this.getTop()},getTop(){letquery = this.createSelectorQuery();
query.selectViewport().scrollOffset();
query.selectAll(`.mod_poster`).boundingClientRect().exec(res => {console.log(res)console.log(res[0].scrollTop, res[1][this.currIndex].top)letoriginTop = res[0].scrollTop;
this.setData({top: originTop + this.currIndex * 224.5})});
},/*** 生命周期函数--监听页面加载*/onLoad:function(options) {wx.getSystemInfo({success: res => {//转为rpxletch = (750 / res.screenWidth) * res.windowHeight - 80;
this.setData({ch})},})this.videoContext = wx.createVideoContext('tvp');
},/*** 生命周期函数--监听页面初次渲染完成*/onReady:function() {},/*** 生命周期函数--监听页面显示*/onShow:function() {},/*** 生命周期函数--监听页面隐藏*/onHide:function() {},/*** 生命周期函数--监听页面卸载*/onUnload:function() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh:function() {},/*** 页面上拉触底事件的处理函数*/onReachBottom:function() {},/*** 用户点击右上角分享*/onShareAppMessage:function() {},// 默认阻止滚动stopScroll() {returnfalse;
}})复制代码
8. 我的
关于,我的部分实现基本内容是展示用户头像、姓名,显示是否开通了会员,观看历史,我的看单和设置功能,由于时间关系,小编只实现设置的部分功能
1.wxml部分
复制代码
2. js 部分
// miniprogram/pages/mine/mine.jsconst utils = require('../../utils/utils.js')//获取应用实例const app = getApp()Page({/*** 页面的初始数据*/data: {userInfo: {}},navigatItem(e) {returnutils.navigatItem(e)},getUserInfo:function(e) {app.globalData.userInfo = e.detail.userInfothis.setData({userInfo: e.detail.userInfo})},lookBans:function() {const that = this;
wx.showModal({content:'暂时未开发!',showCancel:false,confirmColor:'#FF4500',success(res) {}})},/*** 生命周期函数--监听页面加载*/onLoad:function(options) {if(app.globalData.userInfo) {this.setData({userInfo: app.globalData.userInfo})}else{// 在没有 open-type=getUserInfo 版本的兼容处理wx.getUserInfo({success: res => {app.globalData.userInfo = res.userInfo;
console.log(res.userInfo)this.setData({userInfo: res.userInfo})}})}},/*** 生命周期函数--监听页面初次渲染完成*/onReady:function() {},/*** 生命周期函数--监听页面显示*/onShow:function() {},/*** 生命周期函数--监听页面隐藏*/onHide:function() {},/*** 生命周期函数--监听页面卸载*/onUnload:function() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh:function() {},/*** 页面上拉触底事件的处理函数*/onReachBottom:function() {},/*** 用户点击右上角分享*/onShareAppMessage:function() {}})复制代码
3. 你需要注意的地方
在实现 设置功能部分时,这个小编在utils中写一个共有的 工具函数,用于页面跳转等操作。utils.js源码如下:
letnavigatItem = (e) => {const url = e.currentTarget.dataset.url ||'/pages/main/main'const open = e.currentTarget.dataset.openconst toUrl = () => {wx.navigateTo({url,})}if(open) {toUrl()}else{if(ifLogined()) {toUrl()}else{wx.navigateTo({url:'/pages/mine/mine'})}}}module.exports = {navigatItem}复制代码
项目完整源码:
github.com/hongdeyuan/…
9. 结语
小编在写该项目时,踩了不少的坑,这里只写出了几个。虽然有些地方用框架的话会更方便,但是我觉得徒手写项目自己的能力才会得到进阶;最后,感谢大家来学习该文章,感谢你们的支持,欢迎各位来学习讨论。
如果你喜欢这篇文章或者可以帮到你,不妨点个赞吧!
原文:
https://juejin.im/post/5dd401c9f265da0bc8031459
推荐阅读
- 4月23日海军节,我在青岛等你,一起看强大的中国海军。(如图如视频)
- 视频转换器哪种好用()
- 不懂法,害人终害己
- 腾讯视频(我有一段rap想给你说)
- 百度云极速下载,来体验飞的感觉,还可以看最新动漫的百度云视频哦
- 视频搬运工小赵-10#16.04元
- 狗狗定点大小便视频教程下载地址
- 复盘二
- SwiftUI|SwiftUI iOS 瀑布流组件之仿CollectionView不规则图文混合(教程含源码)
- 【实用教程】4种获取无水印视频素材的方法