移动端响应式开发应该要注意的的都在这里了

1,664 阅读27分钟

响应式介绍

随着移动互联网的兴起,不同设备的分辨率相差较大,如果在不同的设置上显示同一个页面,则用户体验差。响应式网页设计是一种方法,使得一个网站能够兼容多个终端,而不用为每个终端制作特定的版本。它使得一个网站可以在任何类型的屏幕上,都可以被轻松地浏览和使用。采用响应式设计,在不同设备中,网站会重新排列,展现出不同的设计风格,以完美的适配任何尺寸的屏幕。

设计原则

关于响应式设计,有渐进增加和优雅降级两个设计原则

  渐进增强(progressive enhancement),是指基本需求得到满足、实现,再根据不同浏览器及不同分辨率设备的特点,利用高级浏览器下的新特性提供更好的体验。比如,圆角、阴影、动画等

  优雅降级(graceful degradation)则正好相反,现有功能已经开发完备,但需要向下兼容版本和不支持该功能的浏览器。虽然兼容性方案的体验不如常规方案,但保证了功能可用性

  移动优先的响应式布局采用的是渐进增强原则,制作响应式网站时,先搞定手机版,然后再去为更大设备去设计和开发更复杂的功能。特征是使用min-width匹配页面宽度。从上到下书写样式时,首先考虑的是移动设备的使用场景,默认查询的是最窄的情况,再依次考虑设备屏幕逐渐变宽的情况

  由简入繁易,由繁入简难。如果是桌面优先,布局端是桌面端代码,只有在media中,才是手机端代码,加载了多余的桌面端代码。如果是图片文件,则下载的无用资源更多

  无论从界面设计还是代码执行效率的角度而言,移动优先都有明显优势

响应式布局如何实现

实现响应式布局的三个要素:

  1. viewport视口设置
  2. @media媒体查询
  3. 不要写死的尺寸
    • 多用百分比宽度来确定布局尺寸
    • 多用rem、em来确定布局尺寸
    • 多用vh、vw来确定布局尺寸

看到以上三条规则,蒙蔽状态吧,下面咱们就一一的对每条来进行详细的讲解。

在去了解移动响应式之前,先看几个响应式的网站,对它有个感觉吧!

星巴克官网 INSIDE Bootstrap

看完以上三个网站实例,回想一下我们之前做的网站可是固定写死的页面!!!!下面咱们就进入正题

viewport视口设置

一、viewport的概念
	通俗的讲,移动设备上的viewport就是设备的屏幕上能用来显示我们的网页的那一块区域,在具体一点,就是浏览器上(也可能是一个app中的webview)用来显示网页的那部分区域,但viewport又不局限于浏览器可视区域的大小,它可能比浏览器的可视区域要大,也可能比浏览器的可视区域要小。
	在默认情况下,一般来讲,移动设备上的viewport都是要大于浏览器可视区域的,这是因为考虑到移动设备的分辨率相对于桌面电脑来说都比较小,所以为了能在移动设备上正常显示那些传统的为桌面浏览器设计的网站,移动设备上的浏览器都会把自己默认的viewport设为980px或1024px(也可能是其它值,这个是由设备自己决定的),但带来的后果就是浏览器会出现横向滚动条,因为浏览器可视区域的宽度是比这个默认的viewport的宽度要小的。

下图列出了一些设备上浏览器的默认viewport的宽度。

1

