深入動畫函數的封裝 1.動畫函數的封裝 1.1 緩動效果的實現 這裡有一些核心的演算法,(目標值 現在的位置) / 10 = 每一次移動的步長 實現的代碼: 1.2 多值移動 比如我現在有這樣的一個需求:點擊走到800 再點擊走到500,相當於往回走了,我們可以加一個條件判斷一下步長 1. 如果是正值 ...
深入動畫函數的封裝
1.動畫函數的封裝
1.1 緩動效果的實現
這裡有一些核心的演算法,(目標值 - 現在的位置) / 10 = 每一次移動的步長
拿一個具體的效果舉例子,比如讓一個元素慢下來,
實現想法:讓元素的移動距離變下,每一次的步長都變小,核心的演算法:** (目標值 - 現在的位置) / 10 做為每次移動的距離步長**,其停止的條件就是當盒子到達目標位置就停止定時器
實現的代碼:
<body>
<button>點擊之後老李才飛!</button>
<span>BM老李</span>
<script>
// 緩動動畫函數封裝obj目標對象 target 目標位置
// 思路:
// 1. 讓盒子每次移動的距離慢慢變小, 速度就會慢慢落下來。
// 2. 核心演算法:(目標值 - 現在的位置) / 10 做為每次移動的距離 步長
// 3. 停止的條件是: 讓當前盒子位置等於目標位置就停止定時器
function animate(obj, target) {
// 先清除以前的定時器,只保留當前的一個定時器執行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
// 步長值寫到定時器的裡面
var step = (target - obj.offsetLeft) / 10;
if (obj.offsetLeft == target) {
// 停止動畫 本質是停止定時器
clearInterval(obj.timer);
}
// 把每次加1 這個步長值改為一個慢慢變小的值 步長公式:(目標值 - 現在的位置) / 10
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
var span = document.querySelector('span');
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
// 調用函數
animate(span, 500);
})
// 勻速動畫 就是 盒子是當前的位置 + 固定的值 10
// 緩動動畫就是 盒子當前的位置 + 變化的值(目標值 - 現在的位置) / 10)
</script>
</body>
1.2 多值移動
比如我現在有這樣的一個需求:點擊走到800 再點擊走到500,相當於往回走了,我們可以加一個條件判斷一下步長
- 如果是正值,則步長往大了取整
- 如果是負值,則步長 向小了取整
核心代碼
+++
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
// 解釋一下,這裡的,Math.celi(step)函數返回大於或等於一個給定數字的最小整數。比如我給一個0.95 它給我的就是1,比比如我給的4,它就是4 如果是-7.44 它就返回 -7
// Math.floor(step); 返回小於或等於一個給定數字的最大整數。註意這玩意兒 向下取整
+++
1.3 關於回調函數
關於回調函數其實比較簡單,說白了就是動作(函數)結束之後下一步改乾什麼。註意在動畫函數中,回調函數寫的位置:定時器結束的位置。
2.常見的網頁特效例子
2.1 輪播圖
註意哈,我們每次遇到問題的時候嗎,一定要先想清楚再去敲代碼,想清楚 非常非常的關鍵。不要一上來就敲代碼!!!,到頭來你都不知道在寫什麼鬼東西。
- 需求分析
1.滑鼠經過輪播圖模塊,左右按鈕顯示,離開隱藏左右按鈕。
2.點擊右側按鈕一次,圖片往左播放一張,以此類推,左側按鈕同理。
3.圖片播放的同時,下麵小圓圈模塊跟隨一起變化。
4.點擊小圓圈,可以播放相應圖片。
5.滑鼠不經過輪播圖,輪播圖也會自動播放圖片。
6.滑鼠經過,輪播圖模塊, 自動播放停止。
實現代碼:
window.addEventListener('load', function() {
// 1. 獲取元素
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.focus');
var focusWidth = focus.offsetWidth;
// 2. 滑鼠經過focus 就顯示隱藏左右按鈕
focus.addEventListener('mouseenter', function() {
arrow_l.style.display = 'block';
arrow_r.style.display = 'block';
clearInterval(timer);
timer = null; // 清除定時器變數
});
focus.addEventListener('mouseleave', function() {
arrow_l.style.display = 'none';
arrow_r.style.display = 'none';
timer = setInterval(function() {
//手動調用點擊事件
arrow_r.click();
}, 2000);
});
// 3. 動態生成小圓圈 有幾張圖片,我就生成幾個小圓圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
// console.log(ul.children.length);
for (var i = 0; i < ul.children.length; i++) {
// 創建一個小li
var li = document.createElement('li');
// 記錄當前小圓圈的索引號 通過自定義屬性來做
li.setAttribute('index', i);
// 把小li插入到ol 裡面
ol.appendChild(li);
// 4. 小圓圈的排他思想 我們可以直接在生成小圓圈的同時直接綁定點擊事件
li.addEventListener('click', function() {
// 幹掉所有人 把所有的小li 清除 current 類名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下我自己 當前的小li 設置current 類名
this.className = 'current';
// 5. 點擊小圓圈,移動圖片 當然移動的是 ul
// ul 的移動距離 小圓圈的索引號 乘以 圖片的寬度 註意是負值
// 當我們點擊了某個小li 就拿到當前小li 的索引號
var index = this.getAttribute('index');
// 當我們點擊了某個小li 就要把這個li 的索引號給 num
num = index;
// 當我們點擊了某個小li 就要把這個li 的索引號給 circle
circle = index;
// num = circle = index;
console.log(focusWidth);
console.log(index);
animate(ul, -index * focusWidth);
})
}
// 把ol裡面的第一個小li設置類名為 current
ol.children[0].className = 'current';
// 6. 克隆第一張圖片(li)放到ul 最後面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 7. 點擊右側按鈕, 圖片滾動一張
var num = 0;
// circle 控制小圓圈的播放
var circle = 0;
// flag 節流閥
var flag = true;
arrow_r.addEventListener('click', function() {
if (flag) {
flag = false; // 關閉節流閥
// 如果走到了最後複製的一張圖片,此時 我們的ul 要快速複原 left 改為 0
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth, function() {
flag = true; // 打開節流閥
});
// 8. 點擊右側按鈕,小圓圈跟隨一起變化 可以再聲明一個變數控制小圓圈的播放
circle++;
// 如果circle == 4 說明走到最後我們克隆的這張圖片了 我們就複原
if (circle == ol.children.length) {
circle = 0;
}
// 調用函數
circleChange();
}
});
// 9. 左側按鈕做法
arrow_l.addEventListener('click', function() {
if (flag) {
flag = false;
if (num == 0) {
num = ul.children.length - 1;
ul.style.left = -num * focusWidth + 'px';
}
num--;
animate(ul, -num * focusWidth, function() {
flag = true;
});
// 點擊左側按鈕,小圓圈跟隨一起變化 可以再聲明一個變數控制小圓圈的播放
circle--;
// 如果circle < 0 說明第一張圖片,則小圓圈要改為第4個小圓圈(3)
// if (circle < 0) {
// circle = ol.children.length - 1;
// }
circle = circle < 0 ? ol.children.length - 1 : circle;
// 調用函數
circleChange();
}
});
function circleChange() {
// 先清除其餘小圓圈的current類名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下當前的小圓圈的current類名
ol.children[circle].className = 'current';
}
// 10. 自動播放輪播圖
var timer = setInterval(function() {
//手動調用點擊事件
arrow_r.click();
}, 2000);
})
2.2 圖外話,截流閘
當我們執行上面的代碼的時候會發現一些小的bug,比如我瘋狂的去點擊下一張,就有問題了!這個時候截流閘孕育而生
核心原理:
節流閥目的:當上一個函數動畫內容執行完畢,再去執行下一個函數動畫,讓事件無法連續觸發。
核心實現思路:利用回調函數,添加一個變數來控制,鎖住函數和解鎖函數。
開始設置一個變數var flag= true;
If(flag){flag = false; do something} 關閉水龍頭
利用回調函數動畫執行完畢, flag = true 打開水龍頭
實例代碼塊:
2.3 返回頂部
重要的事情再說一遍!一定要先分析清楚需求再下手去做!
- 需求分析:
1. 帶有動畫的返回頂部
2. 此時可以繼續使用我們封裝的動畫函數
3. 只需要把所有的left 相關的值改為 跟 頁面垂直滾動距離相關就可以了
4. 頁面滾動了多少,可以通過 window.pageYOffset 得到
5. 最後是頁面滾動,使用 window.scroll(x,y)
代碼實現
//1. 獲取元素
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
// banner.offestTop 就是被捲去頭部的大小 一定要寫到滾動的外面
var bannerTop = banner.offsetTop
// 當我們側邊欄固定定位之後應該變化的數值
var sliderbarTop = sliderbar.offsetTop - bannerTop;
// 獲取main 主體元素
var main = document.querySelector('.main');
var goBack = document.querySelector('.goBack');
var mainTop = main.offsetTop;
// 2. 頁面滾動事件 scroll
document.addEventListener('scroll', function() {
// console.log(11);
// window.pageYOffset 頁面被捲去的頭部
// console.log(window.pageYOffset);
// 3 .當我們頁面被捲去的頭部大於等於了 172 此時 側邊欄就要改為固定定位
if (window.pageYOffset >= bannerTop) {
sliderbar.style.position = 'fixed';
sliderbar.style.top = sliderbarTop + 'px';
} else {
sliderbar.style.position = 'absolute';
sliderbar.style.top = '300px';
}
// 4. 當我們頁面滾動到main盒子,就顯示 goback模塊
if (window.pageYOffset >= mainTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
})
// 3. 當我們點擊了返回頂部模塊,就讓視窗滾動的頁面的最上方
goBack.addEventListener('click', function() {
// 裡面的x和y 不跟單位的 直接寫數字即可
// window.scroll(0, 0);
// 因為是視窗滾動 所以對象是window
animate(window, 0);
});
3.移動端的觸摸屏幕事件
3.1 觸屏事件
一般來說移動端的js都相容的比較不錯。
- touch事件一覽表
3.1 觸屏事件對象
TouchEvent 是一類描述手指在觸摸平面(觸摸屏、觸摸板等)的狀態變化的事件。這類事件用於描述一個或多個觸點,使開發者可以檢測觸點的移動,觸點的增加和減少,等等
touchstart、touchmove、touchend 三個事件都會各自有事件對象。
觸摸事件對象重點我們看三個常見對象列表:
註意哈在實際的開發中,最常用的還是targetTocuhes,因為平時我們都是給元素註冊觸摸事件
3.1 實現拖拽元素
touchstart、touchmove、touchend可以實現拖動元素
但是拖動元素需要當前手指的坐標值 我們可以使用 targetTouches[0] 裡面的pageX 和 pageY
移動端拖動的原理: 手指移動中,計算出手指移動的距離。然後用盒子原來的位置 + 手指移動的距離
手指移動的距離: 手指滑動中的位置 減去 手指剛開始觸摸的位置
拖動元素三步曲:
(1) 觸摸元素 touchstart: 獲取手指初始坐標,同時獲得盒子原來的位置
(2) 移動手指 touchmove: 計算手指的滑動距離,並且移動盒子
(3) 離開手指 touchend:
註意: 手指移動也會觸發滾動屏幕所以這裡要阻止預設的屏幕滾動 e.preventDefault();
示例代碼:
<body>
<div></div>
<script>
// (1) 觸摸元素 touchstart: 獲取手指初始坐標,同時獲得盒子原來的位置
// (2) 移動手指 touchmove: 計算手指的滑動距離,並且移動盒子
// (3) 離開手指 touchend:
var div = document.querySelector('div');
var startX = 0; //獲取手指初始坐標
var startY = 0;
var x = 0; //獲得盒子原來的位置
var y = 0;
div.addEventListener('touchstart', function(e) {
// 獲取手指初始坐標
startX = e.targetTouches[0].pageX;
startY = e.targetTouches[0].pageY;
x = this.offsetLeft;
y = this.offsetTop;
});
div.addEventListener('touchmove', function(e) {
// 計算手指的移動距離: 手指移動之後的坐標減去手指初始的坐標
var moveX = e.targetTouches[0].pageX - startX;
var moveY = e.targetTouches[0].pageY - startY;
// 移動我們的盒子 盒子原來的位置 + 手指移動的距離
this.style.left = x + moveX + 'px';
this.style.top = y + moveY + 'px';
e.preventDefault(); // 阻止屏幕滾動的預設行為
});
</script>
</body>