day-095-ninety-five-20230620-移动端事件处理-响应式
移动端事件处理
移动端的事件处理
- 移动端事件处理
- PC端主要以:
鼠标事件
、键盘事件
、资源加载事件
、动画事件
等事件为主。- 其中
click
在PC端
是点击事件
!
- 其中
- 移动端主要以:
手指事件
(单手指
和多手指
)、资源加载事件
、动画事件
等为主。- 其中,
click
在移动端
是单击事件
。
- 其中,
- PC端主要以:
移动端事件问题
- 移动端事件的各个问题:
-
问题1:
click事件
在移动端
存在300ms的延迟
。- 原因:
-
click事件
在移动端
是单击事件
:- 在第一次点击后,需要观察
300ms
,看是否触发了第二次点击;- 如果
没有触发第二次点击
,则为单击操作
,触发click事件
。 - 如果
触发了第二次点击
,则为双击操作
,click事件
是不触发的!
- 如果
- 在第一次点击后,需要观察
-
- 解决方案:
-
用
touch事件模型
-即单手指事件模型
,来代替click事件
。-
touchstart
:手指开始触摸。 -
touchmove
:手指移动。 -
touchend
:手指离开。<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>基于touch实现点击操作</title> <style> .box { position: absolute; top: 20px; left: 40px; box-sizing: border-box; width: 100px; height: 100px; border: 1px solid #000; } </style> </head> <body> <div class="box"></div> <!-- IMPORT JS --> <script> // 基于touch事件模型模拟出“点击”的效果 const box = document.querySelector(".box"); // 简易的处理方法:只要手指离开盒子,则认为触发了点击操作「这样是不准确的,如果手指之前发了移动,则本操作不再是点击,而是滑动」 box.ontouchend = function () { this.style.background = "pink"; }; </script> </body> </html>
-
touchcancel
:因意外情况如手机没电,导致touch事件取消
了。 -
但是这种方式,如果需要自己去实现,太繁琐了!
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>基于touch实现点击操作</title> <style> .box { position: absolute; top: 20px; left: 40px; box-sizing: border-box; width: 100px; height: 100px; border: 1px solid #000; } </style> </head> <body> <div class="box"></div> <!-- IMPORT JS --> <script> // 基于touch事件模型模拟出“点击”的效果 const box = document.querySelector(".box"); box.ontouchstart = function (ev) { /* 手指按下的时候:记录手指起始的坐标位置 ev:TouchEvent touches/targetTouches/changedTouches:都记录了手指位置的相关信息「伪数组」 我们平时都用changedTouches,因为其可以在 touchend 事件中,记录出手指离开屏幕时的坐标 */ let finger = ev.changedTouches[0]; this.startX = finger.pageX; this.startY = finger.pageY; this.isMove = false; }; box.ontouchmove = function (ev) { /* 手指移动的时候:获取最新的手指坐标,减去起始坐标,计算出偏移的距离 在给定的误差值(一般都是10px)范围内,计算是否发生移动 */ let finger = ev.changedTouches[0]; let changeX = finger.pageX - this.startX, changeY = finger.pageY - this.startY; if (Math.abs(changeX) > 10 || Math.abs(changeY) > 10) this.isMove = true; this.changeX = changeX; this.changeY = changeY; }; box.ontouchend = function (ev) { /* 手指离开屏幕的时候:判断是移动还是点击操作 如果是移动操作,还可以基于偏移的距离算出移动的方向 */ let { isMove, changeX, changeY } = this; if (!isMove) { console.log("当前是点击操作"); this.style.background = "pink"; return; } if (Math.abs(changeX) >= Math.abs(changeY)) { // 是左右滑动 if (changeX >= 0) { console.log("向右滑动"); } else { console.log("向左滑动"); } return; } // 是上下滑动 if (changeY >= 0) { console.log("向下滑动"); } else { console.log("向上滑动"); } }; /* 移动端的常规操作,基本上都是基于 touchstart/touchmove/touchend 模拟出来的 + 模拟点击 + 模拟滑动「知道滑动方向」 + 模拟单击/双击「300ms」 + 模拟长按「750ms」 + ... 对于一些需要多根手指进行的操作,可以基于 gesturestart/gesturechange/gestureend 模拟出来 + 缩放 + 旋转 + ... */ </script> </body> </html>
-
-
基于一些现有的封装好的事件库来解决。
-
fastclick.js 只能解决
click事件
的300ms延迟
问题。- 适用于:操作简单的移动端产品上,在此产品上,只有点击行为,此时我们继续使用
click事件
,只不过基于fastclick插件
,把其300ms延迟
处理掉即可! - 原理:基于
事件委托
,对页面中的click行为
做统一的处理
,核心还是基于touch事件模型
解决的! - 代码:
-
JS高级进阶/day0619_QQMusic/js/fastclick.js
-
JS高级进阶/day0619_QQMusic/touchDemo.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>基于touch实现点击操作</title> <style> .box { position: absolute; top: 20px; left: 40px; box-sizing: border-box; width: 100px; height: 100px; border: 1px solid #000; } </style> </head> <body> <div class="box"></div> <script src="js/fastclick.js"></script> <script> // 这样的处理在移动端会有300ms延迟问题 // 此时我们基于 fastclick 插件处理一下即可 FastClick.attach(document.body); const box = document.querySelector(".box"); box.onclick = function () { console.log(`fastclick`); this.style.background = "pink"; }; </script> </body> </html>
-
- 适用于:操作简单的移动端产品上,在此产品上,只有点击行为,此时我们继续使用
-
Zepto.js 被称为
移动端的jQuery库
,语法和jQuery
非常类似,但是能够更好的支持移动端。- 相比较于
jQuery
来讲:- 不考虑
IE8
及IE8以下版本
的兼容
。 - 只实现了
jQuery
中最常用
、最核心
的方法
。- 导致
Zepto
比jQuery库
小很多。
- 导致
- 支持
css3动画
。 - 封装了一套
完善的移动端事件处理方案
。
- 不考虑
- 支持的移动端事件操作。
-
tap
点击。 -
singleTap
单击。 -
doubleTap
双击。 -
longTap
长按。 -
swipe
、swipeLeft
/swipeRight
/swipeDown
/swipeUp
滑动。 -
pinchIn
/pinchOut
缩放。 - …
-
- 但是
Zepto
和jQuery
一样,其大部分代码是用来操作DOM
的,已经不适用于当下的Vue开发
/React开发
了。 - 代码:
-
JS高级进阶/day0619_QQMusic/js/zepto.min.js
-
JS高级进阶/day0619_QQMusic/touchDemo.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>基于touch实现点击操作</title> <style> .box { position: absolute; top: 20px; left: 40px; box-sizing: border-box; width: 100px; height: 100px; border: 1px solid #000; } </style> </head> <body> <div class="box"></div> <script src="js/zepto.min.js"></script> <script> $(".box").tap(function () { console.log(`zepto-tap`); $(this).css({ background: "pink", }); }); </script> </body> </html>
-
- 相比较于
-
hammer.js 移动端专属的事件库
- hammer.js
- 代码:
-
JS高级进阶/day0619_QQMusic/js/hammer.min.js
-
JS高级进阶/day0619_QQMusic/touchDemo.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>基于touch实现点击操作</title> <style> .box { position: absolute; top: 20px; left: 40px; box-sizing: border-box; width: 100px; height: 100px; border: 1px solid #000; } </style> </head> <body> <div class="box"></div> <script src="js/hammer.min.js"></script> <script> const box = document.querySelector(".box"); const instHammer = new Hammer(box); instHammer.on("tap", function () { console.log(`hammer-tap`); box.style.background = "pink"; }); </script> </body> </html>
-
-
-
- 原因:
-
问题2:事件穿透问题
- 事件传透的是指:触发某个目标元素的触摸事件(touch事件)时,会同时触发
该目标元素相同位置中其他元素
的鼠标点击click事件
。- 触发步骤:
- 触摸第一层,让第一层隐藏。
- 露出第二层,而第二层是基于click事件处理的!
- 触发步骤:
- 事件触发的先后顺序是:
- 解决方案:click和touch事件不要混合在一起使用!!
- 事件传透的是指:触发某个目标元素的触摸事件(touch事件)时,会同时触发
-
问题3:keydown/up/press等事件在移动端用不了,统一基于input事件代替即可!
- 文本框.οninput=function(){}
- 只要文本框中有内容的输入,则input事件就会触发!
-
移动端模拟点击操作
没做处理-直接用click代替
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>基于touch实现点击操作</title>
<style>
.box {
position: absolute;
top: 20px;
left: 40px;
box-sizing: border-box;
width: 100px;
height: 100px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
<script>
const box = document.querySelector(".box");
// 这样的处理在移动端会有300ms延迟问题。
box.onclick = function () {
this.style.background = "pink";
};
</script>
简易的处理方法-直接用touchend代替
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>基于touch实现点击操作</title>
<style>
.box {
position: absolute;
top: 20px;
left: 40px;
box-sizing: border-box;
width: 100px;
height: 100px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
<script>
// 基于touch事件模型模拟出点击的效果。
const box = document.querySelector(".box");
// 简易的处理方法:只要手指离开盒子,则认为触发了点击操作。
// 这样是不准确的,如果手指之前发生了移动,则本操作不再是点击,而是滑动。
box.ontouchend = function () {
console.log(`touchend-->`);
this.style.background = "pink";
};
</script>
用touch事件模型来代替click事件
- 思路:
- 手指按下的时候:记录手指起始的坐标位置。
- ev:TouchEvent
- touches/targetTouches/changedTouches:都记录了手指位置的相关信息,结果是一个伪数组。
- 我们平时都用changedTouches,因为其可以在touchend事件中,记录出手指离开屏幕时的坐标。
- touches/targetTouches/changedTouches:都记录了手指位置的相关信息,结果是一个伪数组。
- ev:TouchEvent
- 手指移动的时候:获取最新的手指坐标,减去起始坐标,计算出偏移的距离。
- 在给定的误差值(一般都是10px)范围内,计算是否发生移动。
- 手指离开屏幕的时候:判断是移动还是点击操作。
- 如果是移动操作,还可以基于偏移的距离算出移动的方向。
- 移动端的事件
- 移动端的常规操作,基本上都是基于touchstart/touchmove/touchend模拟出来的。
- 模拟点击。
- 模拟滑动-知道滑动方向。
- 模拟单击/双击-300ms。
- 模拟长按-750ms。
- …
- 对于一些需要多根手指进行的操作,可以基于gesturestart/gesturechange/gestureend模拟出来。
- 缩放。
- 旋转。
- …
- 移动端的常规操作,基本上都是基于touchstart/touchmove/touchend模拟出来的。
- 手指按下的时候:记录手指起始的坐标位置。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>基于touch实现点击操作</title>
<style>
.box {
position: absolute;
top: 20px;
left: 40px;
box-sizing: border-box;
width: 100px;
height: 100px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
<script>
// 基于touch事件模型模拟出点击的效果。
const box = document.querySelector(".box");
box.ontouchstart = function (ev) {
// 手指按下的时候:记录手指起始的坐标位置。
// ev:TouchEvent
// - touches/targetTouches/changedTouches:都记录了手指位置的相关信息,结果是一个伪数组。
// - 我们平时都用changedTouches,因为其可以在touchend事件中,记录出手指离开屏幕时的坐标。
// console.log(`ev-->`, ev);
let finger = ev.changedTouches[0];
// console.log(`finger-->`, finger);
this.startX = finger.pageX;
this.startY = finger.pageY;
this.isMove = false;
};
box.ontouchmove = function (ev) {
// 手指移动的时候:获取最新的手指坐标,减去起始坐标,计算出偏移的距离。
// 在给定的误差值(一般都是10px)范围内,计算是否发生移动。
let finger = ev.changedTouches[0];
// console.log(`finger-->`, finger);
let changeX = finger.pageX - this.startX;
let changeY = finger.pageY - this.startY;
if (Math.abs(changeX) > 10 || Math.abs(changeY) > 10) {
this.isMove = true;
}
this.changeX = changeX;
this.changeY = changeY;
};
box.ontouchend = function (ev) {
// 手指离开屏幕的时候:判断是移动还是点击操作。
// 如果是移动操作,还可以基于偏移的距离算出移动的方向。
let { isMove, changeX, changeY } = this;
if (!isMove) {
console.log(`当前是点击操作`);
this.style.background = "pink";
return;
}
if (Math.abs(changeX) >= Math.abs(changeY)) {
// 是左右滑动。
if (changeX >= 0) {
console.log(`向右滑动`);
} else {
console.log(`向左滑动`);
}
return
}
// 是上下滑动。
if (changeY >= 0) {
console.log(`向下滑动`);
} else {
console.log(`向上滑动`);
}
};
// - 移动端的常规操作,基本上都是基于touchstart/touchmove/touchend模拟出来的。
// - 模拟点击。
// - 模拟滑动-知道滑动方向。
// - 模拟单击/双击-300ms。
// - 模拟长按-750ms。
// - ...
// - 对于一些需要多根手指进行的操作,可以基于gesturestart/gesturechange/gestureend模拟出来。
// - 缩放。
// - 旋转。
// - ...
</script>
基于fastclick插件处理
基于zepto进行处理
基于hammer进行处理
响应式
响应式布局开发技巧
- 需要做响应式:
- PC端全屏项目(一般都是管理系统)
- 技术方案:外层容器的宽高采用百分比布局-如vw与vh,一些具体的元素基本都是固定布局。偶尔基于@media进行微调。
- PC端和移动端共用一套项目(一般是结构和样式较为简单的企业官网/宣传页等)
-
技术方案:流式布局(外层容器的宽度按照百分比方式处理),基于@media进行结构和样式的调整。
@media all and (max-width:960px) { .header{ //... } ... }
- 基于@media调整样式越细,展示效果会越好!
- 核心就是大量写样式。
-
- 移动端项目(适配不同型号的手机设备,有的还需要适配pad端)
- 传统方案:基于@media进行样式调整,尽可能适配更多的设备,这是官方提供的方案。
- 这也是官方唯一认可方案:@media。
- 发展:
- 固定布局:只写最小的尺寸,如320px,中间居中。
- …
- 2023年目前:rem等比缩放。
- 新方案:rem等比缩放。
- 不论那一种方案,目前做排列布局,基本上都是基于flex来处理!
- 传统方案:基于@media进行样式调整,尽可能适配更多的设备,这是官方提供的方案。
- PC端全屏项目(一般都是管理系统)
rem响应式布局方案
-
移动端响应式布局第一步:设置meta标签的viewport。
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
- viewport:设置视口(或html页面)的规则。
- width=device-width 让HTML渲染的宽度和设备宽度保持一致。
- initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0 不让HTML页面进行缩放。
- maximum-scale=1.0, minimum-scale=1.0 是为了兼容安卓低端机。
- user-scalable=no 禁止手动缩放。
- 如果不设置meta标签的viewport会出现什么问题?
- 不论手机设备多宽,HTML页面都是以980px的宽度进行渲染。这样就会出现,手机屏幕渲染不全的情况。
- 不完全渲染:就会出现横竖向的滚动条。
- 完全渲染:就要把页面整体缩小,所有内容都会变得很小。
- 不论手机设备多宽,HTML页面都是以980px的宽度进行渲染。这样就会出现,手机屏幕渲染不全的情况。
- viewport:设置视口(或html页面)的规则。
-
rem响应式布局方案
- 什么是rem?
-
px物理像素,固定单位。
-
em相对单位,相对于父元素的字体大小。
-
一般用于段落首行缩进两个汉字之类的效果。
.box{ font-size:14px;//此时在该元素的子元素中1em=14px; p{ text-indent:2em;//首行缩进两个字符。 } }
-
-
rem(root em) 相对单位,相对于根元素(也就是html)的字体大小。
html{ font-size:20px;//1rem=20px。浏览器能识别的最小字体是12px。 } .box1{ width:100px; } .box2{ width:5rem; } //目前box1和box2是一样大的。 //但是如果以后,把html的字体改为30px了,那么box1依然是100px,但是box2则自动变为150px了! //也就是:只要修改根元素的字体大小,那么所有以rem为单位的样式,都会自动跟着等比缩放。
- 也就是:只要修改根元素的字体大小,那么所有以rem为单位的样式,都会自动跟着等比缩放。
-
- 基于rem实现响应式布局开发的步骤:
-
第一步:按照特定的尺寸(一般是设计稿的尺寸),设置rem和px的初始转换比例,然后把测量出来的像素值,全部按照这个比例,改为rem值,赋值给元素的样式!
-
设计稿的尺寸:一般都是750px的。
- 但UI组件库的设计稿一般都是375px的。
-
rem和px的初始转换比例:设置的值一定是方便计算的!
html{ font-size:16px;//1rem=100px ; 750px }
量出来一个盒子大小是300px*260px。font-size为28px。
.box{ width:3rem; height:2.6rem; font-size:0.28rem; }
-
-
第二步:获取当前设备的尺寸,计算出相比较于设计稿而言,缩放的比例。然后按照这个比例,去修改html的字体大小(也就是rem和px的换算比例)。
- 公式:当前设备的宽度/当前的换算比例=设计稿宽度(750px)/初始换算比例(100)
- 当前的换算比例=(当前设备的宽度/设计稿宽度(750px))*初始换算比例(100)
- 基于公式计算出最新的换算比例后,修改html的字体大小,那么之前所有以rem为单位的样式,都会按照最新的换算比例,实现等比缩放。
- 公式:当前设备的宽度/当前的换算比例=设计稿宽度(750px)/初始换算比例(100)
-
第三步:我们一般都会限制一个最大的缩放范围(比如:540),设备宽度即便超过这个范围,换算比例也不会再继续放大了,整个页面内容最宽540,左右两边预留空白即可。
- 为什么设计师给我们的设计稿,都比实际的手机尺寸大一倍?
- iphone6/7/8 -> 375px
- iphone6/7/8 plus / iphoneXR -> 414px
- 750px的设计稿是参照375px设备来设计的,之所以大一倍,是因为:DPR屏幕像素密度比Device Pixel Ratio
- 物理像素
- 分辨率
- 在DPR=2.0的设备上,我们准备的原始图片大小,要比最后设置的尺寸大一倍。
- 屏幕就是按照大一倍的方式渲染的。
- 在DPR=3.0的上,原始图片大小,要比设置的尺寸大两倍。
- …
- DPR对图片是最有影响的,对于文字等影响不大!!而且经过实测,DPR=3.0相比于DPR=2.0,变化也不是很大!
- 所以设计师给我们比真正尺寸大一倍的设计稿,其目的:让我们切出比设计尺寸大一倍的图片!如果设计稿中没有图片,按照375px的设计稿也是没有问题的。UI组件库一般都是这样的!
- 理论上,官方告诉我们图片的处理方案是这样的:
- 准备三张图
logo.png
logo@2x.png
logo@3x.png
。 - 我们需要根据当前设备的DPR,来决定使用那张图。
- 准备三张图
- 只不过前端这样处理太麻烦了,需要写js代码或@media动态控制加载的图片,而NativeApp开发有现成的处理方案。
- 都是按照三张图处理。
- 所以在WebApp开发中,我们只会准备一张二倍图,不论DPR是多少,加载的都是这个二倍图。
- 如果某些二倍图片在DPR为3的设备上,看越来模糊,就单独找设计师要一张三倍图。
- 目前实测,一般用二倍图就好了!
- 为什么设计师给我们的设计稿,都比实际的手机尺寸大一倍?
-
- 什么是rem?
移动端处理css
- 在移动端编写CSS3样式,为了兼容低版本的浏览器,我们需要写两套
- -webkit-transition:
- transition:
- 期望可以自动加前缀
-
webpack -> postcss
- 需要使用webpack。
-
prefixfree.min.js文章来源:https://www.toymoban.com/news/detail-493399.html
- 可以通过引入这个js来就可以了。
<script src="./js/prefixfree.min.js" async></script>
-
…文章来源地址https://www.toymoban.com/news/detail-493399.html
-
参考
到了这里,关于20230620----重返学习-移动端事件处理-响应式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!