二、css中的1px并不等于设备的1px
	在css中我们一般使用px作为单位,在pc浏览器中css的1个像素往往都是对应着电脑屏幕的1个物理像素,这可能会造成我们的一个错觉,那就是css中的像素就是设备的物理像素。
    
    但实际情况却并非如此,css中的像素只是一个抽象的单位,在不同的设备或不同的环境中,css中的1px所代表的设备物理像素是不同的。在为桌面浏览器设计的网页中,我们无需对这个津津计较,但在移动设备上,必须弄明白这点。
    在早先的移动设备中,屏幕像素密度都比较低,如iphone3,它的分辨率为320x480,在iphone3上,一个css像素确实是等于一个屏幕物理像素的。后来随着技术的发展,移动设备的屏幕像素密度越来越高,从iphone4开始,苹果公司便推出了所谓的Retina屏,分辨率提高了一倍,变成640x960,但屏幕尺寸却没变化,这就意味着同样大小的屏幕上,像素却多了一倍,这时,一个css像素是等于两个物理像素的。其他品牌的移动设备也是这个道理。例如安卓设备根据屏幕像素密度可分为ldpi、mdpi、hdpi、xhdpi等不同的等级,分辨率也是五花八门,安卓设备上的一个css像素相当于多少个屏幕物理像素,也因设备的不同而不同,没有一个定论。

	还有一个因素也会引起css中px的变化,那就是用户缩放。例如,当用户把页面放大一倍,那么css中1px所代表的物理像素也会增加一倍;反之把页面缩小一倍,css中1px所代表的物理像素也会减少一倍。关于这点,在后面的部分还会讲到。

	在移动端浏览器中以及某些桌面浏览器中,window对象有一个devicePixelRatio属性,它的官方的定义为:设备物理像素和设备独立像素的比例,也就是 devicePixelRatio = 物理像素 / 独立像素。
	css中的px就可以看做是设备的独立像素,所以通过devicePixelRatio,我们可以知道该设备上一个css像素代表多少个物理像素。例如,在Retina屏的iphone上,devicePixelRatio的值为2,也就是说1个css像素相当于2个物理像素。但是要注意的是,devicePixelRatio在不同的浏览器中还存在些许的兼容性问题,所以我们现在还并不能完全信赖这个东西,具体的情况可以看下[这篇文章](http://www.quirksmode.org/blog/archives/2012/06/devicepixelrati.html)。

devicePixelRatio的测试结果:

14

三、PPK的关于三个viewport的理论

ppk大神对于移动设备上的viewport有着非常多的研究(第一篇第二篇第三篇),有兴趣的同学可以去看一下, 这里有很多数据和观点也是出自那里。ppk认为,移动设备上有三个viewport。

​ 首先,移动设备上的浏览器认为自己必须能让所有的网站都正常显示,即使是那些不是为移动设备设计的网站。但如果以浏览器的可视区域作为viewport的话,因为移动设备的屏幕都不是很宽,所以那些为桌面浏览器设计的网站放到移动设备上显示时,必然会因为移动设备的viewport太窄,而挤作一团,甚至布局什么的都会乱掉。

​ 也许有人会问,现在不是有很多手机分辨率都非常大吗,比如768x1024,或者1080x1920这样,那这样的手机用来显示为桌面浏览器设计的网站是没问题的吧?

​ 前面我们已经说了,css中的1px并不是代表屏幕上的1px,你分辨率越大,css中1px代表的物理像素就会越多,devicePixelRatio的值也越大,这很好理解,因为你分辨率增大了,但屏幕尺寸并没有变大多少,必须让css中的1px代表更多的物理像素,才能让1px的东西在屏幕上的大小与那些低分辨率的设备差不多,不然就会因为太小而看不清。所以在1080x1920这样的设备上,在默认情况下,也许你只要把一个div的宽度设为300多px(视devicePixelRatio的值而定),就是满屏的宽度了。

​ 回到正题上来,如果把移动设备上浏览器的可视区域设为viewport的话,某些网站就会因为viewport太窄而显示错乱,所以这些浏览器就决定默认情况下把viewport设为一个较宽的值,比如980px,这样的话即使是那些为桌面设计的网站也能在移动浏览器上正常显示了。ppk把这个浏览器默认的viewport叫做 layout viewport。*这个layout viewport的宽度可以通过 document.documentElement.clientWidth 来获取。

​ 然而,layout viewport 的宽度是大于浏览器可视区域的宽度的,所以我们还需要一个viewport来代表 浏览器可视区域的大小,ppk把这个viewport叫做 visual viewport。visual viewport的宽度可以通过window.innerWidth 来获取,但在Android 2, Oprea mini 和 UC 8中无法正确获取。

2 3

​ 现在我们已经有两个viewport了:layout viewportvisual viewport

​ 但浏览器觉得还不够,因为现在越来越多的网站都会为移动设备进行单独的设计,所以必须还要有一个能完美适配移动设备的viewport。

​ 所谓的完美适配指的是,首先不需要用户缩放和横向滚动条就能正常的查看网站的所有内容;第二,显示的文字的大小是合适,比如一段14px大小的文字,不会因为在一个高密度像素的屏幕里显示得太小而无法看清,理想的情况是这段14px的文字无论是在何种密度屏幕,何种分辨率下,显示出来的大小都是差不多的。当然,不只是文字,其他元素像图片什么的也是这个道理。ppk把这个viewport叫做 ideal viewport,也就是第三个viewport——移动设备的理想viewport。

​ ideal viewport并没有一个固定的尺寸,不同的设备拥有着不同的ideal viewport。所有的iphone的ideal viewport宽度都是320px,无论它的屏幕宽度是320还是640,也就是说,在iphone中,css中的320px就代表iphone屏幕的宽度。

4 5

但是安卓设备就比较复杂了,有320px的,有360px的,有384px的等等,关于不同的设备ideal viewport的宽度都为多少,可以到viewportsizes.com去查看一下,里面收集了众多设备的理想宽度。

总结

ppk把移动设备上的viewport分为layout viewportvisual viewportideal viewport 三类。

​ 其中的ideal viewport是最适合移动设备的viewport,ideal viewport的宽度等于移动设备的屏幕宽度,只要在css中把某一元素的宽度设为ideal viewport的宽度(单位用px),那么这个元素的宽度就是设备屏幕的宽度了,也就是宽度为100%的效果。ideal viewport 的意义在于,无论在何种分辨率的屏幕下,那些针对ideal viewport 而设计的网站,不需要用户手动缩放,也不需要出现横向滚动条,都可以完美的呈现给用户。

四、利用meta标签对viewport进行控制
移动设备默认的viewport是layout viewport,也就是那个比屏幕要宽的viewport,但在进行移动设备网站的开发时,我们需要的是ideal viewport。那么怎么才能得到ideal viewport呢?这就该轮到meta标签出场了。

我们在开发移动设备的网站时,最常见的的一个动作就是把下面这个东西复制到我们的head标签中:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">

该meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。也许允不允许用户缩放不同的网站有不同的要求,但让viewport的宽度等于设备的宽度,这个应该是大家都想要的效果,如果你不这样的设定的话,那就会使用那个比屏幕宽的默认viewport,也就是说会出现横向滚动条。

这个name为viewport的meta标签到底有哪些东西呢,又都有什么作用呢?

meta viewport 标签首先是由苹果公司在其safari浏览器中引入的,目的就是解决移动设备的viewport问题。后来安卓以及各大浏览器厂商也都纷纷效仿,引入对meta viewport的支持,事实也证明这个东西还是非常有用的。

在苹果的规范中,meta viewport 有6个属性(暂且把content中的那些东西称为一个个属性和值),如下:

width设置layout viewport 的宽度,为一个正整数,或字符串"width-device"
initial-scale设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale允许用户的最大缩放值,为一个数字,可以带小数
height设置layout viewport 的高度,这个属性对我们并不重要,很少使用
user-scalable是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许

这些属性可以同时使用,也可以单独使用或混合使用,多个属性同时使用时用逗号隔开就行了。

五、把当前的viewport宽度设置为 ideal viewport 的宽度

要得到ideal viewport就必须把默认的layout viewport的宽度设为移动设备的屏幕宽度。因为meta viewport中的width能控制layout viewport的宽度,所以我们只需要把width设为width-device这个特殊的值就行了。

<meta name="viewport" content="width=device-width">

下图是这句代码在各大移动端浏览器上的测试结果:

6

可以看到通过width=device-width,所有浏览器都能把当前的viewport宽度变成ideal viewport的宽度,但要注意的是,在iphone和ipad上,无论是竖屏还是横屏,宽度都是竖屏时ideal viewport的宽度。

这样的写法看起来谁都会做,没吃过猪肉,谁还没见过猪跑啊~,确实,我们在开发移动设备上的网页时,不管你明不明白什么是viewport,可能你只需要这么一句代码就够了。

可是你肯定不知道

<meta name="viewport" content="initial-scale=1">

这句代码也能达到和前一句代码一样的效果,也可以把当前的的viewport变为 ideal viewport。

呵呵,傻眼了吧,因为从理论上来讲,这句代码的作用只是不对当前的页面进行缩放,也就是页面本该是多大就是多大。那为什么会有 width=device-width 的效果呢?

要想清楚这件事情,首先你得弄明白这个缩放是相对于什么来缩放的,因为这里的缩放值是1,也就是没缩放,但却达到了 ideal viewport 的效果,所以,那答案就只有一个了,缩放是相对于 ideal viewport来进行缩放的,当对ideal viewport进行100%的缩放,也就是缩放值为1的时候,不就得到了 ideal viewport吗?事实证明,的确是这样的。下图是各大移动端的浏览器当设置了<meta name="viewport" content="initial-scale=1"> 后是否能把当前的viewport宽度变成 ideal viewport 的宽度的测试结果。

7

测试结果表明 initial-scale=1 也能把当前的viewport宽度变成 ideal viewport 的宽度,但这次轮到了windows phone 上的IE 无论是竖屏还是横屏都把宽度设为竖屏时ideal viewport的宽度。但这点小瑕疵已经无关紧要了。

但如果width 和 initial-scale=1同时出现,并且还出现了冲突呢?比如:

<meta name="viewport" content="width=400, initial-scale=1">

width=400表示把当前viewport的宽度设为400px,initial-scale=1则表示把当前viewport的宽度设为ideal viewport的宽度,那么浏览器到底该服从哪个命令呢?是书写顺序在后面的那个吗?不是。当遇到这种情况时,浏览器会取它们两个中较大的那个值。例如,当width=400,ideal viewport的宽度为320时,取的是400;当width=400, ideal viewport的宽度为480时,取的是ideal viewport的宽度。(ps:在uc9浏览器中,当initial-scale=1时,无论width属性的值为多少,此时viewport的宽度永远都是ideal viewport的宽度)

最后,总结一下,要把当前的viewport宽度设为ideal viewport的宽度,既可以设置 width=device-width,也可以设置 initial-scale=1,但这两者各有一个小缺陷,就是iphone、ipad以及IE 会横竖屏不分,通通以竖屏的ideal viewport宽度为准。所以,最完美的写法应该是,两者都写上去,这样就 initial-scale=1 解决了 iphone、ipad的毛病,width=device-width则解决了IE的毛病:

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

@media 的使用方式

【1】第一种 直接嵌入

@media (max-with: 480px){
    body{
        background-color: red;
    }
}

【2】第二种 外接方式

<link rel="stylesheet" media="max-width: 480px" href="css/viewport.css">

媒体类型

media属性用于为不同的媒介类型规定不同的样式

screen         计算机屏幕(默认值)    
tty            电传打字机以及使用等宽字符网格的类似媒介
tv             电视类型设备(低分辨率、有限的屏幕翻滚能力)
projection     放映机
handheld       手持设备(小屏幕、有限的带宽)
print          打印预览模式 / 打印页
braille        盲人用点字法反馈设备
aural          语音合成器
all            适合所有设备

真正广发使用且所有浏览器都兼容的媒介类型是screenall

**演示默认的screenprint**

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <title>媒体类型 - Media Type</title>
		<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <link rel="stylesheet" href="css/style.css">
    <link rel="stylesheet" href="css/print.css" media="print">
  </head>
  <body>
    <h1>Meida Type</h1>
  </body>
</html>

style.css

/*默认的设备下 背景色是黑色,字体颜色*/
body{
	background-color: #000000;
	color: #fff;
}

print.css

/*打印机设备下*/
body{
	background-color: #fff;
	color: #000;
}

效果显示

媒体特性

在设置媒体查询的时候,我们通过一些表达式来判断媒体特性,比如说分辨率 、高度、宽度、宽高的比例、设备宽高比、设备宽度和高度、屏幕显示的方向(横屏还是竖屏)。

现在就以横竖屏为例来演示个效果

/*横屏*/
@media (orientation: landscape) {
    body{
        background-color: #008B8B;
    }
}
/*竖屏*/
@media (orientation: portrait) {
    body{
        background-color: #4169E1;
}

效果:

width和height

表示屏幕的宽度和高度。同样前面也可以加max和min前缀。

/* 小于640px的样式 */
body {
    background-color: #000;
    color: #fff;
}
/* 在640到991之间的样式 中间既可以使用and和也可以使用or连接符 */
@media all and (min-width: 640px) and (max-width: 991px) {
    body{
        background-color: blueviolet;
    }
}
/* 在992px到1189px之间的样式 并且再横屏的时候 显示绿色 */
@media (max-width:1189px) and (min-width: 992px) , screen and (orientation: portrait){
    body{
        background-color: green;
    }
}
/* 大于1190px的样式 */
@media (min-width:1190px) {
    body{
        background-color: red;
    }
}

响应式图片

【1】使用img标签来设置图片的显示

/* 简单的解决方法可以使用百分比,但这样不友好,会放大或者缩小图片,那么可以尝试给图片指定的最大宽度为百分比。假如图片超过了,就缩小。假如图片小了,就原尺寸输出 */
img{
	width: 100%;
	max-width: 100%;
} 

【2】使用background属性设置背景图

将background-size属性设置为cover,则会把背景图像扩展至足够大,使背景图像完全覆盖背景区域。

.bg_img{
	background: url(../images/01.jpg) no-repeat center center;
	background-size: cover;
	height: 500px;
}

rem自适应布局

在页面整体布局中,页面元素的尺寸大小(长度、宽度、内外边距等)和页面字体的大小也是重要的工作之一。一个合理设置,则会让页面看起来层次分明,重点鲜明,赏心悦目。反之,一个不友好的页面尺寸和字体大小设置,则会增加页面的复杂性,增加用户对页面理解的复杂性;甚至在当下访问终端(iPhone、iPad、PC、Android…)层出不穷的今天,适应各式各样的访问终端,将成为手中的一块“烫手的山芋”。所以在近几年,“九宫格”式的“流式布局”再度回归。为了提供页面布局,及其它的可维护性、可扩展性,我们尝试将页面元素的大小,以及字体大小都设置为相对值,不再是孤立的固定像素点。使其能在父元素的尺寸变化的同时,子元素也能随之适应变化。以及结合少量最新CSS3的@media查询,来实现“响应式布局”,bootstrap这类CSS框架大势兴起。

然而在CSS中,W3C文档把尺寸单位划为为两类:相对长度单位绝对长度单位

绝对单位

px:像素单位是绝对单位,你设置了多少,不管什么屏幕下,都会保持不变。
缺点:任何情况下都是固定值

相对单位

%:相对单位,它是相对于父级(自身)大小进行定位
缺点:能确定范围的还是比较好计算,对于不太好确定值的地方不好使用百分比,并且会导致变形,高度一般不好控制

em:相对于当前容器的字体大小进行变化
比如当前容器内字体大小为20px,那么1em = 20px
缺点:会根据当前容器字体大小发生变化,假设每个容器字体大小不一致,那么计算会非常繁琐

rem:font size of the root element。相对于当前根(html)元素的字体大小进行变化
比如当前设置html的字体大小为20px,那么1rem = 20px; 浏览器默认的1rem = 16px
如何才能使用rem进行适配呢?
  • 字体大小
    • 值根据html根元素大小而定,同样可以作为宽度,高度等单位
  • 适配原理
    • 将px替换成rem,动态修改html的font-size适配
  • 兼容性
    • Ios6以上和android2.1以上,基本覆盖所有流行的手机系统

看个例子演示一下:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
		<style type="text/css">
			* {
				padding: 0;
				margin: 0;
			}
			ul{
				list-style: none;
			}
			html{
				width: 100%;
				height: 100%;
			}
			body{
				width: 100%;
				height: 100%;
			}
			.head-box{
				width: 100%;
				height: 70px;
				background-color: #000000;
				
			}
			.list .item{
				float: left;
				width: 100px;
				height: 100px;
				margin: 20px;
				border: 1px solid red;
				font-size: 14px;
			}
		
			
		
		</style>
		<script src="js/resize.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<header class="head-box">
			<div class="head-top"></div>
			<div class="head-bottom"></div>
		</header>

		<ul class="list clearfix">
			<li class="item">1111111</li>
			<li class="item"></li>
			<li class="item"></li>
			<li class="item"></li>
			<li class="item"></li>
			<li class="item"></li>
			<li class="item"></li>
			<li class="item"></li>
		</ul>
	</body>
</html>

展示效果:

设置html的font-size为50px,将盒子的宽高和外边距修改成rem。

html{
    width: 100%;
    height: 100%;
    font-size: 50px;
}
.list .item{
    float: left;
    width: 2.0rem;
    height: 2.0rem;
    margin: 0.2rem;
    border: 1px solid red;
    font-size: 14px;
}

但是如果现在是以ipad显示,我们正常希望是看到的一行上显示三个盒子。如果把font-size的大小手动改大,就能达到想要的效果。

动态修改font-size
window.addEventListener('resize',function(){
    var htmlWidth = document.documentElement.clientWidth || document.body.clientWidth;
    document.getElementsByTagName('html')[0].style.fontSize =(htmlWidth / 750) * 100 + 'px';
});

依iphone678为例,则html的font-size为50px,则1rem = 50px; 1px = 0.02rem

完整的动态修改html的font-size的js文件

!(function(doc, win) {
	var docEl = doc.documentElement,
		resizeEvt = 'onorientationchange' in window ? 'onorientationchange' : 'resize',
		recalc = function() {
			var clientWidth = docEl.clientWidth;
			if (!clientWidth) return;
			if (clientWidth >= 750) {
				docEl.style.fontSize = '100px';
			} else {
				docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
			}
			docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
		};

	if (!doc.addEventListener) return;
	win.addEventListener(resizeEvt, recalc, false);
	doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
flexiable.js

flexiable.js是由淘宝官方提出来的最佳的h5移动适配解决方案。官网

有个Hbuilderx的px转rem工具当然方便,如果没有这种工具呢?这个时候我们要学一下less和sass语言了。它们是一种预处理的css语言。下一章介绍。

less

定义变量

在less文件中,我们可以声明变量

@color: #f00;
h2{
    color: @color;
}
Minxins混入

Minxins是一种将一组属性从一个规则集合混入到另一个规则集合的方法。

@color: #f00;
@width: 100px;
@height: @width;
.active{
	border: 1px solid @color;
	width: @width;
	height: @height;
}
#box2{
    /*混入了.active的规则*/
	.active;
}
嵌套规则

less能够可以使用嵌套规则来编写我们的代码

有如下结构

<div id="box">
    <h2>
        <span>Mjj</span>
    </h2>
</div>

以前的css写法:

#box {
  width: 100px;
  height: 100px;
  border: 1px solid #000;
}
#box h2 {
  background-color: green;
  padding: 20px;
}
#box h2 span {
  color: #f00;
}

