fix(VideoCtrl): 重构全屏视频适配逻辑,修复适配bug

补充详细的全屏视频适配踩坑注释,重构适配算法,实现按屏幕比例最大化显示视频且不变形,修复了原代码中UI旋转、锚点和uvRect计算错误的问题
This commit is contained in:
“虞渠成” 2026-09-17 14:59:20 +08:00
parent 36e8055c26
commit eea5878552

View File

@ -162,13 +162,41 @@ namespace Kill.UI.Components
FitWindowAspect(windowImage, player.width / (float)Mathf.Max(1, (int)player.height));
if (needFullScreen)
{
// 根据视频宽高比选择全屏UI竖屏视频用竖屏全屏UI否则用默认全屏UI
// ============================================================
// 全屏视频适配经验总结(踩坑记录,避免后续重复犯错)
// ============================================================
// 目标:完整显示视频、不变形;在不同设备宽高比下都让视频占屏幕比例最大化。
//
// 1. UI 节点角色(场景已配置):
// - portraitFullImage未旋转的 RawImage专门用于竖屏视频
// - fullImage旋转了 -90° 的 RawImage专门用于横屏视频让 app 不旋转也能看横屏视频)
// - 选择规则video 高>宽 → portraitFullImage否则 → fullImage
//
// 2. 视觉屏幕 = 父 stretch 节点的实际 rect手机屏幕真实宽高
// 与子节点是否旋转无关。RectTransform 的 sizeDelta 在 UI 旋转 90° 后,
// (sizeDelta.x, sizeDelta.y) 视觉上变成 (sizeDelta.y, sizeDelta.x)。
//
// 3. 视频长边贴齐策略(按视频/屏幕比例选边):
// 判定 = 视频短边占屏幕比 = 1/videoAspect vs 屏幕宽高比
// - 1/videoAspect > 屏幕比 → 视频长边贴屏幕短边(视觉宽=parentW留黑上下
// 例:竖屏手机 750x1624 + 横屏视频 1.78 → 视觉=(750, 1335)sizeDelta=(1335, 750)
// - 1/videoAspect < 屏幕比 → 视频长边贴屏幕长边(视觉高=parentH留黑左右
// 例:接近正方形 1179x1600 + 横屏视频 1.78 → 视觉=(900, 1600)sizeDelta=(1600, 900)
// - 比例接近时(屏幕接近正方形)走"长边贴长边",让视频占用面积更大
//
// 4. 常见错误:
// - 用 cover 模式裁切 UV横屏视频在竖屏手机上会左右被裁丢失画面
// - 用 contain 让短边贴齐屏幕短边:在 750x1624 上视频会被压缩成窄长条
// - frameAspect 写反rotated90 时应该是 parentW/parentH 还是 parentH/parentW 要分清
// - 视觉屏幕写成了 rotated90 ? parentH : parentW视觉屏幕与旋转无关始终是 parentW x parentH
//
// 5. UV 不动:使用 contain 不裁切即可,无需调整 uvRect
// ============================================================
currentFullImage = (player.width < player.height && portraitFullImage != null) ? portraitFullImage : fullImage;
// 全屏纹理按视频分辨率创建,保证宽高比正确、不变形
RenderTexture fullTexture = new RenderTexture((int)player.width, (int)player.height, 1);
renderTextures.Add(fullTexture);
currentFullImage.texture = fullTexture;
// 显示区域铺满屏幕保持视频比例超出部分裁剪cover模式尽量铺满屏幕
float videoAspect = player.width / (float)Mathf.Max(1, (int)player.height);
var fullRt = currentFullImage.rectTransform;
var fullParent = fullRt.parent as RectTransform;
@ -177,41 +205,36 @@ namespace Kill.UI.Components
{
float parentW = fullParent.rect.width;
float parentH = fullParent.rect.height;
// 检测全屏UI是否旋转了90°横屏视频旋转90°后铺满竖屏手机屏幕
// 检测全屏UI是否旋转了90°
float rotZ = Mathf.Abs(Mathf.DeltaAngle(fullRt.localEulerAngles.z, 0f));
bool rotated90 = rotZ >= 45f && rotZ <= 135f;
if (rotated90)
// 视觉屏幕 = 父 stretch 覆盖区域即手机屏幕真实宽高与UI旋转无关
float screenVisualW = parentW;
float screenVisualH = parentH;
// 按视频/屏幕比例决定贴齐哪一边,确保视频占屏幕比例最大化
float visualW, visualH;
if ((1f / videoAspect) > (screenVisualW / screenVisualH))
{
// 旋转90°的UI矩形宽高互换旋转后视觉铺满屏幕
// 视频长边贴屏幕短边(横向贴满,留黑上下)
visualW = screenVisualW;
visualH = visualW * videoAspect;
}
else
{
// 视频长边贴屏幕长边(纵向贴满,留黑左右)
visualH = screenVisualH;
visualW = visualH / videoAspect;
}
// RectTransform 的 sizeDeltaUI 旋转 90° 时 (x,y) = (visualH, visualW)
float rtW = rotated90 ? visualH : visualW;
float rtH = rotated90 ? visualW : visualH;
fullRt.anchorMin = new Vector2(0.5f, 0.5f);
fullRt.anchorMax = new Vector2(0.5f, 0.5f);
fullRt.pivot = new Vector2(0.5f, 0.5f);
fullRt.sizeDelta = new Vector2(parentH, parentW);
}
else
{
fullRt.anchorMin = Vector2.zero;
fullRt.anchorMax = Vector2.one;
fullRt.pivot = new Vector2(0.5f, 0.5f);
fullRt.sizeDelta = Vector2.zero;
}
// 按视频比例裁剪cover旋转后视频宽对应屏幕高显示比例需取反
float frameAspect = rotated90 ? parentH / parentW : parentW / parentH;
if (Mathf.Abs(frameAspect - videoAspect) >= 0.001f)
{
if (videoAspect > frameAspect)
{
// 视频相对更宽:裁剪左右
float uvW = frameAspect / videoAspect;
currentFullImage.uvRect = new Rect((1 - uvW) / 2f, 0, uvW, 1);
}
else
{
// 视频相对更窄:裁剪上下
float uvH = videoAspect / frameAspect;
currentFullImage.uvRect = new Rect(0, (1 - uvH) / 2f, 1, uvH);
}
}
fullRt.sizeDelta = new Vector2(rtW, rtH);
fullRt.anchoredPosition = Vector2.zero;
}
}
videoPlayer.frame = 0;