# 1 移动端适配

为什么要做适配

  • 为了适应各种移动端设备,完美呈现应有的布局效果
  • 各个移动端设备,分辨率大小不一致,网页想铺满整个屏幕,并在各种分辨下等比缩放

# 适配方案

  • 固定高度,宽度百分比适配-布局非常均匀,适合百分比布局
  • 固定宽度,改变缩放比例适配-什么情况都可以
  • Rem适配
  • 像素比适配

单位

  • em根据元素自身的字体大小计算,元素自身 16px 1em=16px
  • Rem R -> root 根节点( html ) 根据html的字体大小计算其他元素尺寸

百分比适配

固定高度,宽度百分比适配

  • 根据设置的大小去设置高度,单位可以用px 百分比 auto
  • 常用Flex布局
  • 百分比宽度

640设计稿为例,在外层容器上设置最大以及最小的宽

#wrapper {
    max-width: 640px; /*设置设计稿的宽度*/
    min-width: 300px;
    margin: 0 auto;
}
@程序员poetry: 代码已经复制到剪贴板

后面的区块布局都用百分比,具体元素大小用px计算

Rem适配(常用)

  • 根据屏幕的分辨率动态设置html的文字大小,达到等比缩放的功能
  • 保证html最终算出来的字体大小,不能小于12px
  • 在不同的移动端显示不同的元素比例效果
  • 如果htmlfont-size:20px的时候,那么此时的1rem = 20px
  • 把设计图的宽度分成多少分之一,根据实际情况
  • rem做盒子的宽度,viewport缩放

head加入常见的meta属性

<meta name="format-detection" content="telephone=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!--这个是关键-->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,minimum-scale=1.0">
@程序员poetry: 代码已经复制到剪贴板

把这段代码加入head中的script预先加载

// rem适配用这段代码动态计算html的font-size大小
(function(win) {
    var docEl = win.document.documentElement;
    var timer = '';

    function changeRem() {
        var width = docEl.getBoundingClientRect().width;
        if (width > 750) { // 750是设计稿大小
            width = 750;
        }
        var fontS = width / 10; // 把设备宽度十等分 1rem<=75px
        docEl.style.fontSize = fontS + "px";
    }
    win.addEventListener("resize", function() {
        clearTimeout(timer);
        timer = setTimeout(changeRem, 30);
    }, false);
    win.addEventListener("pageshow", function(e) {
        if (e.persisted) { //清除缓存
            clearTimeout(timer);
            timer = setTimeout(changeRem, 30);
        }
    }, false);
    changeRem();
})(window)
@程序员poetry: 代码已经复制到剪贴板

像素比适配

  • window.devicePixelRatio
  • 物理像素是手机屏幕分辨率
  • 独立像素 指css像素 屏幕宽度
  • 像素比 = 物理像素 / css宽度
  • 获取设备的像素比 window.devicePixelRatio

# 2 移动端300ms延迟

由来:300毫秒延迟解决的是双击缩放。双击缩放,手指在屏幕快速点击两次。safari浏览器就会将网页缩放值原始比例。由于用户可以双击缩放或者是滚动的操作,

当用户点击屏幕一次之后,浏览器并不会判断用户确实要打开至这个链接,还是想要进行双击操作 因此,safair浏览器就会等待300ms,用来判断用户是否在次点击了屏幕

解决方案

  1. 禁用缩放,设置meta标签 user-scalable=no
  2. fastclick.js

原理:FastClick的实现原理是在检查到touchend事件的时候,会通过dom自定义事件立即发出click事件,并把浏览器在300ms之后真正的click事件阻止掉。fastclick.js还可以解决穿透问题

  • fastclick可以解决在手机上点击事件的300ms延迟
  • zepto的touch模块,tap事件也是为了解决在click的延迟问题

触摸事件的响应顺序

  • ontouchstart
  • ontouchmove
  • ontouchend
  • onclick

# 3 如何解决移动端 Retina 屏 1px 像素问题

  • 伪元素 + transform scaleY(.5)
  • border-image
  • background-image
  • box-shadow

一般来说,在PC端浏览器中,设备像素比(dpr)等于1,1个css像素就代表1个物理像素;但是在retina屏幕中,dpr普遍是2或3,1个css像素不再等于1个物理像素,因此比实际设计稿看起来粗不少

  1. 伪元素+scale
<style>
    .box{
        width: 100%;
        height: 1px;
        margin: 20px 0;
        position: relative;
    }
    .box::after{
        content: '';
        position: absolute;
        bottom: 0;
        width: 100%;
        height: 1px;
        transform: scaleY(0.5);
        transform-origin: 0 0; 
        background: red;
    }
</style>

<div class="box"></div>
@程序员poetry: 代码已经复制到剪贴板
  1. border-image
div{
    border-width: 1px 0px;
    border-image: url(border.png) 2 0 stretch;
}
@程序员poetry: 代码已经复制到剪贴板

# 4 如何解决移动端击穿(穿透)问题

在移动端开发的时候,我们有时候会遇到这样一个bug:点击关闭遮罩层的时候,遮罩层下面的带有点击的元素也会被触发,给人一种击穿了页面的感觉,这是为什么呢?

  • 点击“打开弹框”按钮,显示遮罩层
  • 点击“关闭弹框”按钮,遮罩层消失,底下的连接被触发
var show = document.getElementById('show') // 打开按钮
var mask = document.getElementById('mask') // 遮罩层
var btn = document.getElementById('btn')   // 关闭按钮

show.onclick = function () {
    mask.style.display = 'block'
}

btn.addEventListener('touchstart', function () {
    mask.style.display = 'none'
}, false)
@程序员poetry: 代码已经复制到剪贴板
  • 这样问题的形成原因是什么呢?
  • 我们先来看一段代码:(以下代码需在移动端上运行)
<div id="btn">我是一个按钮</div>
var btn = document.getElementById('btn')
btn.addEventListener('touchstart', function () {
    console.log('start')    
}, false)

btn.addEventListener('touchmove', function () {
    console.log('move')
}, false)

btn.addEventListener('touchend', function () {
    console.log('touchend')
}, false)

btn.addEventListener('click', function () {
    console.log('click')
}, false)
@程序员poetry: 代码已经复制到剪贴板

以上代码会出现2种运行情况

start ===> move ===> end
start ===> end ===> click
@程序员poetry: 代码已经复制到剪贴板

看到这里相信大家都明白了,由于「关闭弹框」按钮绑定的事件是touch,a标签是click事件,在touch事件触发后,我们弹出框的遮罩层就消失了,这时候的click事件就被a标签给捕获到了,形成了击穿的效果

方法一、阻止默认事件

btn.addEventListener('touchend', function (e) {
    mask.style.display = 'none'
    e.preventDefault()
}, false)
@程序员poetry: 代码已经复制到剪贴板

在执行 touchstart 和 touchend 事件时,隐藏执行完隐藏命令后,立即阻止后续事件(推荐在touchend时,阻止后续的默认事件)

方法二、统一使用click事件

btn.addEventListener('click', function () {
    mask.style.display = 'none'
}, false)
@程序员poetry: 代码已经复制到剪贴板

这个方法简单,就是交互的效率没有click事件高,另外,用户在touch的时候,有可能微微滑动了一下,就会无法触发点击事件。影响用户体验。

方法三、延迟执行

btn.addEventListener('touchend', function () {
    setTimeout(function 
@程序员poetry: 代码已经复制到剪贴板
阅读全文
Last Updated: 2/12/2025, 2:40:37 PM