使用less嵌套规则编写

@color: #f00;
@width: 100px;
@height: @width;
#box{
	width: @width;
	height: @height;
	border: 1px solid #000;
	h2{
		background-color: green;
		padding: 20px;
		span{
			color: @color;
		}
	}
}

生成的代码更简洁,并模仿HTML的结构

运算

算数运算符+,-,*,/可以在less中使用

@a: 20px;
@b: 30px;
@sum = @a + @b;

.active{
    width: @sum;
}
逃离

它允许你使用任意字符串作为属性或变量值。内部的任何内容~"anything"按原样使用。除了插值外没有任何变化。

// 插入变量 使用@min768,相当于把(min-width:768px)完整的插入进去
@min768:~"(min-width:768px)";
body{
	@media @min768 {
		background-color: red;
	}
}
//编译之后
@media (min-width:768px) {
  body {
    background-color: red;
  }
}
可变插值

选择器名称可以作为可变的值

//定义自己的选择器
@my-selector: banner;
//使用自己的选择器
.@{my-selector}{
	font-size: 30px;
}

//编译为
.banner{
    font-size: 30px;
}

网址插入

// Variables
@images: "../img";

// Usage
body {
  color: #444;
  background: url("@{images}/white-sand.png");
}

导入语句

@import './hello.less'
父选择器

