RxJS: Drag and Drop

in #javascript6 years ago

Recently, I had been using a lot of RxJS, and I find it really useful not just to do asynchronous programming, but also for building User Interface!


DnD.gif
Drag and Drop with RxJS

I created a drag and drop that works on both mouse click (Desktop) and touch (Phone) with less than 60 lines of codes and RxJS is the only dependency! Code hosted at CodePen

Here's the code breakdown

First, get the RxJS 6 CDN from https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.2/rxjs.umd.min.js and import necessary functions.

const { fromEvent, interval } = rxjs;
const { takeUntil, mergeMap, flatMap, map, merge } = rxjs.operators;

Create a reference of the drag and drop target.

// dom element
const target = document.querySelector(".box");

Then, bind both mouse and touch event together (since both used very similar DOM API).

// event
const mousemove = fromEvent(document, "mousemove").pipe(
  merge(fromEvent(document, "touchmove"))
);
const mouseup = fromEvent(target, "mouseup").pipe(
  merge(fromEvent(target, "touchend"))
);
const mousedown = fromEvent(target, "mousedown").pipe(
  merge(fromEvent(target, "touchstart"))
);

Then, Create an observable called drag, which is a combination of mousedown/touchdown, mousemove/touchmove, mouseup/touchup. The data grab will take until mouseup/touchup operation is done after mousedown/touchdown and mousemove/touchmove. if (md.type.startsWith("mouse")) is used to check whether the operation is mouse or touch since both have different structure of api on the x and y location.

// create drag observerble
const drag = mousedown.pipe(
  flatMap(md => {
    let startX, startY, startLeft, startTop;

    if (md.type.startsWith("mouse")) {
      startX = md.clientX + window.scrollX;
      startY = md.clientY + window.scrollY;
      startLeft = parseInt(md.target.style.left, 10) || 0;
      startTop = parseInt(md.target.style.top, 10) || 0;
    } else {
      startX = md.touches[0].clientX + window.scrollX;
      startY = md.touches[0].clientY + window.scrollY;
      startLeft = parseInt(md.target.style.left, 10) || 0;
      startTop = parseInt(md.target.style.top, 10) || 0;
    }

    return mousemove.pipe(
      map(mm => {
        if (mm.type.startsWith("mouse")) {
          return {
            left: startLeft + mm.x - startX,
            top: startTop + mm.y - startY
          };
        } else {
          return {
            left: startLeft + mm.touches[0].clientX - startX,
            top: startTop + mm.touches[0].clientY - startY
          };
        }
      }),
      takeUntil(mouseup)
    );
  })
);

Once the observable is created, we need to subscribe to it, so that it can update the location of the image.

// subscription
const subscription = drag.subscribe(pos => {
  target.style.top = pos.top + "px";
  target.style.left = pos.left + "px";
});

Then it works like a charm!

Try it yourself!

Sort:  

你好吗?快来使用超级好用的steemit客户端---Partiko,这个可是我们华人团队开发的哦。假如我的留言打扰到你,请回复“取消”。

You’ve been upvoted by TeamMalaysia Community :-

To support the growth of TeamMalaysia Follow our upvotes by using steemauto.com and follow trail of @myach

Vote TeamMalaysia witness bitrocker2020 using this link vote for witness

Maybe you want to combine or just check it with interactjs.
I've used it to build a vue component using that (and seems my component project got stalled :D)

Just want to show off some RxJS stuff, it makes life easier by not using any library. And soon, RxJS concept of Observable will be added in JavaScript standard!

That's new to me and I see it moved on to stage-1. If it passes all stage smoothly then it probably will be implemented in 2020 🤔

you can always use babel for the implementation even though it is at stage-1 haha

Coin Marketplace

STEEM 0.30
TRX 0.12
JST 0.034
BTC 64231.88
ETH 3128.59
USDT 1.00
SBD 3.95