响应式布局-官网开发备忘录

461 阅读11分钟

设计稿尺寸问题

一般有分两种,如:蓝湖

  • pc: 一般是1920px * 1080px
  • 移动端: 414px * 896px

pc

虽然给的PC是 1920px * 1080px,但实际的高度,往往会因为系统菜单,软件菜单,应用栏等高度占据一定的高度。

image.png

实际用户可视高度只有884px, 所以实际开发我们可以按1920px * 884px去调试。

由于设计稿的内容高度1080px超出了调试的高度884px 建议

  • 使用固定头部
  • 下面内容正常流滚动即可
  • 底部跟谁内容滚动

image.png

<div>
    <header>
    </header>
    <section>
    </section>
    <footer>
    </footer>
</div>
header{
    position:fixed; /* 如果header前面还有其他内容可以使用sticky,滚动后定住  */
    top: 0;
}
section {
    margin-top:70px;
}

宽度限制

由于虽然设计稿为1920,但是为了兼容更小的尺寸一般给的内容都是包含左右边距的,一般常见实际的宽度可能是1400, 我们可以设置max-width 限制显示的宽度,超过的我们都设置为居中即可。

image.png

<div class="container">
    <div class="row">
    <div>
<div>
.container { 
  height: calc(100vh - 70px); 
  overflow: hidden; 
}
.row {
  max-width: 1400px;
  height: inherit;
  margin: 0 auto; /* 居中对齐*/
  padding: 0 40px; /* 两边留一些边距*/
}

移动端

设计稿尺寸

移动端一般设计稿: 414px * 896px,由于浏览器一般会设置统一视口 <meta name="viewport" content="width=device-width, initial-scale=1.0">,所以最终直接按414px的大小开发即可,手机会自动把高分辨率压缩成对应的独立像素。即 414px

宽度确定 高度动态

由于移动端宽度是确定了,高度无法确定,依然跟pc一致

  • 使用头部固定
  • 内容自动滚动即可

image.png

不同尺寸的断点适配

我们以pc优先来编写

  • pc >= 1024
  • ipad >= 768 && <= 1023
  • mobile < 768

.pc {display: block;} 
.ipad {display: none;} 
.mobile {display: none;}
@media (max-width: 1920px) {
}
@media (max-width: 1600px) { 
}
@media (max-width: 1280px) { 
}
@media (max-width: 1080px) { 
}
@media (max-width: 1023px) {
.pc {display: none;} 
.ipad {display: block;} 
.mobile {display: none;}
}
@media (max-width: 768px) {  
} 
@media (max-width: 767px) {
  .pc {display: none;}
  .ipad {display: none;}
  .mobile {display: block;}
} 

使用

    <section class='pc'>
    
    </section >
     <section class='ipad'>
    
    </section >
     <section class='mobile'>
    
    </section >

优化写法

使用sass优化css断点写法

参考juejin.cn/post/741406…

菜单兼容处理

pc:左边logo,右边内容 image.png

移动端:左边logo,右边更多按钮,点击显示隐藏的菜单 image.png

为了区分逻辑,一般还是分两个写,防止后期代码冲突。

根据上面的断点,处理非pc 只需要针对 class设置为mobile,处理移动端即可(一般ipad跟mobile走一个逻辑)

<div class="pc">
<header :class="['border-b-[1px]','border-gray-400','w-full','flex','justify-center','fixed','top-0' ]">
        <div class="flex justify-between "> 
          <img class=" h-[6.25rem]" src="/assets/logo.png" alt="nrs" />
           <ul class="flex items-center ">
              <li  @click="gotoUrl('/')">
                <span >About Us</span> 
              </li>
               <li  @click="gotoUrl('/a')">
                <span >About Us</span> 
              </li>
               <li  @click="gotoUrl('/b')">
                <span >About Us</span> 
              </li> 
            </ul> 
        </div> 
      </header> 
</div>
<div class="mobile ipad">
 <header  >
        <div class="flex justify-between  ">
          <img class="w-[4rem] h-[4rem] ml-8" src="/assets/logo.png" /> 
          <img @click="clickShowMenu(!isShowMenu)" class="w-[2rem] h-[2rem] m-4 mr-8 " :src="isShowMenu ? close : menu" />
        </div> 
         <div v-if="isShowMenu" :class="['absolute','top-18'] ">
            <ul class="flex flex-col pt-8 pb-8">
              <li  @click="gotoUrl('/')">
                <span >About Us</span> 
              </li>
               <li  @click="gotoUrl('/a')">
                <span >About Us</span> 
              </li>
               <li  @click="gotoUrl('/b')">
                <span >About Us</span> 
              </li> 
            </ul>
         </div>
      </header>