&引用父选择器

a {
  color: blue;
  &:hover {
    color: green;
  }
}
//编译
a {
  color: blue;
}

a:hover {
  color: green;
}

看看这个例子,或许你能晓得&符号的强大之处

.button {
  &-ok {
    background-image: url("ok.png");
  }
  &-cancel {
    background-image: url("cancel.png");
  }

  &-custom {
    background-image: url("custom.png");
  }
}
//编译
.button-ok {
  background-image: url("ok.png");
}
.button-cancel {
  background-image: url("cancel.png");
}
.button-custom {
  background-image: url("custom.png");
}
自定义函数

@base:375 / 375 * 0.01;
//自定义函数
.px2rem(@name, @px) {
     @{name}: @px * @base * 1rem;
}
//使用自定义函数
.footer{
	font-size: 20px;
	.px2rem(padding,30);
}

移动端事件

touch事件

当我们手指放在屏幕上面时、在屏幕上滑动时或从屏幕上移开时触发。具体有以下几个触摸事件

1. touchstart:当手指触摸屏幕时触发;即使已经有一个手指放在了屏幕上也会触发
2. touchmove:当手指在屏幕上滑动时连续地触发。在这个事件发生期间,调用preventDefault()可以阻止滚动
3. touchend:当手指从屏幕上移开时触发
4. touchcancel:当系统停止跟踪触摸时触发(不常用)。关于此事件的确切触发时间,文档中没有明确说明
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <style type="text/css">
    .box {
        width: 200px;
        height: 200px;
        background-color: red;
    }
    </style>
