这次终于搞清楚移动端开发了(三)

3,618 阅读7分钟

我们知道,当把我们开发的pc端页面放在移动端展示,会出现布局错误。所以我们要做移动端适配,来让页面更合适。

接着第一期的文章

接着第二期的文章

接下来我们就来学习一下viewport标签并且学习如何做适配。

viewport

meta-viewport 标签是苹果公司在 2007 年引进的,用于移动端布局视口的控制。

使用示例:

<meta name="viewport" content="width=device-width,initial-scale=1.0">

viewport 相关选项

  • width 布局视口的宽度
  • initial-scale 【系统】初始缩放比例
  • maximum-scale 允许【用户】缩放的最大比例
  • minimum-scale 允许【用户】缩放的最小比例
  • user-scalable 是否允许用户缩放
  • viewport-fit 设置为cover值可以解决刘海屏的留白问题

1. width

width值可以是设备宽度标识 device-width,也可以是具体值,但有些安卓手机是不支持具体值,IOS全系列都支持。

2. initial-scale

  • initial-scale 为页面初始化时的显示比例。
  • initial-scale = 屏幕宽度(设备独立像素) / 布局视口宽度。
  • 只写initial-scale = 1.0 也可以实现完美视口,但为了良好的兼容性,width=device-width, initial-scale=1.0一般一起写。

3. maximum-scale

  • 设置允许用户最大缩放比例,苹果浏览器 safari 不认识该属性
  • maximum-scale = 屏幕宽度(设备独立像素) / 视觉视口宽度值

4. minimum-scale

  • 设置允许用户最小缩放比例。
  • minimum-scale = 屏幕宽度(设备独立像素) / 视觉视口宽度值

5. user-scalable

是否允许用户通过手指缩放页面。苹果浏览器 safari 不认识该属性

6.viewport-fit

设置为 cover 可以解决『刘海屏』的留白问题

所以在我们做移动端开发的时候,做好这样写<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">

了解了上面介绍的只是,那么我们来看看日和做适配,来使每个手机合理的展示内容。

适配

为什么要做适配?

由于移动端设备的屏幕尺寸大小不一,会出现:同一个元素,在两个不同的手机上显示效果不一样(比例不同)。

要想让同一个元素在不同设备上,显示效果一样,就需要适配无论采用何种适配方式,中心原则永远是:等比

主流的适配方式有三种:

  • viewport 适配
  • rem 适配(主流方式,几乎完美适配)
  • vw适配

viewport 适配

  • 方法:拿到设计稿之后,设置布局视口宽度为设计稿宽度,然后直接按照设计稿给宽高进行布局即可。

  • 优点:不用复杂的计算,直接使用图稿上标注的px值

  • 缺点:

    • 不能使用完整的meta标签,会导致在某些安卓手机上有兼容性问题。
    • 不希望适配的东西,例如边框,也强制参与了适配
    • 图片会失真

rem适配

首先来介绍一下em,rem

em 和 rem 都是 css 中的长度单位。而且两个都是相对长度单位,不过两个有点区别

  • em 相对的是父级元素的字体大小
  • rem 相对的是根元素的字体大小

rem适配的原理:编写样式时统一使用rem为单位,在不同设备上动态调整根字体大小。

方案一

淘宝、百度的移动端页面用的此方案

  • 设置完美视口
  • 通过js设置根字体大小 =  ( 当前设备横向独立像素值 * 100) / 设计稿宽度,设计稿对应的机型根字体设置为100px了。
  • 编写样式时,直接以rem为单位,值为:设计值 / 100 
  • 增加 JS 代码进行实时适配
     function adapter() {
        //获取布局视口宽度,因为开启了理想视口,布局视口=设备横向独立像素值 
        const dpWidth = document.documentElement.clientWidth        
        //计算根字体大小                                                      
        const rootFonstSize = (dpWidth * 100)/375                      
        //设置根字体大小                                                      
        document.documentElement.style.fontSize = rootFonstSize + 'px'
     }
     adapter() 
     // 注意:onresize事件检测的是布局视口的变化。
     window.onresize = adapter

优势:编写样式时直接挪动小数点即可。

方案二

