在所有非同步中行為中柠衅,最麻煩的大概就是 UI 操作了烤黍,因?yàn)?UI 是直接影響使用者的感受漠吻,如果處理的不好對使用體驗(yàn)會大大的扣分赵誓!
UI 大概是所有非同步行為中最不好處理的打毛,不只是因?yàn)樗苯佑绊懥擞脩趔w驗(yàn),更大的問題是 UI 互動常常是高頻率觸發(fā)的事件俩功,而且多個(gè)元件間的時(shí)間序需要不一致幻枉,要做到這樣的 UI 互動就不太可能用 Promise 或 async/await,但是用 RxJS 仍然能輕易地處理诡蜓!
今天我們要介紹的兩個(gè) Operators熬甫,delay 跟 delayWhen 都是跟 UI 互動比較相關(guān)的。當(dāng)我們的網(wǎng)頁越來越像應(yīng)用程式時(shí)蔓罚,UI 互動就變得越重要椿肩,讓我們來試試如何用 RxJS 完成基本的 UI 互動瞻颂!
Operators
delay
delay 可以延遲 observable 一開始發(fā)送元素的時(shí)間點(diǎn),范例如下
var source = Rx.Observable.interval(300).take(5);
var example = source.delay(500);
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});
// 0
// 1
// 2
// 3
// 4
當(dāng)然直接從 log 出的訊息看郑象,是完全看不出差異的
讓我們直接看 Marble Diagram
source : --0--1--2--3--4|
delay(500)
example: -------0--1--2--3--4|
從 Marble Diagram 可以看得出來贡这,第一次送出元素的時(shí)間變慢了,雖然在這裡看起來沒什麼用厂榛,但是在 UI 操作上是非常有用的盖矫,這個(gè)部分我們最后示范。
delay 除了可以傳入毫秒以外击奶,也可以傳入 Date 型別的資料辈双,如下使用方式
var source = Rx.Observable.interval(300).take(5);
var example = source.delay(new Date(new Date().getTime() + 1000));
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});
這好像也能用在預(yù)定某個(gè)日期,讓程式掛掉
delayWhen
delayWhen 的作用跟 delay 很像柜砾,最大的差別是 delayWhen 可以影響每個(gè)元素湃望,而且需要傳一個(gè) callback 并回傳一個(gè) observable,范例如下
var source = Rx.Observable.interval(300).take(5);
var example = source
.delayWhen(
x => Rx.Observable.empty().delay(100 * x * x)
);
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});
這時(shí)我們的 Marble Diagram 如下
source : --0--1--2--3--4|
.delayWhen(x => Rx.Observable.empty().delay(100 * x * x));
example: --0---1----2-----3-----4|
這裡傳進(jìn)來的 x 就是 source 送出的每個(gè)元素局义,這樣我們就能對每一個(gè)做延遲喜爷。
這裡我們用 delay 來做一個(gè)小功能冗疮,這個(gè)功能很簡單就是讓多張照片跟著滑鼠跑萄唇,但每張照片不能跑一樣快!
首先我們準(zhǔn)備六張大頭照术幔,并且寫進(jìn) HTML
<img src="https://res.cloudinary.com/dohtkyi84/image/upload/c_scale,w_50/v1483019072/head-cover6.jpg" alt="">
<img src="https://res.cloudinary.com/dohtkyi84/image/upload/c_scale,w_50/v1483019072/head-cover5.jpg" alt="">
<img src="https://res.cloudinary.com/dohtkyi84/image/upload/c_scale,w_50/v1483019072/head-cover4.jpg" alt="">
<img src="https://res.cloudinary.com/dohtkyi84/image/upload/c_scale,w_50/v1483019072/head-cover3.jpg" alt="">
<img src="https://res.cloudinary.com/dohtkyi84/image/upload/c_scale,w_50/v1483019072/head-cover2.jpg" alt="">
<img src="https://res.cloudinary.com/dohtkyi84/image/upload/c_scale,w_50/v1483019072/head-cover1.jpg" alt="">
用 CSS 把 img 改成圓形另萤,并加上邊筐以及絕對位置
img{
position: absolute;
border-radius: 50%;
border: 3px white solid;
transform: translate3d(0,0,0);
}
再來寫 JS,一樣第一步先抓 DOM
var imgList = document.getElementsByTagName('img');
第二步建立 observable
var movePos = Rx.Observable.fromEvent(document, 'mousemove')
.map(e => ({ x: e.clientX, y: e.clientY }))
第三步撰寫邏輯
function followMouse(DOMArr) {
const delayTime = 600;
DOMArr.forEach((item, index) => {
movePos
.delay(delayTime * (Math.pow(0.65, index) + Math.cos(index / 4)) / 2)
.subscribe(function (pos){
item.style.transform = 'translate3d(' + pos.x + 'px, ' + pos.y + 'px, 0)';
});
});
}
followMouse(Array.from(imgList))
這裡我們把 imgList 從 Collection 轉(zhuǎn)成 Array 后傳入 followMouse()
诅挑,并用 forEach 把每個(gè) omg 取出并利用 index 來達(dá)到不同的 delay 時(shí)間四敞,這個(gè) delay 時(shí)間的邏輯大家可以自己想,不用跟我一樣拔妥,最后 subscribe 就完成啦忿危!
最后完整的范例在這裡
今日小結(jié)
今天我們介紹了兩個(gè) operators 并帶了一個(gè)小范例,這兩個(gè) operators 在 UI 操作上都非常的實(shí)用没龙,我們明天會接著講幾個(gè) operators 可以用來做高頻率觸發(fā)的事件優(yōu)化铺厨!