</head>

<body>
    <!-- 1.移动端常用事件 -->
    <!-- 
		touchstart 在屏幕上按下手指时触发
		touchmove 在屏幕上连续的滑动手指时触发
		touchend 当手指在屏幕上抬起,或者划出屏幕时触发
	 -->
    <div class="box">
    </div>
    <script type="text/javascript">
    window.onload = function() {
        var oDiv = document.querySelector('.box');
        oDiv.addEventListener('touchstart', function(event) {
            console.log('按下手指了');
        }, false);
        oDiv.addEventListener('touchmove', function(event) {
            console.log('touchmove');
        }, false);
        oDiv.addEventListener('touchend', function(event) {
            console.log('touchend');
        }, false);
    }
    </script>
</body>

</html>

touch 事件对象

事件对象的属性都存储在touches数组中

clientX:触摸目标在视口中的x坐标。
clientY:触摸目标在视口中的y坐标。
identifier:标识触摸的唯一ID。
pageX:触摸目标在页面中的x坐标。
pageY:触摸目标在页面中的y坐标。
screenX:触摸目标在屏幕中的x坐标。
screenY:触摸目标在屏幕中的y坐标。
target:触目的DOM节点目标。

滑动实现

  1. 让触摸的元素随着手指的滑动做位置的改变
  2. 位置的改变,需要当前手指的坐标
  3. 在每一个触摸点中会记录当前触摸点的坐标。e.touches[0] 第一个触摸点
