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>标签的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

相关推荐
2501_915918414 小时前
iOS 混淆与 IPA 加固一页式行动手册(多工具组合实战 源码成品运维闭环)
android·运维·ios·小程序·uni-app·iphone·webview
Q_Q51100828518 小时前
python+uniapp基于微信小程序团购系统
spring boot·python·微信小程序·django·uni-app·node.js·php
炒毛豆18 小时前
uniapp微信小程序+vue3基础内容介绍~(含标签、组件生命周期、页面生命周期、条件编译(一码多用)、分包))
vue.js·微信小程序·uni-app
盛夏绽放1 天前
关于 uni-app 与原生微信小程序中的生命周期 —— 一次“生命旅程”的解读
微信小程序·小程序·uni-app
知识分享小能手1 天前
uni-app 入门学习教程,从入门到精通,uni-app 基础知识详解 (2)
前端·javascript·windows·学习·微信小程序·小程序·uni-app
2501_916008891 天前
iOS 发布全流程详解,从开发到上架的流程与跨平台使用 开心上架 发布实战
android·macos·ios·小程序·uni-app·cocoa·iphone
风清云淡_A1 天前
【uniapp】uni.uploadFile上传数据多出一个304的get请求处理方法
uni-app
shykevin1 天前
uni-app x商城,商品列表组件封装以及使用
windows·uni-app
cesske1 天前
uniapp 编译支付宝小程序canvas 合成图片实例,支付宝小程序 canvas 渲染图片 可以换成自己的图片即可
小程序·uni-app·apache