页面 css:
html, body { height: 100%; background-color: tomato; position: relative; } #drag { position: absolute; width: 100px; height: 100px; background-color: #fff; cursor: all-scroll; }要实现的功能如下:
当在这个 div 上按下鼠标左键(mousedown)时,开始监听鼠标移动(mousemove)位置
当鼠标松开(mouseup)时,结束监听鼠标移动
当鼠标移动被监听时,更新 div 样式来实现拖拽效果
实现思路:
我们可以使用 fromEvent 去转化 DOM 事件
const mouseDown$ = fromEvent(eleDrag, 'mousedown') const mouseMove$ = fromEvent(eleBody, 'mousemove') const mouseUp$ = fromEvent(eleBody, 'mouseup')
对于鼠标按下这个数据流,每次鼠标按下事件发生时都转成鼠标移动的数据流
mouseDown$.pipe( map(mouseDownEvent => mouseMove$) )
鼠标松开时,结束监听鼠标移动,我们可以用 takeUntil 表示这个逻辑
mouseDown$.pipe( map(mouseDownEvent => mouseMove$.pipe( takeUntil(mouseUp$) )) )
上面的 map 操作符内将每次 mousedown 映射为一个 Observable,形成了高阶 Observable,我们需要用 concatlAll 压平,map 和 concatAll 连用,可以用更简洁的 concatMap
mouseDown$.pipe( concatMap(mouseDownEvent => mouseMove$.pipe( takeUntil(mouseUp$) )) )
订阅这个 mousemove 数据流更新 div 位置。我们可以获取 mousemove event 中的 clientX 和 clientY,减去初始鼠标按下时鼠标相对 div 元素的值来得到最终 div 的绝对位置的 left 和 top。也可以使用 withLatestFrom 操作符,见 demo。
mouseDown$.pipe( concatMap(mouseDownEvent => mouseMove$.pipe( map(mouseMoveEvent => ({ left: mouseMoveEvent.clientX - mouseDownEvent.offsetX, top: mouseMoveEvent.clientY - mouseDownEvent.offsetY })), takeUntil(mouseUp$) )) ).subscribe(position => { eleDrag.style.left = position.left + 'px' eleDrag.style.top = position.top + 'px' })这里是一个更复杂一些的例子,当页面滑动到视频出页面时视频 fixed 定位,这是可以拖拽移动视频位置。通过 getValidValue 对视频拖拽的位置进行了一个限制。
缓存把上游的多个数据缓存起来,当时机合适时再把汇聚的数据传给下游。
1)buffer、bufferTime、bufferCount、bufferWhen、bufferToggle
对于 buffer 这一组操作符,数据汇聚的形式就是数组。
buffer 接收一个 Observable 作为 notifier,当 notifier 发出数据时,将 缓存的数据传给下游。
interval(300).pipe( take(30), buffer(interval(1000)) ).subscribe( x => console.log(x) ) // [0, 1, 2] // [3, 4, 5] // [6, 7, 8] // [9, 10, 11, 12]bufferTime 是用时间来控制时机,上面可以改成 bufferTime(1000)
bufferCount 是用数量来控制时机,如 3 个一组,bufferCount(3)
bufferWhen 接收一个叫做 closeSelector 的参数,它应该返回一个 Observable。通过这个 Observable 来控制缓存。这个函数没有参数。下面的方法等价于前面的 buffer:
interval(300).pipe( take(30), bufferWhen(() => { return interval(1000) }) ).subscribe( x => console.log(x) )bufferToggle 和 buffer 的不同是可以不断地控制缓存窗口的开和关,一个参数是一个 Observable,称为 opening,第二个参数是称为 closeSelector 的一个函数。这个函数的参数是 opening 产生的数据。前一个参数用来控制缓存的开始时间,后一个控制缓存的结束。与 bufferWhen 相比,它的 closeSelector 可以接收参数,控制性更强。
我们可以使用 buffer 来做事件的过滤,下面的代码只有 500ms 内连续点击两次以上才会输出 ‘success’ 。
fromEvent(document.querySelector('#btn'), 'click').pipe( bufferTime(500), filter(arr => arr.length >= 2) ).subscribe( x => console.log('success') )2)window、windowTime、windowCount、windowWhen、windowToggle
与前面的 buffer 类似,不过 window 缓存数据汇聚的形式是 Observable,因此形成了高阶 Observable。
debounceTime、throttleTime类似 lodash 的 debounce 和 throttle,用来降低事件的触发频率。
我们做搜索时,常常要对输入进行 debounce 来减少请求频率。
fromEvent(document.querySelector('#searchInput'), 'input').pipe( debounceTime(300), map(e => e.target.value) ).subscribe( input => document.querySelector('#text').textContent = input // 发送请求 ) distinct、distinctUntilChanged