当前位置:网站首页>12_微信小程序之微信视频号滚动自动播放视频效果实现
12_微信小程序之微信视频号滚动自动播放视频效果实现
2022-07-03 00:26:00 【andr_gale】
12_微信小程序之微信视频号滚动自动播放视频效果实现
一.获取视频的分辨率、时长、缩略图
微信小程序提供了三种方式可以获取视频的分辨率、时长:
wx.getVideoInfo(Object):只能用于本地视频VideoDecoder.start(Object):可用于网络视频或者本地视频借助video标签的bindloadedmetadata回调: 可用于网络视频或者本地视频,但必须在video标签被渲染的情况下才能获取到,并且获取速度较慢
/** * @author gale * 传入视频链接,获取视频分辨率,时长,缩略图(视频第一帧的数据) */ decoderSource: function (source) { return new Promise((resolve, reject) => { var decoder = wx.createVideoDecoder() decoder.on("start", (res) => { //获取视频分辨率 var width = res.width var height = res.height //获取视频时长 var duration = res.duration/1000 var formatDur = this.formatDuration(duration) //获取视频的缩略图,即第一帧的图像 var frameData = decoder.getFrameData() while (!frameData) { frameData = decoder.getFrameData() } var fileName = source.substring(source.lastIndexOf("/") + 1, source.lastIndexOf(".")) let pngData = upng.encode([new Uint8ClampedArray(frameData.data).buffer], frameData.width, frameData.height) let base64 = wx.arrayBufferToBase64(pngData) var filePath = wx.env.USER_DATA_PATH + "/" + fileName + ".png" var systemManager = wx.getFileSystemManager() systemManager.writeFileSync(filePath, base64, "base64") resolve({ width: width, height: height, duration: duration, formatDur: formatDur, thumbnail: filePath, src: source }) decoder.remove() }) decoder.start({ source: source, mode: 1 }) }) }
二.微信小程序中,同一个页面存在多个播放器的坑
在微信小程序中,一个页面内,存在多个播放器时,会导致部分视频不能正常播放,同一页面存在多个video时,video无法正常播放一直在加载转圈
三.格式化视频时长
/** * @author gale * 格式化视频时长,如 14.6 格式化成 00:14 */
formatDuration: function(seconds) {
var format = ""
var h = parseInt(seconds/3600),
m = parseInt(seconds%3600/60),
s = parseInt(seconds%3600%60);
if(h>0){
h = h<10 ? '0'+h : h
format += h+":"
}
m = m<10 ? '0'+m : m
s = s<10 ? '0'+s : s
format+=m+":"+s
return format;
}
四.在列表中先渲染视频的缩略图和时长,播放当前视频时,将图片替换为video
这里采用自定义组件的方式实现
<!--components/video-list/index.wxml-->
<scroll-view class="video-list" scroll-y bindscroll="onScroll">
<view class="list">
<view class="video-item-wrapper" style="width: {
{
item.videoWidth}}px;" wx:for="{
{_videoList}}">
<view class="video-item" style="height: {
{
item.videoHeight}}px; background: #00f;">
<video wx:if="{
{playIndex == index}}" id="player" class="player" src="{
{item.src}}" object-fit="contain" show-center-play-btn="{
{false}}" custom-cache="{
{true}}" autoplay="{
{true}}"></video>
<block wx:else>
<image class="thumbnail" src="{
{item.thumbnail}}"/>
<view class="action">
<view class="play-wrapper" bindtap="play" data-index="{
{index}}">
<image class="play" src="./images/play.png"/>
<view style="margin-top: 10rpx;">{
{item.formatDur}}</view>
</view>
</view>
</block>
</view>
</view>
</view>
</scroll-view>
// components/video-list/index.js
Component({
/** * 组件的属性列表 */
properties: {
videoList: {
type: Array,
value: [],
observer: function(newVal, oldVal) {
var that = this
const query = that.createSelectorQuery()
query.select(".video-list").boundingClientRect()
query.exec((res) => {
var itemWidth = res[0].width
for(var i=0; i<newVal.length; i++) {
newVal[i].videoWidth = Math.floor(itemWidth)
newVal[i].videoHeight = Math.floor(itemWidth/(newVal[i].width/newVal[i].height))
}
that.setData({
_videoList: newVal
})
})
}
},
playIndex: {
type: Number,
value: -1,
observer: function(newVal, oldVal) {
var that = this
this.setData({
playIndex: newVal
})
if(newVal >= 0) {
var videoContext = wx.createVideoContext('player', that)
if(videoContext) {
videoContext.stop()
}
var timer = setTimeout(function() {
clearTimeout(timer)
var videoContext = wx.createVideoContext('player', that)
if(videoContext) {
videoContext.play()
}
}, 500)
}
}
}
},
/** * 组件的初始数据 */
data: {
_videoList: []
},
/** * 组件的方法列表 */
methods: {
play: function(event) {
var that = this
var index = event.currentTarget.dataset.index
this.setData({
playIndex: index
})
}
}
})
/* components/video-list/index.wxss */
.video-list {
width: 100%;
height: 100%;
}
.list {
width: 100%;
}
.video-item-wrapper {
background: #000;
padding-top: 200rpx;
padding-bottom: 200rpx;
margin-top: 20rpx;
}
.video-item-wrapper:last-child {
margin-bottom: 20rpx;
}
.video-item {
position: relative;
width: 100%;
}
.thumbnail, .player {
position: absolute;
left: 50%;
top: 50%;
width: 100%;
height: 100%;
transform: translate(-50%, -50%);
}
.action {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .6);
}
.play-wrapper {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #fff;
}
.play {
width: 48rpx;
height: 48rpx;
}
这里给播放器上下各100rpx的间距,是因为让整个item高度撑开,以保证每一个视频都能滑动自动播放,实际上传视频的时候,只需要让上传的视频宽高比能撑开播放器而不设置间距即可

