前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >rxjs实现元素拖拽

rxjs实现元素拖拽

作者头像
flytam
发布2020-01-14 17:53:42
1.6K0
发布2020-01-14 17:53:42
举报

最近看了一点rxjs的东西。现学现玩一下…就来尝试下元素拖拽吧

如果使用非rxjs而是普通的js实现思路也不难。

一般实现拖拽的思路是:

1、监听 drag 元素 的 mousedown,回调中设置标识开始拖动,计算出初始点击到元素左上角距离

2、监听 document 的 mousemove,判断 1 中标识处于拖动,通过计算当前位置设置元素的样式

3、监听 document 的 mouseup,设置标识停止拖动

代码语言:javascript
复制
// 不使用rxjs实现。
const drag = document.getElementById("drag");

let isDrag = false;
let initialX = null,
    initialY = null;
drag.addEventListener("mousedown", function(e) {
    isDrag = true;
    const { left, top } = drag.getBoundingClientRect();
    initialX = e.clientX - left;
    initialY = e.clientY - top;
});

document.addEventListener("mouseup", function(e) {
    isDrag = false;
});

document.addEventListener("mousemove", function(e) {
    if (isDrag) {
        drag.style.left = `${e.clientX - initialX}px`;
        drag.style.top = `${e.clientY - initialY}px`;
    }
});
接下来就是使用 rxjs 来实现了

rxjs 中一切皆为流,那么肯定有一个 Observable 源。在拖拽操作中,我们的源肯定就是鼠标的事件了,所以我们这边建立 3 个源,分别是鼠标移动、鼠标点击、鼠标 mouseup

代码语言:javascript
复制
const target = document.getElementById("drag");

const mouseDown = fromEvent(target, "mousedown");
const mouseMove = fromEvent(document, "mousemove");
const mouseUp = fromEvent(document, "mouseup");

接下来,一次拖拽操作的开始肯定是鼠标点击元素准备拖拽了。所以,入口Observable就是 mouseDown 这个 Observable 了。

代码语言:javascript
复制
mouseDown.pipe(...)

通过普通的 js 写拖拽我们知道我们开始肯定是需要获取鼠标点击区域到元素左上角的偏移距离,用于后面拖拽后设置元素的正确位置。这里用到了map操作符。和 js 的数组 map 有点像,输入一系列的值然后处理返回一系列新的值,这个过程都是 immutable 的哦。接下来我们就去计算这个偏移值

代码语言:javascript
复制
mouseDown
    .pipe(
        map(e => {
            const { left, top } = e.target.getBoundingClientRect();
            const clickOffsetX = e.clientX - left;
            const clickOffsetY = e.clientY - top;
            return {
                clickOffsetX,
                clickOffsetY
            };
        }))
        ......

接下来,就是在mousemove事件中去计算元素的位置并设置样式改变元素位置了。上面的pipe运算符就是将前一个操作符的输出作为下一个操作符的输入。

我们知道拖拽的结束就是mouseup触发的时候,这时候需要takeUntil这个操作符。它的含义就是:解释源 Observable 会不停发射数据直到目标 Observable 发射数据。然后又是使用map去根据原来计算出来的偏移值和当前鼠标移动的值去计算元素的位置了

代码语言:javascript
复制
...
        map(({ clickOffsetX, clickOffsetY }) => {
            return mouseMove.pipe(
                takeUntil(mouseUp),
                map(moveEvent => ({
                    x: moveEvent.clientX - clickOffsetX,
                    y: moveEvent.clientY - clickOffsetY
                }))
            );
        })
...

上面我们map又接了一个map,类似于一个二维的Observable,如[[Observable]]。我们再借助concatAll打平成一维即可。整个Observable的处理过程就完成了,最后订阅Observable再设置元素的位置即可。完整代码如下。

代码语言:javascript
复制
// 使用rxjs实现
const {
    fromEvent,
    operators: { map, takeUntil, concatAll, withLatestFrom }
} = rxjs;

const target = document.getElementById("drag");

const mouseDown = fromEvent(target, "mousedown");
const mouseMove = fromEvent(document, "mousemove");
const mouseUp = fromEvent(document, "mouseup");

mouseDown
    .pipe(
        map(e => {
            const { left, top } = e.target.getBoundingClientRect();
            const clickOffsetX = e.clientX - left;
            const clickOffsetY = e.clientY - top;
            return {
                clickOffsetX,
                clickOffsetY
            };
        }),
        map(({ clickOffsetX, clickOffsetY }) => {
            return mouseMove.pipe(
                takeUntil(mouseUp),
                map(moveEvent => ({
                    x: moveEvent.clientX - clickOffsetX,
                    y: moveEvent.clientY - clickOffsetY
                }))
            );
        }),
        concatAll()
    )
    .subscribe(({ x, y }) => {
        target.style.left = `${x}px`;
        target.style.top = `${y}px`;
    });

demo

后话:使用rxjs对于处理复杂的异步操作还是很好的,也包含了很多函数式编程的思想。不过要学习rxjs那种思想感觉还是要有一定的适应过程…慢慢来吧

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 接下来就是使用 rxjs 来实现了
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档