clientX clientY 基于浏览器窗口(视口)
pageX pageY 基于页面(视口)
screenX screenY 基于屏幕

封装:swiper手势事件

包括:swipeLeft,swipeRight,swipeUp,swipeDown

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

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title></title>
    </head>

    <body>
        <div class="box" style="width: 200px;height: 200px; background-color: pink;"></div>
        <script>
            // 封装向左滑动,向右互动

            function swiper(ele, options) {
                var startX;
                var startY;
                var disX;
                var disY;
                var moveX;
                var moveY;
                //默认没有触摸触摸移动事件
                var isMove = false;
                // 1.将元素绑定触摸开始事件,将事件对象传入
                ele.addEventListener('touchstart', function (e) {
                    // 2.单击时,记录开始滑动的坐标
                    startX = e.touches[0].clientX;
                    startY = e.touches[0].clientY;
                    disX = startX - ele.offsetLeft;
                    disY = startY - ele.offsetTop;

                })
                ele.addEventListener('touchmove', function (e) {
                    // 3.开始的坐标在上面已记录,移动的鼠标X坐标
                    moveX = e.touches[0].clientX;
                    moveY = e.touches[0].clientY;

                    // 修改当前状态,表示用户手指触摸移动了
                    isMove = true;
                    // console.log(disX);
                    e.info = {
                        startX: startX,
                        startY: startY,
                        disX: disX,
                        disY: disY
                    }
                })
                ele.addEventListener('touchend', function (e) {
                    // 4.判断用户是否开启了触摸移动事件?
                    if (isMove) {

                        // 5.判断用户是水平方向划,还是垂直方向划
                        var absX = Math.abs(moveX - startX);
                        var absY = Math.abs(moveY - startY);
                        // 6.判断如果x轴大于y轴,将是水平方向划,否则反之
                        if (absX > absY) {
                            // console.log('水平方向划');
                            // 7.如果移动的距离大于开始的距离,向右划,否则向⬅️划
                            moveX > startX ? options.swipeRight() : options.swipeLeft();
                        } else {
                            // console.log('垂直方向划');
                            moveY > startY ? options.swipeDown() : options.swipeUp();
                        }
                        isMove = false;
                    }
                })
            }
            swiper(document.querySelector('.box'), {
                swipeLeft: function () {
                    console.log('向左滑动')
                },
                swipeRight: function () {
                    console.log("向右滑动");

                },
                swipeDown: function () {
                    console.log('向下滑动')
                },
                swipeUp: function () {
                    console.log("向上滑动");

                }
            })
        </script>
    </body>