搜狐、唯品会的移动端页面用的此方案

  • 设置完美视口
  • 通过js设置根字体大小 = 当前设备横向独立像素值 / 10 
  • 编写样式时,直接以rem为单位,值为:设计值 / (设计稿宽度 / 10)
  • 增加 JS 代码进行实时适配
     function adapter() {
        //获取布局视口宽度,因为开启了理想视口,布局视口=设备横向独立像素值 
        const dpWidth = document.documentElement.clientWidth        
        //计算根字体大小                                                      
        const rootFonstSize = dpWidth / 10                     
        //设置根字体大小                                                      
        document.documentElement.style.fontSize = rootFonstSize + 'px'
     }
     adapter() 
     // 注意:onresize事件检测的是布局视口的变化。
     window.onresize = adapter

通过rem适配时,动态设置完根字体大小后,只需要进行设计稿的px和rem的转换即可,不需要考虑其他的。

vw适配(百分比)

京东的移动端页面是通过vw和rem结合的方案 vw和vh是两个相对单位

  • 1vw = 等于布局视口宽度的1%
  • 1vh = 等于布局视口高度的1%

这里不需要通过js代码,直接通过元素的像素和设计稿的布局视口比值,然后vw作为单位即可完成适配。只需要考虑设计稿即可。

下面来通过less来完成在iphone6的设计稿下的345 * 150px的元素渲染适配。

@basic:375/100vw;

*{
	margin: 0;
	padding: 0;
}
#demo{
	width: 345/@basic;
	height: 150/@basic;
	background-color: skyblue;
	margin: 0 auto;
	margin-top: 15/@basic;
	border: 1px solid black;
}

1.gif

下面来介绍一下京东的适配方案。

image.png

1.gif

不过vw和vh有一定的兼容性问题:详见:这里

物理像素边框

高清屏幕下 1px 对应更多的物理像素,所以 1 像素边框看起来比较粗,使用媒查询来设置不同dpr下的边框:

@media screen and (-webkit-min-device-pixel-ratio:2){
    #demo{
        border: 0.5px solid black;
    }
}

移动端事件

事件类型

移动端事件列表

  • touchstart 元素上触摸开始时触发
  • touchmove 元素上触摸移动时触发
  • touchend 手指从元素上离开时触发
  • touchcancel 触摸被打断时触发

这几个事件最早出现于IOS safari中,为了向开发人员转达一些特殊的信息。

应用场景

  • touchstart 事件可用于元素触摸的交互,比如页面跳转,标签页切换
  • touchmove 事件可用于页面的滑动特效,网页游戏,画板
  • touchend 事件主要跟 touchmove 事件结合使用
  • touchcancel 使用率不高

注意:

  • touchmove 事件触发后,即使手指离开了元素,touchmove 事件也会持续触发
  • 触发 touchmove 与 touchend 事件,一定要先触发 touchstart
  • 事件的作用在于实现移动端的界面交互

点击穿透

touch 事件结束后会默认触发元素的 click 事件,如没有设置完美视口,则事件触发的时间间隔为 300ms 左右,如设置完美视口则时间间隔为 30ms 左右(备注:具体的时间也看设备的特性)。

如果 touch 事件隐藏了元素,则 click 动作将作用到新的元素上,触发新元素的 click 事件或页面跳转,此现象称为点击穿透

解决方法一

阻止默认行为

//阻止默认行为
node.addEventListener('touchstart', function(e){
  e.preventDefault(); 
})

解决方法二

使背后元素不具备click特性,用touchXxxx代替click

banner_img.addEventListener('touchstart',()=>{
    location.href = 'http://www.baidu.com'
})

解决方案三

让背后的元素暂时失去click事件,300毫秒左右再复原。因为touch系列事件和click事件极限间隔大约300ms。

#anode{
  pointer-events: none;
}
btn.addEventListener('touchstart',(event)=>{
    shade.style.display = 'none'
    setTimeout(()=>{
        anode.style.pointerEvents = 'auto'
    },300)
})

解决方案四

让隐藏的元素延迟300毫秒左右再隐藏

btn.addEventListener('touchstart',(event)=>{
    setTimeout(()=>{
      shade.style.display =  'none'
    },300)
})