chore:
This commit is contained in:
92
common/js/video-helper.js
Normal file
92
common/js/video-helper.js
Normal file
@@ -0,0 +1,92 @@
|
||||
// iOS 微信小程序 <video> 兼容工具
|
||||
// 问题背景:
|
||||
// 1) 弹窗内的 <video> 用 autoplay 不可靠——popup 初始隐藏,autoplay 在隐藏态不触发,
|
||||
// 等 popup 显示后也不会重新 autoplay,导致"安卓有时能播、iOS 不能";
|
||||
// 2) iOS 对同一个 <video> 实例在 src 重绑 / 弹窗重开 / 播放结束后,不会自动重新加载,
|
||||
// 需要靠 :key 重建实例或 seek(0) 才能重播;
|
||||
// 3) iOS 微信不支持 webm / vp9 / hevc / ogv 等封装或编码,会直接黑屏;
|
||||
// 4) H5 端(含微信内置浏览器)$nextTick 调 play() 太快,弹窗动画未完时浏览器内部
|
||||
// 会自动 pause(),导致 "The play() request was interrupted by a call to pause()" 错误。
|
||||
// 这里统一提供"格式检测"与"在视频可见后触发播放"的能力,供各视频组件复用。
|
||||
//
|
||||
// 重要:微信小程序的 <video>.play() 不要求用户手势(与 H5 不同),只要在视频元素
|
||||
// 已经渲染、popup 已经显示后调用即可。因此请务必在 uni-popup 的 @change(show=true)
|
||||
// 回调里调用本方法,而不是在 open() 里紧跟着 $nextTick 调用(那时元素尚未可见)。
|
||||
|
||||
// iOS 微信 <video> 已知不支持的封装/编码(仅按扩展名做保守判断,用于给出友好提示)
|
||||
const UNSUPPORTED_EXT_ON_IOS = ['webm', 'ogv', 'ogg', 'mov', 'm4v', 'hevc', 'av1', '3gp'];
|
||||
|
||||
// 取 URL 中的小写扩展名(忽略查询参数与 hash)
|
||||
export function getVideoExt(url) {
|
||||
if (!url || typeof url !== 'string') return '';
|
||||
const clean = url.split('?')[0].split('#')[0];
|
||||
const idx = clean.lastIndexOf('.');
|
||||
if (idx < 0) return '';
|
||||
return clean.slice(idx + 1).toLowerCase();
|
||||
}
|
||||
|
||||
// 该地址在 iOS 上是否很可能无法播放(基于扩展名的保守判断)
|
||||
export function isLikelyUnsupportedOnIOS(url) {
|
||||
const ext = getVideoExt(url);
|
||||
if (!ext) return false;
|
||||
return UNSUPPORTED_EXT_ON_IOS.indexOf(ext) !== -1;
|
||||
}
|
||||
|
||||
// 在"视频已经渲染、popup 已经显示"的前提下触发播放。
|
||||
// 必须在 uni-popup 的 @change(show=true) 回调里调用,此时 <video> 元素已可见,
|
||||
// createVideoContext 才能拿到有效实例。
|
||||
// videoId: <video> 的 id;instance: 当前组件/页面实例(组件内必传)。
|
||||
export function playVideoInContext(videoId, instance) {
|
||||
if (!videoId || !instance) return;
|
||||
|
||||
// H5 端需要更长延迟:等 uni-popup 动画播完(~350ms),否则浏览器内部会自动
|
||||
// pause() 导致 "play() interrupted by pause()" 错误;小程序端 $nextTick 即可。
|
||||
// #ifdef H5
|
||||
const delay = 400;
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
const delay = 0;
|
||||
// #endif
|
||||
|
||||
const doPlay = () => {
|
||||
let ctx = null;
|
||||
try {
|
||||
ctx = uni.createVideoContext(videoId, instance);
|
||||
} catch (e) {
|
||||
console.error('[video-helper] createVideoContext failed:', e);
|
||||
return;
|
||||
}
|
||||
if (!ctx) return;
|
||||
|
||||
try {
|
||||
ctx.play();
|
||||
} catch (e) {
|
||||
console.error('[video-helper] play() failed, will retry:', e);
|
||||
// H5 端首次 play() 常被浏览器自动 pause() 打断,延迟重试一次
|
||||
// #ifdef H5
|
||||
setTimeout(() => {
|
||||
try { ctx.play(); } catch (retryErr) {
|
||||
console.error('[video-helper] retry play() also failed:', retryErr);
|
||||
}
|
||||
}, 300);
|
||||
// #endif
|
||||
}
|
||||
};
|
||||
|
||||
if (delay > 0) {
|
||||
// H5:先等 nextTick(DOM 更新),再等 delay(弹窗动画完成)
|
||||
if (typeof instance.$nextTick === 'function') {
|
||||
instance.$nextTick(() => setTimeout(doPlay, delay));
|
||||
} else {
|
||||
setTimeout(doPlay, delay);
|
||||
}
|
||||
} else {
|
||||
// 小程序端:nextTick 即可
|
||||
if (typeof instance.$nextTick === 'function') {
|
||||
instance.$nextTick(doPlay);
|
||||
} else {
|
||||
doPlay();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -804,10 +804,10 @@ export default {
|
||||
}
|
||||
|
||||
&.select {
|
||||
background: var(--diy-category-tree-select-bg-color, #fff);
|
||||
background: var(--diy-category-tree-select-bg-color, var(--base-color-light-8, #FFEDE5));
|
||||
|
||||
view {
|
||||
color: var(--diy-category-tree-select-text-color, #333);
|
||||
color: var(--diy-category-tree-select-text-color, var(--base-color, #FF6B35));
|
||||
font-weight: bold;
|
||||
font-size: var(--diy-category-tree-select-size, 28rpx);
|
||||
}
|
||||
@@ -1527,10 +1527,10 @@ export default {
|
||||
}
|
||||
|
||||
&.select {
|
||||
background: var(--diy-category-tree-select-bg-color, #fff);
|
||||
background: var(--diy-category-tree-select-bg-color, var(--base-color-light-8, #FFEDE5));
|
||||
|
||||
view {
|
||||
color: var(--diy-category-tree-select-text-color, #333);
|
||||
color: var(--diy-category-tree-select-text-color, var(--base-color, #FF6B35));
|
||||
font-weight: bold;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
@@ -1614,11 +1614,11 @@ export default {
|
||||
}
|
||||
|
||||
&.select {
|
||||
background: var(--diy-category-tree-select-bg-color, #fff);
|
||||
background: var(--diy-category-tree-select-bg-color, var(--base-color-light-8, #FFEDE5));
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
view {
|
||||
color: var(--diy-category-tree-select-text-color, #333);
|
||||
color: var(--diy-category-tree-select-text-color, var(--base-color, #FF6B35));
|
||||
font-weight: bold;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user