五.实现滑动自动播放

// components/video-list/index.js
Component({
/** * 组件的属性列表 */
properties: {
videoList: {
type: Array,
value: [],
observer: function(newVal, oldVal) {
var that = this
const query = that.createSelectorQuery()
query.select(".video-list").boundingClientRect()
query.exec((res) => {
var itemWidth = res[0].width
for(var i=0; i<newVal.length; i++) {
newVal[i].videoWidth = Math.floor(itemWidth)
newVal[i].videoHeight = Math.floor(itemWidth/(newVal[i].width/newVal[i].height))
}
that.setData({
_videoList: newVal
})
const query = that.createSelectorQuery()
query.selectAll(".video-item-wrapper").boundingClientRect()
query.exec((res) => {
var items = res[0]
const query = that.createSelectorQuery()
query.select(".list").boundingClientRect()
query.exec((res) => {
var listHeight = res[0].height
that.setData({
videoItems: items,
listHeight: listHeight
})
})
})
})
}
},
playIndex: {
type: Number,
value: -1,
observer: function(newVal, oldVal) {
var that = this
this.setData({
playIndex: newVal
})
if(newVal >= 0) {
var videoContext = wx.createVideoContext('player', that)
if(videoContext) {
videoContext.stop()
}
var timer = setTimeout(function() {
clearTimeout(timer)
var videoContext = wx.createVideoContext('player', that)
if(videoContext) {
videoContext.play()
}
}, 500)
}
}
}
},
/** * 组件的初始数据 */
data: {
_videoList: [],
videoItems: [],
listHeight: 0,
contentHeight: 0
},
lifetimes: {
attached: function() {
var systemInfo = wx.getSystemInfoSync()
var contentHeight = systemInfo.windowHeight
this.setData({
contentHeight: contentHeight
})
}
},
/** * 组件的方法列表 */
methods: {
play: function(event) {
var that = this
var index = event.currentTarget.dataset.index
this.setData({
playIndex: index
})
},
onScroll: function(event) {
var contentHeight = this.data.contentHeight
var scrollHeight = event.detail.scrollHeight
var scrollTop = event.detail.scrollTop
var scrollPosition = Math.floor(scrollTop + contentHeight/2)
var index = this.getCurrentPlayIndex(scrollPosition, scrollHeight)
var playIndex = this.data.playIndex
if(index != playIndex) {
this.setData({
playIndex: index
})
}
},
getCurrentPlayIndex: function(scrollPosition, scrollHeight) {
var contentHeight = this.data.contentHeight
var listHeight = this.data.listHeight ? this.data.listHeight:0
var top = scrollHeight - listHeight
var current = -1
var videoItems = this.data.videoItems
if(!videoItems || videoItems.length <= 0) {
return -1
}
var offset = top - videoItems[0].top
for(var i=0; i<videoItems.length; i++) {
if(scrollPosition >= (videoItems[i].top + offset) && scrollPosition <= (videoItems[i].bottom + offset)) {
current = i
break
}
}
return current
},
}
})

边栏推荐
- 瑞萨RZ/G2L ARM开发板存储读写速度与网络实测
- Initial order of pointer (basic)
- Tensorflow 2. Chapter 15 of X (keras) source code explanation: migration learning and fine tuning
- Is there a free text to speech tool to help recommend?
- [shutter] image component (the placeholder | transparent_image transparent image plug-in is loaded into the memory)
- Array and collection performance comparison
- Thread start and priority
- Baidu AI Cloud takes the lead in building a comprehensive and standardized platform for smart cloud
- Some introduction and precautions about XML
- Kubernetes simple introduction to writing YML
猜你喜欢
![[case sharing] let the development of education in the new era advance with](/img/11/af88d16dc66f00840cbfc5ba5d68bd.jpg)
[case sharing] let the development of education in the new era advance with "number"

Sentry developer contribution Guide - configure pycharm

Shell 实现文件基本操作(sed-编辑、awk-匹配)

【AutoSAR 九 C/S原理架构】

Teach you JDBC hand in hand -- structure separation

Logback configuration file

数据分析思维分析犯法和业务知识——分析方法(一)

百度智能云牵头打造智能云综合标准化平台

1.12 - Instructions

1.11 - 总线
随机推荐
The difference between tail -f, tail -f and tail
Nc17059 queue Q
Lu Zhe, chief scientist of Shiping information: building data and personnel centered security capabilities
Rust ownership (very important)
Extension of flutter
【AutoSAR 十二 模式管理】
leetcode-224:基本计算器
There is an unknown problem in inserting data into the database
【爱死机】《吉巴罗》被忽略的细节
利亚德:Micro LED 产品消费端首先针对 100 英寸以上电视,现阶段进入更小尺寸还有难度
[AUTOSAR five methodology]
这不平凡的两年,感谢我们一直在一起!
Vulkan practice first bullet
关于QByteArray存储十六进制 与十六进制互转
Problèmes de configuration lex & yacc & Bison & Flex
RK3568开发板评测篇(二):开发环境搭建
Vulkan performance and refinement
Unity learns from spaceshooter to record the difference between fixedupdate and update in unity for the second time
Win10 多种方式解决无法安装.Net3.5的问题
数学建模之线性规划(含MATLAB代码)