一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
前言
上一章中,我们补充了 rxjs 的节流防抖方法,这两个方法虽然实现的方式不少,但是本质上的减少一些高频数据流的数据流出。
这次我们就来总结一下之前的所有操作符和辅助方法,来实现一个 元素拖动 的简单案例。
js 版本
首先我们先用熟悉的 js 来实现一个简单的版本,其中就需要用到三个时间,分别是 onmousedown,onmousemove,onmouseup,他们分别代表着鼠标按下,移动和抬起,我们要在 鼠标按下 的时候去获取到当前盒子在屏幕中的位置,这个可以通过 event 里面的属性获取,然后在 鼠标移动 的时候,需要去实时改变盒子当前的定位,通过实时的一个计算以及修改盒子的定位就可以实现,最后在 鼠标抬起 的时候,我们需要去释放 onmousemove 事件。
<body>
<style>
div {
background-color: black;
width: 100px;
height: 100px;
position: absolute;
top: 0px;
left: 0px;
}
</style>
<button id="button">我是按钮</button>
<div id="drag"></div>
</body>
const drag = document.getElementById("drag");
drag.onmousedown = function (event) {
let distanceX = event.clientX - event.target.offsetLeft;
let distanceY = event.clientY - event.target.offsetTop;
document.onmousemove = function (event) {
let positionX = event.clientX - distanceX;
let positionY = event.clientY - distanceY;
console.log(positionX, positionY);
drag.style.left = positionX + "px";
drag.style.top = positionY + "px";
};
drag.onmouseup = function () {
document.onmousemove = null;
};
};
这就是最简单的一个元素拖拽的 js 版本的示例,那么接下来,我们要想如何用已经学过的 rxjs 操作符或者辅助方法配合来实现这个效果。
rxjs 版本
-
在 rxjs 中,我们可以通过 fromEvent 辅助方法来获取事件转为可观察对象,比如最开始的鼠标点击事件,我们可以将它转为可观察对象。
-
在点击事件中,我们需要获取到元素当前的一个坐标位置,并且将坐标位置提供给 鼠标移动方法使用,那么我们首先可以用 map 方法来获取到我们想要的数据,然后在通过 switchMap 操作符就可以切换可观察对象,我们是不是就可以从点击事件上拿到当前元素的位置,然后传递给 switchMap 作为参数提供给移动事件。
-
那么在 js 中,移动事件里面我们要去做什么呢,就是计算获取元素当前的位置并且动态的修改掉元素上的定位值,那么在 rxjs 当中,我们需要在操作符中通过计算获取到移动过后的位置,然后作为数据流将数据流出,那么在获得数据之后就可以进行对元素定位的修改。
-
最后就是要在鼠标抬起的时候,需要取消掉之前移动事件,在 rxjs 中就是停止数据的流出,那么就是需要用到 takeUntil 操作符。接收一个 可观察对象,就是鼠标抬起事件转为的可观察对象,然后就会停止上一个事件的数据流出。
那么综合上面所有的关键步骤,我们就可以用 rxjs 来实现一个元素拖拽的案例了
import { fromEvent } from "rxjs";
import { map, switchMap, takeUntil } from "rxjs/operators";
const drag = document.getElementById("drag");
fromEvent(drag, "mousedown")
.pipe(
map((event) => ({
distanceX: event.clientX - event.target.offsetLeft,
distanceY: event.clientY - event.target.offsetTop,
})),
switchMap(({ distanceX, distanceY }) =>
fromEvent(document, "mousemove").pipe(
map((event) => ({
positionX: event.clientX - distanceX,
positionY: event.clientY - distanceY,
})),
takeUntil(fromEvent(document, "mouseup"))
)
)
)
.subscribe(({ positionX, positionY }) => {
console.log(positionX, positionY);
drag.style.left = positionX + "px";
drag.style.top = positionY + "px";
});
节流
放到最后,从上面的控制台我们可以发现,在拖拽的过程当中,可观察对象会非常频繁的对外发出数据,并且在发出数据之后,是会动态去修改元素的定位的,这是属于在dom操作的范畴,那么我们知道,dom 操作是十分消耗浏览器性能的一个操作,是不能够频繁的去进行的,不然就会很容易造成一个浏览器的卡顿。
那么我们是不是就可以使用节流来对其进行一个限制,在流出数据的时候加上一个很短的时间,这样就可以减少浏览器的消耗,并且由于间隔的时间比较短,所以用户是感受不到添加了节流的,这就是对这个案例的一个性能上的优化。
总结
本文我们结合了多个操作符和辅助方法来实现了一个拖拽效果,在这之后,我们也会更多的去使用 rxjs 来实现一个日常开发中的事件,并且一起来体会 使用了 rxjs 和我们原生的操作,都会有着什么不同。