uni-app从后端返回的富文本中的视频截取一帧为封面
如下图,下面的封面图从视频中截取而来
<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>标签的srcvideoUrls.push(match[1]);} else if (match[2]) { // <video>标签的srcvideoUrls.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 = 5video.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() {// 创建画布的宽高属性节点,就是图片的大小,单位PXvar 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.paramsthis.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-WEIXINthis.richContent = res.busNews.content;//#endifthis.XWtitle = res.busNews.title;this.createTime = res.busNews.createTime;this.videoLists = res.busNews.content;}})},//视频封面reciveMessage(val) {let that = thisthis.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.paramsthis.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-WEIXINthis.richContent = res.busNews.content;//#endifthis.XWtitle = res.busNews.title;this.createTime = res.busNews.createTime;this.videoLists = res.busNews.content;}})},//视频封面reciveMessage(val) {let that = thisthis.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>标签的srcvideoUrls.push(match[1]);} else if (match[2]) { // <video>标签的srcvideoUrls.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 = 5video.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() {// 创建画布的宽高属性节点,就是图片的大小,单位PXvar 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