带鱼屏适配,我用了最笨的办法

0 阅读5分钟

要把官网适配带鱼屏。说实话一开始挺头疼的,21:9 的屏幕和常规 16:9 差太多了,设计那边给的稿子是 2500×1080,核心内容还是 1920×1080。

网上搜了一圈,方案五花八门,什么媒体查询断点、rem 适配、vw/vh 方案... 试了几个都不太顺手。最后用了个最笨的办法 —— 整体 scale 缩放。经过实践效果还不错。

超宽屏幕 2900×1080 分辨率的显示效果:页面居中两边留白 image.png

常规屏幕 1920×1080 分辨率的显示效果:只显示核心内容 image.png

较窄屏幕 900×1080 分辨率的显示效果:显示核心内容,上下留白 image.png

实际效果参考莉莉丝游戏《帕萌战斗日记》官网

先说思路

两层容器嵌套:

  • 外层 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…

有更好的方案欢迎评论区交流。