</div>
   
<script setup lang="ts">
import menu from '@/assets/menu.png'
import close from '@/assets/close.png'
const route = useRoute();
const router = useRouter();
const isShowMenu = ref(false)

const clickShowMenu = (flag:boolean) => {
  isShowMenu.value = flag
  if(isShowMenu.value == true) {
    document.body.classList.add('no-scroll'); // 背景不能滚动
  }else {
    document.body.classList.remove('no-scroll');
  }
}
const gotoUrl = (url:string) => {
  clickShowMenu(false) 
  router.push(url)
}   
</script>     

单位

元素宽度使用vh

由于在pc端和移动里面,往往宽度是固定的,所以我们要做自适应,建议参照的单位以动态变化的高度为主, 建议使用vh做参照单位

margin,padding使用 vw 和 vh

margin,padding ,左右用vw和 vh ,上下用vh

vw根据宽度动态适应

font-size 可以使用vw做参考单位,em rem也可以

.container .title {
  font-size: 4.9vw; /* 94px / 1920px */
}
.container .subtitle {
  font-size: 2.917vw;   /* 56px / 1920px */
}

全局适配

rem

    <style>
        html{
            font-size: 20px
        }
        div{ 
            width: 5rem;
            height: 5rem; 
        }
    </style>

em

    div{
        font-size: 15px;
        width: 5em;
        height: 5em; 
    }

em rem 使用场景

位于默认font-size为16px不好运算,为了方便运算,我们可以统一把font-size设置成一个好运算的比例值,然后不同断点,设置对应的相对尺寸。

  • 当你整个项目的维护者,则直接用rem 设置html的font-size即可。
  • 当你们的代码是内嵌在别的框架内,为了避免互相影响,就不能使用rem,只能使用相对的em。

.container {
 font-size: 100px;
}

.container .title {
  font-size: 0.48em; /* 即设计稿上面如果是48px,只需要往右移两位即可。*/
}
.container .subtitle {
  font-size: 0.24em; /* 即设计稿上面如果是24px,只需要往右移两位即可。*/
}

@media (max-width: 1680px) {
  .container {
    font-size: 85px;
  }
}
 
@media (max-width: 1440px) {
  .floor-container {
    font-size: 80px;
  }
}


@media (max-width: 1080px) {
  .floor-container {
    font-size: 75px;
  } 
}

/* ipad端 1023 - 768 */
@media (max-width: 1023px) {  
  .floor-container {
    font-size: 75px; 
  }
}

 

/* 移动端767px 注意当尺寸为手机端,可以直接用px设置具体的元素 ,因为已经到了最小尺寸了 */
@media (max-width: 767px) { 
  .container {
    font-size: 70px;
  }
}

@media (max-width: 375px) {

}

vw / vw+rem

直接用vw

div{
    width:10vw; 
    height:10vw;
}

html 的font-size用 vw,内容用rem

html{
    font-size: 10vw; /* 如果当前是950 , 10vw就是95*/
}
div{
    width:1rem;   /* 95px*/
    height:1rem; /*   95px*/
}

背景图

自适应填满背景图

<div class="container">
    <div class="row">
        <div class="row-content">
           
        </div>
    </div>
  </div>
.container { 
    position: relative; 
    background: none no-repeat center center;
    background-size: cover;
    height: calc(100vh - 70px);  /*减去固定菜单头部*/
    background-color: #000;
    background-image: url(xxxxx);
} 

调试

插件

可以借助浏览器插件如:Window Resizer microsoftedge.microsoft.com/addons/deta…

image.png

默认提供常用的尺寸

浏览器自带调试工具

image.png

其他

视频自适应高度

通过视频设置为填满的背景,然后通过cover自适应屏幕当前的尺寸,达到自适应效果

div video {
    width: 100%;
    height: 100%;
    position: absolute;
    object-fit: cover;
} 
<div>
    <video muted="" webkit-playsinline="" playsinline="" preload="auto" name="media" class="video pc" autoplay="" loop="">
            <source src="xxxx" type="video/mp4">
    </video>
    <div class="row"> 
    </div>
  </div>

上下居中处理

有些页面需要做垂直居中,建议使用flex 控制

.row {
    display: flex;
    margin: 0 auto;
    align-items: center;
    justify-content: space-between;
}
.row .item{
  
}

上下的高度建议使用vh控制

复杂的响应式布局

image.png 参考 juejin.cn/post/733158…

扩展概念

常用单位

英寸

