如下图,下面的封面图从视频中截取而来

<template>
<view class="detail">
<view class="detail-content" :style="{paddingTop: height + 'px'}">
<view class="xwbiaoti">
{{XWtitle}}
</view>
<view class="fabutime">
{{createTime}}
</view>
<view class="fuwenbjx" ref="htmlContent">
<mp-html :content="richContent" show-img-menu="true" />
</view>
<view class='healthPromotion' :videoLists="videoLists" :change:videoLists="renderScript.createPoster">
</view>
<view class="lineView"></view>
</view>
</view>
</template>
使用使用renderjs 提取视频截图
<script module="renderScript" lang="renderjs">
export default {
methods: {
extractVideoUrls(htmlContent) {
// 假设视频通常在<iframe>或<video>标签中
const videoRegex = /<iframe.*?src="([^"]+)".*?>|<video.*?src="([^"]+)".*?>/g;
let match;
const videoUrls = [];
while ((match = videoRegex.exec(htmlContent)) !== null) {
if (match[1]) { // <iframe>标签的src
videoUrls.push(match[1]);
} else if (match[2]) { // <video>标签的src
videoUrls.push(match[2]);
}
}
return videoUrls;
},
createPoster(val) {
const htmlContent =
'<video src="https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"></video><video src="https://www.w3schools.com/html/mov_bbb.mp4"></video>';
const videoUrls = this.extractVideoUrls(val); //假设视频通常在<iframe>或<video>标签中 提取视频地址
var videoCanList = [],
curDateList = []
videoUrls.forEach((item, index) => {
var promise = new Promise((reslove, reject) => {
// 在缓存中创建video标签
var video = document.createElement("VIDEO")
// 通过setAttribute给video dom元素添加自动播放的属性,因为视频播放才能获取封面图
video.currentTime = 5
video.setAttribute('crossOrigin', 'anonymous');
video.setAttribute('autoplay', 'autoplay')
// 再添加一个静音的属性,否则自动播放会有声音
video.setAttribute('muted', 'muted')
// 上面我们只是创建了video标签,视频播放需要内部的source的标签,scr为播放源
video.innerHTML = '<source src=' + item + ' type="audio/mp4">'
// 再创建canvas画布标签
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// video注册canplay自动播放事件
video.addEventListener('canplay', function() {
// 创建画布的宽高属性节点,就是图片的大小,单位PX
var anw = document.createAttribute("width");
anw.nodeValue = 500;
var anh = document.createAttribute("height");
anh.nodeValue = 300;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
// 画布渲染
ctx.drawImage(video, 0, 0, 500, 300);
// 生成图片
var base64 = canvas.toDataURL('image/png') // 这就是封面图片的base64编码
// 视频结束播放的事件
video.pause()
curDateList.unshift({ // 这里是我自己的数据处理模块
type: 'video',
videoUrl: item.url,
img: base64
})
reslove(base64) // promise函数成功的回调
}, false)
})
videoCanList.push(promise)
})
Promise.all(videoCanList).then(res => {
this.$ownerInstance.callMethod('reciveMessage', res)
})
},
}
}
</script>
<script>
import url from '@/utils/URL.js'
export default {
data() {
return {
content: null,
XWtitle: '', //新闻标题
createTime: '', //发布时间
link: '',
xqid: '',
height: 0,
videoLists: [], // 获取到的视频列表数组
posterList: [], // 视频封面图数组
richContent: '', //带封面的富文本
viedeourlArry: [], //视频地址数组
}
},
components: {
},
created() {
},
onLoad(query) {
this.xqid = query.params
this.getNewsContent();
},
onShow() {
},
methods: {
getNewsContent() {
//新闻列表
this.$request({
url: url.news.newsinfo + this.xqid,
method: 'get',
}).then(res => {
if (res.code == 0) {
this.content = res.busNews.content;
// #ifdef MP-WEIXIN
this.richContent = res.busNews.content;
//#endif
this.XWtitle = res.busNews.title;
this.createTime = res.busNews.createTime;
this.videoLists = res.busNews.content;
}
})
},
//视频封面
reciveMessage(val) {
let that = this
this.posterList = val
that.richContent = that.processRichText(this.content)
},
processRichText(html) {
let that = this
// 匹配视频标签的正则表达式
let videoCount = 0;
const videoRegex = /<video[^>]*>/g;
return html.replace(videoRegex, (match) => {
// 检查是否已经有 poster 属性
if (!match.includes('poster=')) {
videoCount++;
// // 添加封面图,这里可以使用默认封面或根据视频生成
var posterUrl = this.posterList[videoCount - 1];
return match.replace('>', ` poster="${posterUrl}">`);
}
return match;
});
},
}
}
</script>
完整代码如下:
<template>
<view class="detail">
<view class="detail-content" :style="{paddingTop: height + 'px'}">
<view class="xwbiaoti">
{{XWtitle}}
</view>
<view class="fabutime">
{{createTime}}
</view>
<view class="fuwenbjx" ref="htmlContent">
<mp-html :content="richContent" show-img-menu="true" />
</view>
<view class='healthPromotion' :videoLists="videoLists" :change:videoLists="renderScript.createPoster">
</view>
<view class="lineView"></view>
</view>
</view>
</template>
<script>
import url from '@/utils/URL.js'
export default {
data() {
return {
content: null,
XWtitle: '', //新闻标题
createTime: '', //发布时间
link: '',
xqid: '',
height: 0,
videoLists: [], // 获取到的视频列表数组
posterList: [], // 视频封面图数组
richContent: '', //带封面的富文本
viedeourlArry: [], //视频地址数组
}
},
components: {
},
created() {
},
onLoad(query) {
this.xqid = query.params
this.getNewsContent();
},
onShow() {
},
methods: {
getNewsContent() {
//新闻列表
this.$request({
url: url.news.newsinfo + this.xqid,
method: 'get',
}).then(res => {
if (res.code == 0) {
this.content = res.busNews.content;
// #ifdef MP-WEIXIN
this.richContent = res.busNews.content;
//#endif
this.XWtitle = res.busNews.title;
this.createTime = res.busNews.createTime;
this.videoLists = res.busNews.content;
}
})
},
//视频封面
reciveMessage(val) {
let that = this
this.posterList = val
that.richContent = that.processRichText(this.content)
},
processRichText(html) {
let that = this
// 匹配视频标签的正则表达式
let videoCount = 0;
const videoRegex = /<video[^>]*>/g;
return html.replace(videoRegex, (match) => {
// 检查是否已经有 poster 属性
if (!match.includes('poster=')) {
videoCount++;
// // 添加封面图,这里可以使用默认封面或根据视频生成
var posterUrl = this.posterList[videoCount - 1];
return match.replace('>', ` poster="${posterUrl}">`);
}
return match;
});
},
}
}
</script>
<script module="renderScript" lang="renderjs">
export default {
methods: {
extractVideoUrls(htmlContent) {
// 假设视频通常在<iframe>或<video>标签中
const videoRegex = /<iframe.*?src="([^"]+)".*?>|<video.*?src="([^"]+)".*?>/g;
let match;
const videoUrls = [];
while ((match = videoRegex.exec(htmlContent)) !== null) {
if (match[1]) { // <iframe>标签的src
videoUrls.push(match[1]);
} else if (match[2]) { // <video>标签的src
videoUrls.push(match[2]);
}
}
return videoUrls;
},
createPoster(val) {
const htmlContent =
'<video src="https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"></video><video src="https://www.w3schools.com/html/mov_bbb.mp4"></video>';
const videoUrls = this.extractVideoUrls(val); //假设视频通常在<iframe>或<video>标签中 提取视频地址
var videoCanList = [],
curDateList = []
videoUrls.forEach((item, index) => {
var promise = new Promise((reslove, reject) => {
// 在缓存中创建video标签
var video = document.createElement("VIDEO")
// 通过setAttribute给video dom元素添加自动播放的属性,因为视频播放才能获取封面图
video.currentTime = 5
video.setAttribute('crossOrigin', 'anonymous');
video.setAttribute('autoplay', 'autoplay')
// 再添加一个静音的属性,否则自动播放会有声音
video.setAttribute('muted', 'muted')
// 上面我们只是创建了video标签,视频播放需要内部的source的标签,scr为播放源
video.innerHTML = '<source src=' + item + ' type="audio/mp4">'
// 再创建canvas画布标签
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// video注册canplay自动播放事件
video.addEventListener('canplay', function() {
// 创建画布的宽高属性节点,就是图片的大小,单位PX
var anw = document.createAttribute("width");
anw.nodeValue = 500;
var anh = document.createAttribute("height");
anh.nodeValue = 300;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
// 画布渲染
ctx.drawImage(video, 0, 0, 500, 300);
// 生成图片
var base64 = canvas.toDataURL('image/png') // 这就是封面图片的base64编码
// 视频结束播放的事件
video.pause()
curDateList.unshift({ // 这里是我自己的数据处理模块
type: 'video',
videoUrl: item.url,
img: base64
})
reslove(base64) // promise函数成功的回调
}, false)
})
videoCanList.push(promise)
})
Promise.all(videoCanList).then(res => {
this.$ownerInstance.callMethod('reciveMessage', res)
})
},
}
}
</script>
<style>
/*/ 富文本*/
.fuwenbjx {
line-height: 24px;
font-size: 29rpx;
}
.fuwenbjx img {
border-radius: 20rpx;
margin: 20rpx 0;
}
</style>
<style lang="scss" scoped>
.detail {
padding: 0;
padding-bottom: 54px;
line-height: 52rpx;
background: #fff;
.detail-content {
display: flex;
flex-direction: column;
margin-left: 10px;
margin-right: 10px;
height: 1000px;
img {
width: 80%;
margin: 0 auto;
}
.article-share {
display: none;
}
.detailText {
display: flex;
flex-direction: column;
margin-top: 20px;
margin-bottom: 10px;
.textContentTitle {
font-size: 14px;
color: rgba(85, 85, 85, 1);
text-decoration: underline;
}
.textContent {
font-size: 12px;
color: rgba(85, 85, 85, 1);
text-decoration: underline;
}
}
}
}
.xwbiaoti {
font-weight: bold;
color: #000;
font-size: 32rpx;
}
.fabutime {
color: #999;
font-size: 28rpx;
text-align: left;
margin-bottom: 10px;
}
</style>
参考文章:https://blog.csdn.net/yuanqi3131/article/details/128199069