微信小程序踩坑之video组件黑屏闪现 bat365官方网站 📅 2025-11-03 07:13:48 👤 admin 👁️ 1019 ❤️ 828 video组件即使添加poster属性,依然黑屏闪现 1.1、若 controls 属性值为 false 则设置 poster 无效:1.2、底部的黑屏1.3、objectFit="fill",但视频似乎还是没有完整填充:旁边的黑边定位问题过程:1. 样式影响2. 视频有黑边3. 视频宽高比与容器宽高比不匹配object-fit动态计算容器宽高比服务端视频预处理:将视频和容器尺寸匹配 hack方案结合父级 overflow: hidden; 子级 `css transform` / margin: -1rpx缩 放新增一个层级盖住最终方案 1.4、加载视频较完善实现:结合多次播放 1.1、若 controls 属性值为 false 则设置 poster 无效: 补加图片组件兜底,且需要ready周期时就提前加载元素,避免资源空态太久 1.2、底部的黑屏 不是背景色,未加载到的时候展示兜底图 1.3、objectFit=“fill”,但视频似乎还是没有完整填充:旁边的黑边 很遗憾,暂时并未解决,也未通过任何方式进行hack处理成功 环境:skyline引擎、调试最低版本3.6.6 机型表现:and、IOS(IOS黑边更明显,and稍细,但ios部分机型似乎可以通过margin:-1rpx 父级overflow:hidden做隐藏) 定位问题过程: 1. 样式影响 新建页面,纯video标签 2. 视频有黑边 替换线上平台其他正常视频 3. 视频宽高比与容器宽高比不匹配 object-fit src="video.mp4" style="width: 100%; height: 100%; object-fit: cover;" /> cover:保持比例填满容器,裁剪多余部分(无黑边,可能丢失边缘内容)fill:拉伸视频填满容器,不保持比例(无黑边,但可能变形)contain:保持比例完整显示,可能有黑边(默认行为) 动态计算容器宽高比 通过 JS 获取视频原始比例,动态设置容器高度: Page({ data: { videoAspectRatio: 0 }, onVideoLoaded(e) { const { width, height } = e.detail const videoAspectRatio = width / height; const systemInfo = wx.getSystemInfoSync() const containerWidth = systemInfo.windowWidth const containerHeight = containerWidth / videoAspectRatio this.setData({ containerHeight }); } }) style="width: 100%; height: {{containerHeight}}px;" > src="video.mp4" style="width: 100%; height: 100%;" bindloadedmetadata="onVideoLoaded" /> 服务端视频预处理:将视频和容器尺寸匹配 其实,直接通过一个video标签去设置跟视频相同的尺寸即可,不设置的时候视频有默认尺寸。 hack方案 结合父级 overflow: hidden; 子级 css transform / margin: -1rpx缩 放 通过缩放视频消除黑边(适用于固定比例容器): .video-container { width: 100%; height: 400rpx; overflow: hidden; } // video标签 .video-content { width: 100%; height: 100%; transform: scale(1.1); /* 根据实际比例调整 */ } 新增一个层级盖住 盖不住 最终方案 不是video必须场景,建议使用apng等动画方式替代video必须场景,放置一个封面图,点击全屏/放大播放(可以按尺寸处理后,背景自动黑屏) 1.4、加载视频较完善实现:结合多次播放 wx:if="{{showCoverImage}}" src="{{videoPoster}}" class="videoContainer-cover" /> id="curVideo" class="videoContainer-video" src="{{videoSrc}}" poster="{{videoPoster}}" loop="{{true}}" autoplay="{{true}}" muted="{{true}}" bindloadedmetadata="onLoadedData" binderror="onError" objectFit="fill" controls="{{false}}" showFullscreenBtn="{{false}}" showPlayBtn="{{false}}" showProgress="{{false}}" enablePlayGesture="{{false}}" /> // less .videoContainer { width: 100%; height: 100%; position: relative; display: flex; justify-content: center; align-items: center; overflow: hidden; &-poster { position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; z-index: 2; border-radius: inherit; background-color: var(--white); } &-video { position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; z-index: 1; overflow: hidden; margin: 0; padding: 0; border: none; object-fit: fill; outline: none; /* 移除聚焦时的轮廓 */ display: block; /* 避免行内元素空隙 */ background-color: var(--white); } } // 预加载单张图片 preloadImage(url: string): Promise { return new Promise((resolve, reject) => { wx.getImageInfo({ src: url, success: () => { console.log('preloadImage success'); resolve(); }, fail: (err) => reject(err), }); }); }, /** * 预加载视频 * 1. 创建视频上下文 * 2. 调用play()方法预加载视频(不自动播放) */ preloadVideo() { try { // 创建视频上下文: this一定要添加,不然实际play无法正常调用 const videoContext = wx.createVideoContext('Video', this); // 加载视频 // videoContext.play(); // videoContext.pause(); } catch (error) { console.error('preloadVideo error', error); } }, // 视频加载完成 async onLoadedMetaData(e) { console.log('e.detail', e.detail); const { width, height } = e.detail; // 固定宽高? console.info('mp4 onLoadedMetaData 视频加载完成 before', this.data.isVideoLoadEnd); // 不知道为什么重复播放的话,会多次加载完成。所以做一个拦截动作 if (this.data.isVideoLoadEnd) { return; } // 避免覆盖图片隐藏,视频还未正常出现抖动 setTimeout(() => { this.setData({ showCoverImage: false, }); }, 200); // 设置视频相关状态 this.setData({ isVideoPlayEnd: true, isVideoLoadEnd: true, playVideoTimer: setTimeout(() => { this.playVideo(); }, videoStartTime), }); }, /** * 播放视频方法 * 1. 检查视频是否已播放结束,避免重复播放 * 2. 控制视频最多播放 videoPlaytime 次 * 3. 捕获并处理播放过程中的异常 */ playVideo() { try { // 从组件数据中获取播放次数、视频上下文和播放状态 const { playCount = 0, videoContext = null, isVideoPlayEnd = false } = this.data || {}; // 如果视频未播放结束,直接返回避免重复播放 if (!isVideoPlayEnd) { return; } // 控制视频最多播放 videoPlaytime 次 if (playCount < videoPlaytime) { // 更新播放次数 this.setData({ playCount: playCount + 1, }); // 调用视频上下文播放方法 videoContext?.play(); } else { this.clearExistingTimer(); } } catch (error) { // 捕获并记录播放错误 console.error('playVideo error', error); } }, // 视频加载出错 onError(info) { console.error('视频加载出错'); this.setData({ showPoster: true }); }, /** * 视频播放结束事件处理 * 1. 清除现有的定时器 * 2. 设置视频结束状态并启动新的定时器,在指定间隔后重新播放视频 */ onEnded() { // 清除现有的定时器 this.clearExistingTimer(); // 设置视频结束状态并启动新的定时器 this.setData({ isVideoPlayEnd: true, playVideoTimer: setTimeout(() => { this.playVideo(); }, videoInterTime), }); }, ← 含羞草为什么一碰就合拢的原因是什么? - 养花技巧的家庭养花技巧 聊几句山东方言:夜来,拉呱、噶哄、度什么 →