70行代码写一个带记忆功能的拖拽控件-另附精简手势控件

333 阅读1分钟

直接上代码,有注释,简单小功能,好理解!

import { Directive,ElementRef,HostBinding,OnInit,Input } from '@angular/core';
import { fromEvent, merge, animationFrameScheduler, generate } from 'rxjs';
import { map, tap, throttleTime, debounceTime } from 'rxjs/operators';
import { TouchService } from './touch.service';
@Directive({
  selector: '[dragmove]'
})
export class DragmoveDirective implements OnInit {
  @HostBinding('style.left.px') _left: number;
  @HostBinding('style.top.px') _top: number;
  @HostBinding('style.position') _position: string = 'absolute';
  // 如果设置了,那么记录位置,下次来的时候还原上一次操作位置
  _cacheKey: string;
  @Input()
  set dragmove(val: string) {
    if (val) {
      this._cacheKey = val;
      let p = localStorage.getItem(val);
      if (p) {
        let point = JSON.parse(p);
        this._left = point.left;
        this._top = point.top;
      }
    }
  }
  isStart: boolean = false;
  constructor(public ele: ElementRef) {}
  ngOnInit() {
    this.onDrag();
  }
  onDrag() {
    const mousemove = fromEvent(this.ele.nativeElement, 'mousemove');
    const touchmove = fromEvent(this.ele.nativeElement, 'touchmove');
    const nodeRect = this.ele.nativeElement.getBoundingClientRect();
    const rect = {
      height: nodeRect.height,
      width: nodeRect.width
    };
    const move = merge(
      mousemove.pipe(
        map((evt: MouseEvent) => {
          return {
            x: evt.clientX,
            y: evt.clientY
          };
        })
      ),
      touchmove.pipe(
        map((evt: TouchEvent) => {
          return {
            x: evt.touches[0].pageX,
            y: evt.touches[0].pageY
          };
        })
      ),
      animationFrameScheduler
    ).pipe(
      map(res => {
        this._top = res.y - rect.height / 2;
        this._left = res.x - rect.width / 2;
        return {
          left: this._left,
          top: this._top
        };
      }),
      debounceTime(300),
      tap(res => this._cacheKey && localStorage.setItem(this._cacheKey, JSON.stringify(res)))
    );
    move.subscribe((res: any) => {});
  }
}

使用

<!--不带记忆功能-->
<div class="cube" dragmove></div>
<!--带记忆功能-->
<div class="cube" dragmove="dragmove-1"></div>

  • 超级精简手势控件
import {
  Directive,
  OnInit,
  ElementRef,
  Input,
  Output,
  EventEmitter
} from '@angular/core';
import { fromEvent } from 'rxjs';
import { map, switchMap, takeUntil, debounceTime, tap } from 'rxjs/operators';
@Directive({
  selector: '[swipeMove]'
})
export class SwipeMoveDirective implements OnInit {
  // 移动像素临界值
  @Output() swipeMove: EventEmitter<any> = new EventEmitter();
  len: number = 10;
  constructor(public ele: ElementRef) {}
  ngOnInit() {
    this.handler(this.ele.nativeElement);
  }
  handler(ele: Element) {
    let start = fromEvent(ele, 'touchstart');
    let move = fromEvent(ele, 'touchmove');
    let end = fromEvent(ele, 'touchend');
    document.oncontextmenu = () => false;
    let t = (evt: TouchEvent) => ({
      x: evt.touches[0].pageX,
      y: evt.touches[0].pageY,
      t: new Date().getTime(),
      type: evt.type
    });
    // |start----------------------------
    // -----------|move------------------
    // --------------------------|end----
    //            |--------------|
    start
      .pipe(
        // 规整
        map(t),
        switchMap(poi1 =>
          move.pipe(
            // 规整
            map(t),
            // 直到end
            takeUntil(end),
            map(end => ({
              dx: end.x - poi1.x,
              dy: end.y - poi1.y,
              dt: end.t - poi1.t
            })),
            map(res => {
              // x 主导
              if (Math.abs(res.dx) > Math.abs(res.dy)) {
                if (res.dx > this.len) return 'right';
                else if (res.dx < -this.len) return 'left';
                else return 'tap';
              } else {
                if (res.dy > this.len) return 'down';
                else if (res.dy < -this.len) return 'up';
                else return 'tap';
              }
            }),
            tap(res=>this.swipeMove.next(res)),
            debounceTime(100)
          )
        )
      )
      .subscribe(res => {});
  }
}
<!--left right up down tap-->
<div class="cube" (swipeMove)="swipeMove($event)"></div>

小结

rxjs流式响应式编程的强大