带鱼屏适配,我用了最笨的办法
要把官网适配带鱼屏。说实话一开始挺头疼的,21:9 的屏幕和常规 16:9 差太多了,设计那边给的稿子是 2500×1080,核心内容还是 1920×1080。
网上搜了一圈,方案五花八门,什么媒体查询断点、rem 适配、vw/vh 方案... 试了几个都不太顺手。最后用了个最笨的办法 —— 整体 scale 缩放。经过实践效果还不错。
超宽屏幕 2900×1080 分辨率的显示效果:页面居中两边留白
常规屏幕 1920×1080 分辨率的显示效果:只显示核心内容
较窄屏幕 900×1080 分辨率的显示效果:显示核心内容,上下留白
实际效果参考莉莉丝游戏的 《帕萌战斗日记》官网
先说思路
两层容器嵌套:
- 外层 2500×1080,带鱼屏用户能看到的扩展区域
- 内层 1920×1080,核心内容区,普通屏幕也能完整显示
然后根据视口大小,算一个缩放比例,整体 scale。就这么简单。
核心就一行 CSS
--s: min(100vw / 1920px, 100vh / 1080px);
取宽高缩放比的最小值,保证页面不会超出屏幕边界。
这里用了 CSS 的 min() 函数,直接在样式里算。省得写 JS 监听 resize 了。
容器结构
#element_pc (撑满视口,overflow: hidden)
└── #container_2500 (2500×1080,加 scale 缩放)
└── .box_1920 (1920×1080,居中放核心内容)
具体代码
HTML 结构
<div id="element_pc">
<div id="container_2500" class="swiper MySwiper">
<!-- 顶部导航,fixed 定位 -->
<div class="header">...</div>
<!-- Swiper 垂直轮播 -->
<div class="swiper-wrapper">
<div class="swiper-slide section1">
<!-- 2500 区域可以放带鱼屏专属的装饰元素 -->
<div class="box_1920">
<!-- 1920 区域放主要内容 -->
</div>
</div>
<div class="swiper-slide section2">...</div>
</div>
</div>
</div>
CSS 核心部分
#element_pc {
width: 100vw;
height: 100vh;
position: relative;
overflow: hidden;
/* 设计稿尺寸,改这俩变量就行 */
--designW: 1920px;
--designH: 1080px;
--s: min(100vw / var(--designW), 100vh / var(--designH));
}
#container_2500 {
width: 2500px;
height: 1080px;
position: absolute;
left: 50%;
top: 50%;
/* 先居中,再缩放 */
transform: translate(-50%, -50%) scale(var(--s));
background-color: #e7e7e7;
}
.box_1920 {
width: var(--designW);
height: var(--designH);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
记得加上浏览器前缀:
-webkit-transform: translate(-50%, -50%) scale(var(--s));
-ms-transform: translate(-50%, -50%) scale(var(--s));
JS 部分
用了 Swiper 做垂直轮播,配合导航栏联动。这块没什么特别的,常规操作:
var mySwiper = new Swiper('.MySwiper', {
direction: 'vertical',
mousewheel: { forceToAxis: true },
keyboard: { enabled: true },
on: {
slideChange: function () {
let indexObj = { 0: 'NAV1', 1: 'NAV2' }
hederChangeNav(indexObj[this.activeIndex]);
},
},
})
function hederChangeNav (val) {
$('.header .nav_item').removeClass('active');
$('.header .nav_item.' + val).addClass('active');
mySwiper.slideTo(val === 'NAV1' ? 0 : 1);
}
不同屏幕啥效果
实测了几种屏幕:
| 屏幕类型 | 效果 |
|---|---|
| 16:9 普通屏 | 显示 1920 核心区域,2500 两边被裁掉,正常使用 |
| 21:9 带鱼屏 | 2500 区域完整显示,两边的装饰元素都能看到 |
| 32:9 超宽屏 | 上下会有黑边,因为高度先达到缩放上限 |
32:9 的黑边其实也能接受,毕竟那种屏幕本来就少。真要适配可以再套一层更宽的容器,但感觉没必要。
踩的坑
1. fixed 定位不跟着缩放
导航栏用的 fixed 定位,结果发现它不受父容器 scale 影响。
一开始想着把导航也放 scale 容器里,但那样滚动的时候导航会跟着动。最后还是单独处理了,导航栏宽度写死,位置用百分比适配。
2. CSS min() 兼容性
min() 函数不支持 IE,老版本 Safari 也有问题。
如果要兼容老浏览器,得用 JS 算缩放比例,监听 resize 事件动态设置。大概这样:
function updateScale() {
const s = Math.min(
window.innerWidth / 1920,
window.innerHeight / 1080
);
document.getElementById('container_2500').style.transform =
`translate(-50%, -50%) scale(${s})`;
}
window.addEventListener('resize', updateScale);
updateScale();
不过现在项目不用管 IE 了,直接 CSS 搞定。
3. 字体模糊
scale 缩放可能导致字体模糊,特别是缩放比例不是整数的时候。
没找到完美方案,加了个 transform-origin: center 稍微好点。如果真的很在意,可能得用别的方案了。
文件怎么放
实际项目里我是这么组织内容的:
- 主要内容、交互元素 → 放
.box_1920里,确保普通屏幕也能正常用 - 装饰性背景、扩展视觉元素 → 放
#container_2500里,带鱼屏用户能看到更多
比如首屏背景图可以用 2500 宽的,两边是延伸的装饰,中间 1920 是主体。普通屏用户看到的就是 1920 的部分,带鱼屏用户能看到完整的 2500。
这方案适合什么场景
适合:
- 官网、落地页这种展示型页面
- 设计稿是固定尺寸的
- 不需要考虑移动端(移动端另外写一套)
- 不用支持 IE
不适合:
- 内容型网站,比如博客、文档站
- 需要响应式布局的
- 对文字清晰度要求极高的
这个方案说白了就是"设计稿多大就做多大,然后整体缩放"。优点是简单,设计稿是啥样,页面就是啥样,不用考虑响应式布局,不用算百分比,所有尺寸直接写 px。缺点也很明显,只适合展示型页面,内容型网站别用这个,文章页面缩来缩去体验很差。其实我一直觉得这方案有点"作弊"的感觉,不够优雅。但能用、好维护、上线快,有时候这就够了。毕竟代码是写给人看的,也是写给 deadline 看的。
代码我放在github了,github.com/xxhe1024/ul…
有更好的方案欢迎评论区交流。