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

相关推荐
游戏开发爱好者812 分钟前
在 iOS 开发、测试与上架过程中 如何做证书管理
android·ios·小程序·https·uni-app·iphone·webview
码界奇点1 小时前
基于Spring Boot 3与UniApp的跨平台新零售电商系统设计与实现
spring boot·uni-app·毕业设计·uniapp·零售·源代码管理
WeiAreYoung1 小时前
uni-app xcode 制作iOS原生插件
uni-app
2501_916007471 小时前
在没有 Mac 的情况下完成 iOS 应用上架 App Store
android·macos·ios·小程序·uni-app·iphone·webview
2501_9160088911 小时前
iOS 上架需要哪些准备,账号、Bundle ID、证书、描述文件、安装测试及上传
android·ios·小程序·https·uni-app·iphone·webview
时光慢煮16 小时前
从踩坑到跑通:uni-app 项目落地 HarmonyOS 的完整实录(含模拟器 / 真机)
华为·uni-app·harmonyos
FFF-X16 小时前
UniApp 小程序实现自定义每张图片播放时长的轮播图(基于 uView 的 u-swiper)
小程序·uni-app
奚大野...17 小时前
uni-app手机端项目touchmove禁止页面上下拉滑动
前端·javascript·uni-app
酒醉的胡铁19 小时前
uniapp运行到鸿蒙证书配置
服务器·uni-app·harmonyos
华玥作者21 小时前
uni-app + Vite 项目中使用 @uni-helper/vite-plugin-uni-pages 实现自动路由配置(超详细)
前端·uni-app·vue·vue3·vite