觸屏事件概述
移動(dòng)端瀏覽器兼容性較好,我們不需要考慮以前 JS 的兼容性問(wèn)題,可以放心的使用原生 JS 書(shū)寫(xiě)效果,但是移動(dòng)端也有自己獨(dú)特的地方。比如觸屏事件 touch(也稱觸摸事件),Android和 IOS 都有。
touch 對(duì)象代表一個(gè)觸摸點(diǎn)。觸摸點(diǎn)可能是一根手指,也可能是一根觸摸筆。觸屏事件可響應(yīng)用戶手指(或觸控筆)對(duì)屏幕或者觸控板操作。
常見(jiàn)的觸屏事件如下:

觸摸事件對(duì)象(TouchEvent)
TouchEvent 是一類(lèi)描述手指在觸摸平面(觸摸屏、觸摸板等)的狀態(tài)變化的事件。這類(lèi)事件用于描述一個(gè)或多個(gè)觸點(diǎn),使開(kāi)發(fā)者可以檢測(cè)觸點(diǎn)的移動(dòng),觸點(diǎn)的增加和減少,等等

因?yàn)槠綍r(shí)我們都是給元素注冊(cè)觸摸事件,所以重點(diǎn)記住 targetTocuhes
touchstart、touchmove、touchend 三個(gè)事件都會(huì)各自有事件對(duì)象。
<script>
// 觸摸事件對(duì)象
// 1. 獲取元素
// 2. 手指觸摸DOM元素事件
var div = document.querySelector('div');
div.addEventListener('touchstart', function(e) {
// console.log(e);
// touches 正在觸摸屏幕的所有手指的列表
// targetTouches 正在觸摸當(dāng)前DOM元素的手指列表
// 如果偵聽(tīng)的是一個(gè)DOM元素,他們兩個(gè)是一樣的
// changedTouches 手指狀態(tài)發(fā)生了改變的列表 從無(wú)到有 或者 從有到無(wú)
// 因?yàn)槲覀円话愣际怯|摸元素 所以最經(jīng)常使用的是 targetTouches
console.log(e.targetTouches[0]);
// targetTouches[0] 就可以得到正在觸摸dom元素的第一個(gè)手指的相關(guān)信息比如 手指的坐標(biāo)等等
});
// 3. 手指在DOM元素身上移動(dòng)事件
div.addEventListener('touchmove', function() {
});
// 4. 手指離開(kāi)DOM元素事件
div.addEventListener('touchend', function(e) {
// console.log(e);
// 當(dāng)我們手指離開(kāi)屏幕的時(shí)候,就沒(méi)有了 touches 和 targetTouches 列表
// 但是會(huì)有 changedTouches
});
</script>
案例:移動(dòng)端拖動(dòng)元素
touchstart、touchmove、touchend可以實(shí)現(xiàn)拖動(dòng)元素
但是拖動(dòng)元素需要當(dāng)前手指的坐標(biāo)值 我們可以使用 targetTouches[0] 里面的pageX 和 pageY
移動(dòng)端拖動(dòng)的原理: 手指移動(dòng)中,計(jì)算出手指移動(dòng)的距離。然后用盒子原來(lái)的位置 + 手指移動(dòng)的距離
-
手指移動(dòng)的距離: 手指滑動(dòng)中的位置 減去 手指剛開(kāi)始觸摸的位置
拖動(dòng)元素三步曲:
(1) 觸摸元素 touchstart: 獲取手指初始坐標(biāo),同時(shí)獲得盒子原來(lái)的位置
(2) 移動(dòng)手指 touchmove: 計(jì)算手指的滑動(dòng)距離,并且移動(dòng)盒子
(3) 離開(kāi)手指 touchend:
注意: 手指移動(dòng)也會(huì)觸發(fā)滾動(dòng)屏幕所以這里要阻止默認(rèn)的屏幕滾動(dòng) e.preventDefault();
// (1) 觸摸元素 touchstart: 獲取手指初始坐標(biāo),同時(shí)獲得盒子原來(lái)的位置
// (2) 移動(dòng)手指 touchmove: 計(jì)算手指的滑動(dòng)距離,并且移動(dòng)盒子
// (3) 離開(kāi)手指 touchend:
var div = document.querySelector('div');
var startX = 0; //獲取手指初始坐標(biāo)
var startY = 0;
var x = 0; //獲得盒子原來(lái)的位置
var y = 0;
div.addEventListener('touchstart', function(e) {
// 獲取手指初始坐標(biāo)
startX = e.targetTouches[0].pageX;
startY = e.targetTouches[0].pageY;
x = this.offsetLeft;
y = this.offsetTop;
});
div.addEventListener('touchmove', function(e) {
// 計(jì)算手指的移動(dòng)距離: 手指移動(dòng)之后的坐標(biāo)減去手指初始的坐標(biāo)
var moveX = e.targetTouches[0].pageX - startX;
var moveY = e.targetTouches[0].pageY - startY;
// 移動(dòng)我們的盒子 盒子原來(lái)的位置 + 手指移動(dòng)的距離
this.style.left = x + moveX + 'px';
this.style.top = y + moveY + 'px';
e.preventDefault(); // 阻止屏幕滾動(dòng)的默認(rèn)行為
});
移動(dòng)端常見(jiàn)特效
移動(dòng)端輪播圖功能和基本PC端一致。
- 可以自動(dòng)播放圖片
- 手指可以拖動(dòng)播放輪播圖
案例分析:
- 自動(dòng)播放功能
- 開(kāi)啟定時(shí)器
- 移動(dòng)端移動(dòng),可以使用translate 移動(dòng)
-
想要圖片優(yōu)雅的移動(dòng),請(qǐng)?zhí)砑舆^(guò)渡效果
image.png - 自動(dòng)播放功能-無(wú)縫滾動(dòng)
- 注意,我們判斷條件是要等到圖片滾動(dòng)完畢再去判斷,就是過(guò)渡完成后判斷
- 此時(shí)需要添加檢測(cè)過(guò)渡完成事件 transitionend
- 判斷條件:如果索引號(hào)等于 3 說(shuō)明走到最后一張圖片,此時(shí) 索引號(hào)要復(fù)原為 0
- 此時(shí)圖片,去掉過(guò)渡效果,然后移動(dòng)
- 如果索引號(hào)小于0, 說(shuō)明是倒著走, 索引號(hào)等于2
-
此時(shí)圖片,去掉過(guò)渡效果,然后移動(dòng)
image.png
- 完整版:
window.addEventListener('load', function() {
// alert(1);
// 1. 獲取元素
var focus = document.querySelector('.focus');
var ul = focus.children[0];
// 獲得focus 的寬度
var w = focus.offsetWidth;
var ol = focus.children[1];
// 2. 利用定時(shí)器自動(dòng)輪播圖圖片
var index = 0;
var timer = setInterval(function() {
index++;
var translatex = -index * w;
ul.style.transition = 'all .3s';
ul.style.transform = 'translateX(' + translatex + 'px)';
}, 2000);
// 等著我們過(guò)渡完成之后,再去判斷 監(jiān)聽(tīng)過(guò)渡完成的事件 transitionend
ul.addEventListener('transitionend', function() {
// 無(wú)縫滾動(dòng)
if (index >= 3) {
index = 0;
// console.log(index);
// 去掉過(guò)渡效果 這樣讓我們的ul 快速的跳到目標(biāo)位置
ul.style.transition = 'none';
// 利用最新的索引號(hào)乘以寬度 去滾動(dòng)圖片
var translatex = -index * w;
ul.style.transform = 'translateX(' + translatex + 'px)';
} else if (index < 0) {
index = 2;
ul.style.transition = 'none';
// 利用最新的索引號(hào)乘以寬度 去滾動(dòng)圖片
var translatex = -index * w;
ul.style.transform = 'translateX(' + translatex + 'px)';
}
// 3. 小圓點(diǎn)跟隨變化
// 把ol里面li帶有current類(lèi)名的選出來(lái)去掉類(lèi)名 remove
ol.querySelector('.current').classList.remove('current');
// 讓當(dāng)前索引號(hào) 的小li 加上 current add
ol.children[index].classList.add('current');
});
// 4. 手指滑動(dòng)輪播圖
// 觸摸元素 touchstart: 獲取手指初始坐標(biāo)
var startX = 0;
var moveX = 0; // 后面我們會(huì)使用這個(gè)移動(dòng)距離所以要定義一個(gè)全局變量
var flag = false;
ul.addEventListener('touchstart', function(e) {
startX = e.targetTouches[0].pageX;
// 手指觸摸的時(shí)候就停止定時(shí)器
clearInterval(timer);
});
// 移動(dòng)手指 touchmove: 計(jì)算手指的滑動(dòng)距離, 并且移動(dòng)盒子
ul.addEventListener('touchmove', function(e) {
// 計(jì)算移動(dòng)距離
moveX = e.targetTouches[0].pageX - startX;
// 移動(dòng)盒子: 盒子原來(lái)的位置 + 手指移動(dòng)的距離
var translatex = -index * w + moveX;
// 手指拖動(dòng)的時(shí)候,不需要?jiǎng)赢?huà)效果所以要取消過(guò)渡效果
ul.style.transition = 'none';
ul.style.transform = 'translateX(' + translatex + 'px)';
flag = true; // 如果用戶手指移動(dòng)過(guò)我們?cè)偃ヅ袛喾駝t不做判斷效果
e.preventDefault(); // 阻止?jié)L動(dòng)屏幕的行為
});
// 手指離開(kāi) 根據(jù)移動(dòng)距離去判斷是回彈還是播放上一張下一張
ul.addEventListener('touchend', function(e) {
if (flag) {
// (1) 如果移動(dòng)距離大于50像素我們就播放上一張或者下一張
if (Math.abs(moveX) > 50) {
// 如果是右滑就是 播放上一張 moveX 是正值
if (moveX > 0) {
index--;
} else {
// 如果是左滑就是 播放下一張 moveX 是負(fù)值
index++;
}
var translatex = -index * w;
ul.style.transition = 'all .3s';
ul.style.transform = 'translateX(' + translatex + 'px)';
} else {
// (2) 如果移動(dòng)距離小于50像素我們就回彈
var translatex = -index * w;
ul.style.transition = 'all .1s';
ul.style.transform = 'translateX(' + translatex + 'px)';
}
}
// 手指離開(kāi)的時(shí)候就重新開(kāi)啟定時(shí)器
clearInterval(timer);
timer = setInterval(function() {
index++;
var translatex = -index * w;
ul.style.transition = 'all .3s';
ul.style.transform = 'translateX(' + translatex + 'px)';
}, 2000);
});
// 返回頂部模塊制作
var goBack = document.querySelector('.goBack');
var nav = document.querySelector('nav');
window.addEventListener('scroll', function() {
if (window.pageYOffset >= nav.offsetTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
});
goBack.addEventListener('click', function() {
window.scroll(0, 0);
})
})
classList 屬性
classList屬性是HTML5新增的一個(gè)屬性,返回元素的類(lèi)名。但是ie10以上版本支持。
該屬性用于在元素中添加,移除及切換 CSS 類(lèi)。有以下方法
element.classList.add(’類(lèi)名’);
focus.classList.add('current');
移除類(lèi):
element.classList.remove(’類(lèi)名’);
focus.classList.remove('current');
切換類(lèi):
element.classList.toggle(’類(lèi)名’);
focus.classList.toggle('current');
注意:以上方法里面,所有類(lèi)名都不帶點(diǎn)
click 延時(shí)解決方案
移動(dòng)端 click 事件會(huì)有 300ms 的延時(shí),原因是移動(dòng)端屏幕雙擊會(huì)縮放(double tap to zoom) 頁(yè)面。
解決方案:
- 禁用縮放。 瀏覽器禁用默認(rèn)的雙擊縮放行為并且去掉300ms 的點(diǎn)擊延遲。
<meta name="viewport" content="user-scalable=no">
- 2.利用touch事件自己封裝這個(gè)事件解決300ms 延遲。
原理就是:
- 當(dāng)我們手指觸摸屏幕,記錄當(dāng)前觸摸時(shí)間
- 當(dāng)我們手指離開(kāi)屏幕, 用離開(kāi)的時(shí)間減去觸摸的時(shí)間
- 如果時(shí)間小于150ms,并且沒(méi)有滑動(dòng)過(guò)屏幕, 那么我們就定義為點(diǎn)擊
//封裝tap,解決click 300ms 延時(shí)
function tap (obj, callback) {
var isMove = false;
var startTime = 0; // 記錄觸摸時(shí)候的時(shí)間變量
obj.addEventListener('touchstart', function (e) {
startTime = Date.now(); // 記錄觸摸時(shí)間
});
obj.addEventListener('touchmove', function (e) {
isMove = true; // 看看是否有滑動(dòng),有滑動(dòng)算拖拽,不算點(diǎn)擊
});
obj.addEventListener('touchend', function (e) {
if (!isMove && (Date.now() - startTime) < 150) { // 如果手指觸摸和離開(kāi)時(shí)間小于150ms 算點(diǎn)擊
callback && callback(); // 執(zhí)行回調(diào)函數(shù)
}
isMove = false; // 取反 重置
startTime = 0;
});
}
//調(diào)用
tap(div, function(){ // 執(zhí)行代碼 });
- 3.使用插件。fastclick 插件解決300ms 延遲。
<script>
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
var div = document.querySelector('div');
div.addEventListener('click', function() {
alert(11);
})
</script>
移動(dòng)端常用開(kāi)發(fā)插件
什么是插件
移動(dòng)端要求的是快速開(kāi)發(fā),所以我們經(jīng)常會(huì)借助于一些插件來(lái)幫我完成操作,那么什么是插件呢?
JS 插件是 js 文件,它遵循一定規(guī)范編寫(xiě),方便程序展示效果,擁有特定功能且方便調(diào)用。如輪播圖和瀑布流插件。
特點(diǎn):它一般是為了解決某個(gè)問(wèn)題而專門(mén)存在,其功能單一,并且比較小。
我們以前寫(xiě)的animate.js 也算一個(gè)最簡(jiǎn)單的插件
fastclick 插件解決 300ms 延遲。 使用延時(shí)
GitHub官網(wǎng)地址: https://github.com/ftlabs/fastclick
插件的使用
引入 js 插件文件。
按照規(guī)定語(yǔ)法使用。
fastclick 插件解決 300ms 延遲。 使用延時(shí)
GitHub官網(wǎng)地址: https://github.com/ftlabs/fastclick
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
Swiper 插件的使用
中文官網(wǎng)地址: https://www.swiper.com.cn/
引入插件相關(guān)文件。
按照規(guī)定語(yǔ)法使用
其他移動(dòng)端常見(jiàn)插件
lsuperslide: http://www.superslide2.com/
l iscroll: https://github.com/cubiq/iscroll
插件的使用總結(jié)
1.確認(rèn)插件實(shí)現(xiàn)的功能
2.去官網(wǎng)查看使用說(shuō)明
3.下載插件
4.打開(kāi)demo實(shí)例文件,查看需要引入的相關(guān)文件,并且引入
5.復(fù)制demo實(shí)例文件中的結(jié)構(gòu)html,樣式css以及js代碼
移動(dòng)端視頻插件 zy.media.js
H5 給我們提供了 video 標(biāo)簽,但是瀏覽器的支持情況不同。
不同的視頻格式文件,我們可以通過(guò)source解決。
但是外觀樣式,還有暫停,播放,全屏等功能我們只能自己寫(xiě)代碼解決。
這個(gè)時(shí)候我們可以使用插件方式來(lái)制作。
我們可以通過(guò) JS 修改元素的大小、顏色、位置等樣式。
移動(dòng)端常用開(kāi)發(fā)框架
移動(dòng)端視頻插件 zy.media.js
框架,顧名思義就是一套架構(gòu),它會(huì)基于自身的特點(diǎn)向用戶提供一套較為完整的解決方案。框架的控制權(quán)在框架本身,使用者要按照框架所規(guī)定的某種規(guī)范進(jìn)行開(kāi)發(fā)。
插件一般是為了解決某個(gè)問(wèn)題而專門(mén)存在,其功能單一,并且比較小。
前端常用的框架有 Bootstrap、Vue、Angular、React 等。既能開(kāi)發(fā)PC端,也能開(kāi)發(fā)移動(dòng)端
前端常用的移動(dòng)端插件有 swiper、superslide、iscroll等。
框架: 大而全,一整套解決方案
插件: 小而專一,某個(gè)功能的解決方案
Bootstrap
Bootstrap 是一個(gè)簡(jiǎn)潔、直觀、強(qiáng)悍的前端開(kāi)發(fā)框架,它讓 web 開(kāi)發(fā)更迅速、簡(jiǎn)單。
它能開(kāi)發(fā)PC端,也能開(kāi)發(fā)移動(dòng)端
Bootstrap JS插件使用步驟:
1.引入相關(guān)js 文件
2.復(fù)制HTML 結(jié)構(gòu)
3.修改對(duì)應(yīng)樣式
4.修改相應(yīng)JS 參數(shù)

