当前位置:网站首页>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
},
}
})
边栏推荐
- [shutter] image component (the placeholder | transparent_image transparent image plug-in is loaded into the memory)
- leetcode-2280:表示一个折线图的最少线段数
- 全志A40i/T3如何通过SPI转CAN
- Thank you for being together for these extraordinary two years!
- leetcode-224:基本计算器
- lex && yacc && bison && flex 配置的问题
- 【AutoSAR 十 IO架构】
- 【AutoSAR 九 C/S原理架构】
- [AUTOSAR + IO Architecture]
- University of Toronto: Anthony coach | the conditions of deep reinforcement learning can induce dynamic risk measurement
猜你喜欢
1.12 - Instructions
Liad: the consumer end of micro LED products is first targeted at TVs above 100 inches. At this stage, it is still difficult to enter a smaller size
百度智能云牵头打造智能云综合标准化平台
The difference between tail -f, tail -f and tail
【AutoSAR 二 AppL概述】
Logback configuration file
【AutoSAR 一 概述】
图解网络:什么是虚拟路由器冗余协议 VRRP?
1.11 - bus
[case sharing] let the development of education in the new era advance with "number"
随机推荐
Vulkan is not a "panacea"“
Advanced pointer (I)
Web2.0 giants have deployed VC, and tiger Dao VC may become a shortcut to Web3
[applet project development -- JD mall] user defined search component of uni app (middle) -- search suggestions
node_ Modules cannot be deleted
Web2.0的巨头纷纷布局VC,Tiger DAO VC或成抵达Web3捷径
Leetcode-1964: find the longest effective obstacle race route to each position
antv x6节点拖拽到画布上后的回调事件(踩大坑记录)
关于QByteArray存储十六进制 与十六进制互转
How to systematically learn machine learning
How to find out the currently running version of Solr- How do I find out version of currently running Solr?
Thread start and priority
【案例分享】让新时代教育发展与“数”俱进
File operation io-part2
【AutoSAR 二 AppL概述】
腾讯云免费SSL证书扩展文件含义
【日常训练】871. 最低加油次数
Kubernetes resource object introduction and common commands (V) - (NFS & PV & PVC)
Arduino开发之按键检测与正弦信号输出
Array common operation methods sorting (including ES6) and detailed use