就是设备屏幕对角线的长度,物理世界的真实长度。

  • 1英寸 = 2.54厘米
  • iphone se 尺寸: 4.7英寸

像素

每一个像素具有颜色和位置,硬件上实际显示的点。也叫设备的物理像素

分辨率

屏幕由像素组成分辨率。

英寸 和 分比率关系

手机

我们可以看到相同的手机尺寸会有不同的分辨率

如 :

  • 小米XX : 6.5寸 2340* 1080 , 像素点数量: 2,918, 400
  • 苹果XX : 6.5寸 2560 * 1440 , 像素点数量:2,527, 200

可以看到相同尺寸拥有不同分布率,分辨率高的会清晰一点

电视机/显示器

相同的尺寸,拥有更多的像素点显示内容会更清晰。 如电视机21寸,1024 *760 就比 800 * 600 要清晰的多。

电脑分辨率 设置

  • 当显示器尾1920* 1080 ,设置 960 * 540 时候,显示器会模拟像素点。会把用多个像素模拟单个像素显示(是多个像素模拟一个模糊的像素)
  • 苹果最新显示设置 支持 960 * 540 (HiDPI)会把(是多个像素模拟一个高清的像素),

像素点和清晰度的关系(PPI)

PPI:每英寸包含的像素点,ppi越高越清晰 公式:开平方根(水平像毫点数(平方)+垂直像赛点数(平方)) = ppi

独立像素

iphone 不同的尺寸为什么可以达到一样的显示效果。 假设 300 * 300px 的div ,在320 * 480 显示,接近蹭满。 在640*960 手机上,应该只会显示显示一半。

如果不同设备都做兼容,不同设备都要单独开发,成本很高。

为了统一乔布斯引入的独立像素,所有高清设备最终都要转化成独立像素,只要独立开发即可。

ratina屏幕

低分比率:1 * 1个像素 = ratina 高分辨率: 2 * 2 个像素显示。

注意显示的手机分比率都比显示起要高。 所以看起来才感觉高清。

iphone6

  • 物理像素 750 * 1334
  • 设备独立像素 375 * 667

设备例外: iPhone6 plus

  • 物理像素 1080 * 1920
  • 设备独立像素 414 * 736

可以通过浏览器模拟手机 image.png

还原pc

image.png

例子

如果要显示 300 * 300 div 在不同iphone都一样

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        div{
            width: 300px;
            height: 300px;
            background-color: red;
        }
    </style>
</head>
<body>
        <div></div>
</body>
</html> 
  • div元素 300 * 300
  • iphone6 128 * 128
  • 布局视口 980
  • 设备独立像素 375 * 667
实际显示的div =  设备独立像素宽度 / 布局视口 * div宽度
                375         / 980       * 300   = 114
  • 布局视口 == 设备独立像素 , 实际显示的div == css里面设置的 300px
  • 要做的就是把默认的 980 设置为 375

视口

布局视口

基准视口:无论pc和移动端默认布局宽度 980px, 是一个固定的物理宽度

  • document.documentElement.clientWidth(pc不包含滚动条,菜单栏)
  • document.innerWidth(pc包含滚动条,菜单栏)
  • pc会随着宽度拉大而变化
  • 移动端由于不会动态拉大,所以都是一个默认值980

image.png

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        div{
            width: 970px;
            height: 300px;
            background-color: red;
        }
    </style>
</head>
<body>
        <div></div>
</body>
</html> 

我们可以看到当不特殊声明的时候,在移动端 默认是视口980的宽度,我们设置970基本上快填满

视觉视口

image.png

image.png

  • 尤其是移动端显示的宽,比如 iphone是独立像素宽度是 375 * 667,虽然iphone的尺寸是750 但是实际是按 独立像素显示,所以理想的宽度还是 375
  • window.screen.width

理想视口

就是布局视口 == 设备独立像素

通过设置 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 实现

image.png

属性描述
width正整数 或 device-width以像素为单位,定义布局视口的宽度,一般默认980px
height正整数 或 device-height以像素为单位,定义布局视口的高度
initial-scale允许是小数定义页面初始缩放比例
minimum-scale0.0 - 10.0定义缩放的最小值
maximum-scale允汻是小数定义缩放的最大值(ios10&ios10+无效)
user-scalableyes / no设置是香允许缩放,同上无效
  • initial-scale = 等比缩放: 设备独立像素/视觉视口宽度

  • 视觉视口宽度 = 设备独立像素 / initial-scale

  • device-width 默认就是 设备独立像素的宽度

  • ios10及 ios10+设置最大缩放值无效

  • initial 和 width 是有冲突的

  • initial 和 最小值 是一致的

  • 部分安卓机型,不接受width=具体数值

  • 正常情況下,我们会把初始最大都设置为1、不允许用户缩放而而