</html>

移动端300ms延迟的解决方法

一般情况下,如果没有经过特殊处理,移动端浏览器在派发点击事件的时候,通常会出现300ms左右的延迟。也就是说,当我们点击页面的时候移动端浏览器并不是立即作出反应,而是会等上一小会儿才会出现点击的效果。在移动WEB兴起的初期,用户对300ms的延迟感觉不明显。但是,随着用户对交互体验的要求越来越高,现今,移动端300ms的点击延迟逐渐变得明显而无法忍受。那么,移动端300ms的点击延迟是怎么来的呢?

300ms延迟的由来

这要追溯至 2007 年初。苹果公司在发布首款 iPhone 前夕,遇到一个问题:当时的网站都是为大屏幕设备所设计的。于是苹果的工程师们做了一些约定,应对 iPhone 这种小屏幕浏览桌面端站点的问题。这当中最出名的,当属双击缩放(double tap to zoom),这也是会有上述 300 毫秒延迟的主要原因。

双击缩放,顾名思义,即用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。 那么这和 300 毫秒延迟有什么联系呢? 假定这么一个场景。用户在 iOS Safari 里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。 鉴于iPhone的成功,其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放,几乎现在所有的移动端浏览器都有这个功能。之前人们刚刚接触移动端的页面,在欣喜的时候往往不会care这个300ms的延时问题,可是如今touch端界面如雨后春笋,用户对体验的要求也更高,这300ms带来的卡顿慢慢变得让人难以接受。