结论

  • meta viewport 标签只对移动端浏览器有效,对 PC 端浏览器是无效的。
  • 当移动端的时候,设置 width=device-width 后,如iphone, initial-scale的值原来 是 375/980,变成375/375 = 1,达到 布局视口 == 视觉视口 == 理想视口

适配

由于iphone也有不同的尺寸,如 iphone se 和 iPhone xr。一个是 375 一个是414 。

方案1 直接写PX + meta设置scale缩放

  • 适合已有已经直接写px的项目
  • 先根据当前写死px是根据多少尺寸宽度编写,然后拿到比例值
  • 在动态运行时,判断当前设备的尺寸,再通过 meta设置scale缩放
«script>

var meta = document. createElement("meta");
var scale = 1 / window. devicePixelRatio;
meta. name = "viewport":
meta.content = "initial-scale" + scale +",minimum-scale"+
scale+",maximum-scale="+ scale+" ,user-scalable=no": 
document. head.appendchild(meta);

方案2 视口宽度 + html设置font-size em 或 rem 。

var widths = document.documentElement.clientwidth; 
var num = 10;
html.style.fontsize = widths / num + 'px'; //通过当前浏览器布局视口宽度 来动态计算所有元素的宽度。 除于10 方便计算。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div{
            width: 5rem;
            height: 5rem;
            background-color: red;
        }
    </style>
</head>
<body>
        <div></div>

        <script>
            var widths = document.documentElement.clientWidth; 
            var num = 10;
            document.documentElement.style.fontSize = widths / num + 'px'; //通过当前浏览器布局视口宽度 来动态计算所有元素的宽度。 除于10 方便计算。 
        </script>
</body>
</html>

当在iphone se div宽度是187.5px fontsize是37.5px image.png

当在iphone xr div宽度是207px fontsize是41.4px image.png

滚动到顶部

  • 滚动按钮默认隐藏
  • 监听滚动事件,如果滚动条超过当前屏幕,则显示按钮
  • 如果滚动条小于超过当前屏幕,则隐藏按钮

Dec-31-2024 10-52-46.gif

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>backtotop</title> 
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<style>
    #header {
        padding-bottom: 2000px;
        border: 1px solid #000;
    }

    #footer {
        padding-bottom: 2000px;
        border: 1px solid #000;
    }

    .container {
        position: relative;
        font-size: 16px; 
        /* overflow: hidden; */
    }

    /* 返回首页 */
    .product-back-to-top {
        position: fixed;
        right: 5vw;
        bottom: 5vw;
        cursor: pointer;
        width: 80px;
        height: 80px;
        border-radius: 40px;
        display: flex; 
        z-index: 11;
        flex-direction: column;
        align-items: center;  
        font-weight: bold;
        justify-content: space-evenly;
        box-shadow: 0 10px 16px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
        opacity: 0.8;
        /* 默认不显示 */
        visibility: hidden;
    }

    .product-back-to-top:hover {
        opacity: 1;
    }

    .product-back-to-top i {
        transform: rotate(-90deg);
        font-size: 32px;
        vertical-align: middle;
    } 
</style>

<body>
    <div class="container">
        <div class="product-back-to-top"> 
            <span>Top</span>
        </div>
        <div id="header">
            我是header
        </div>
        <div class="gsap">
            我是内容
        </div>
        <div id="footer">
            我是footer
        </div>
    </div>
    <script>
        $(document).ready(function() { 
            let fixedStartVal = 70; 
            if ($(window).width() < 1023) { // ipad 或移动
                fixedStartVal = 52;
            }
            // 返回顶部
            $('.product-back-to-top').click(function () { 
                var container = $('html');
                let offset = $("#header").offset().top - fixedStartVal 
                container.animate({ scrollTop: offset })
            })
            checkBackToTopState()
            function checkBackToTopState() {
                let nowTop = $(window).scrollTop()
                let screenHeight = $(window).height() 
                console.log("nowTop",nowTop,screenHeight) 
                // if ($(window).width() < 767) { //只有ipad 或 移动端才控制
                    if (nowTop < screenHeight) {
                        $('.product-back-to-top').css("visibility", "hidden")
                    } else {
                        $('.product-back-to-top').css("visibility", "visible")
                    }
                // } 
            }
            $(window).scroll(function () {
                checkBackToTopState() 
            })

        }); 
    </script>
</body>

</html>