解决

第一步,在head标签中引入

<script src="https://cdn.bootcss.com/fastclick/1.0.6/fastclick.js"></script>

第二步,加载完fastclick.js脚本后

document.addEventListener('DOMcontentLoaded',function() {
    FaskClick.attach(document.body);
}) 
document.querySelector('a').addEventListener('click',function() {
    //正常的click响应明显时间加快
    console.log('click');
})

移动端常用库zepto.js

pc端目前我们使用的库通常是jQuery,而在移动端库推出来一个zepto.js,那它比jQuery有什么优势呢?

1、大小方面 , 压缩后的 zepto.min.js 大小只有21K, 使用服务器端 gzip 压缩后大小只有5~10K, 可以说非常的小。

2、虽然体积小,但是功能很齐全, 多出来了一些触摸屏的事件 ,用它来开发iPhone和Android网页是首选, 但它不支持IE。

3、Zepto是jQuery的精简,针对移动端去除了大量jQuery的兼容代码。

4、因为精简,所以Zepto核心库的性能要比jQuery优秀。

zepto.js官网

注意:亲测,zepto.js的触摸事件不起作用,why?? 好吧,我果断放弃这个移动端库了,看来还是vue的天下,像Vux,iVew等移动端库,肯定比这个zepto.js要好。我的亲娘哎,你要折磨死俺

移动端常用库swiper

Swiper是纯JavaScript打造的滑动特效插件。

swiper是实现触屏焦点图、触屏Tab切换、触屏多图切换等常用效果,并且它开源、免费、稳定、使用简单、功能强大、是架构移动终端网站的重要选择

swiper4.x使用方法

swiper4.x使用方法

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

    <head>
        <meta charset="utf-8">
        <title>Swiper demo</title>
        <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">

        <!-- Link Swiper's CSS -->
        <link rel="stylesheet" href="css/swiper.min.css">

        <!-- Demo styles -->
        <style>
            html,
            body {
                position: relative;
                height: 100%;
            }

            body {
                background: #eee;
                font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
                font-size: 14px;
                color: #000;
                margin: 0;
                padding: 0;
            }

            .swiper-container {
                width: 100%;
            }

            .swiper-slide {
                text-align: center;
                font-size: 18px;
                background: #fff;

                /* Center slide text vertically */
                display: flex;
                justify-content: center;
                align-items: center;
            }

            img {
                width: 100%;
            }
            .my-bullet-active{
                background-color: yellow;
                opacity: 1;
            }
        </style>
    </head>

    <body>
        <!-- Swiper -->
        <div class="swiper-container">
            <div class="swiper-wrapper">
                <div class="swiper-slide">
                    <a href="javascript:void(0)"><img src="images/01.jpg" alt=""></a>
                </div>
                <div class="swiper-slide">
                    <a href="javascript:void(0)"><img src="images/02.jpg" alt=""></a>
                </div>
                <div class="swiper-slide">
                    <a href="javascript:void(0)"><img src="images/03.jpg" alt=""></a>
                </div>
            </div>
            <!-- 下一个和上一个 -->
            <!-- <div class="swiper-button-next"></div> -->
            <!-- <div class="swiper-button-prev"></div> -->
            <!-- 添加分页器 -->
            <div class="swiper-pagination"></div>
            <!--分页器。如果放置在swiper-container外面,需要自定义样式。-->
        </div>
        <!-- Swiper JS -->
        <script src="js/swiper.min.js"></script>

        <!-- Initialize Swiper -->
        <script>
            var swiper = new Swiper('.swiper-container', {
                dicreaction:'horizontal',
                grabCursor:true,
                autoplay: true, //是否自动播放
                loop: true, //设置无限循环
                navigation: {
                    nextEl: '.swiper-button-next',
                    prevEl: '.swiper-button-prev',
                },
                pagination: {
                    el: '.swiper-pagination',
                    bulletActiveClass: 'my-bullet-active',//自定义类名 
                },
            });
        </script>
    </body>

</html>