用需求理解rxjs之操作符

需求一:获取鼠标点击或手指触摸的位置数据

分析:无序合并

import { Directive, ElementRef, Output, EventEmitter } from "@angular/core";
import { Observable } from "rxjs/Observable";
import { fromEvent } from "rxjs/observable/fromEvent";
import { merge } from "rxjs/observable/merge";
// 需求一:获取鼠标点击或手指触摸的位置数据

@Directive({
  selector: "[getTouchendOrMouseupPosition]"
})
export class GetTouchendOrMouseupPositionDirective {
  @Output()
  getTouchendOrMouseupPosition: EventEmitter<{
    x: number;
    y: number;
  }> = new EventEmitter();
  constructor(public ele: ElementRef) {}

  ngOnInit() {
    this.getEndPosition().subscribe(res => {
      this.getTouchendOrMouseupPosition.emit(res);
    });
  }
  getEndPosition(): Observable<{ x: number; y: number }> {
    let ele = this.ele.nativeElement;
    let mouseup = fromEvent(ele, "mouseup").map((ev: any) => ({
      x: ev.clientX,
      y: ev.clientY
    }));
    let touchend = fromEvent(ele, "touchend").map((ev: any) => ({
      x: ev.changedTouches[0].clientX,
      y: ev.changedTouches[0].clientY
    }));
    let mouseupAndTouchEnd = merge(mouseup, touchend);
    return mouseupAndTouchEnd;
  }
}
  • 使用
getTouchendOrMouseupPosition(e: {x:number,y: number}){
    console.log('最后的位置为:',e);
}

需求二: 逐字打印

分析:递归处理

import {
  Directive,
  Input,
  ElementRef,
  EventEmitter,
  Output
} from "@angular/core";
import { from } from "rxjs/observable/from";
import { interval } from "rxjs/observable/interval";
import "rxjs/add/operator/scan";
import "rxjs/add/operator/zip";

@Directive({
  selector: "[writeWordByString]"
})
export class WriteWordByStringDirective {
  // 书写的字符
  @Input() writeWordByString: string;
  // 书写速度
  @Input() writeSpeed: number = 200;
  @Input() writeLoop: boolean = false;
  // 返回处理结果
  @Output() onWrite: EventEmitter = new EventEmitter();
  constructor(public ele: ElementRef) {}
  ngOnInit() {
    this.getByWord();
  }
  getByWord() {
    let word = from(this.writeWordByString)
      .zip(interval(this.writeSpeed), (x, y) => x)
      .scan((origin, next) => origin + next);
    word.subscribe(
      res => {
        this.onWrite.emit(res);
      },
      () => {},
      () => {
        this.getByWord();
      }
    );
  }
}
  • 使用
{{str}}

需求三:关键字搜索

分析:去除无用http请求

import {
  Directive,
  Input,
  ElementRef,
  EventEmitter,
  Output
} from "@angular/core";
import { fromEvent } from "rxjs/observable/fromEvent";
import "rxjs/add/operator/debounceTime";
import "rxjs/add/operator/pluck";
import "rxjs/add/operator/filter";

import { HttpClient } from "@angular/common/http";

@Directive({
  selector: "[searchByKeyword]"
})
export class SearchByKeywordDirective {
  // 请求url
  @Input() searchByKeyword: string;
  @Input() searchKey: string = "key";
  @Output() onSearch: EventEmitter = new EventEmitter();
  constructor(public ele: ElementRef, public http: HttpClient) {}
  ngOnInit() {
    if (this.searchByKeyword.indexOf("?") > -1) {
      // 如果有?不处理
    } else {
      this.searchByKeyword += `?${this.searchKey}=`;
    }
    this.doOnSearch();
  }
  doOnSearch() {
    fromEvent(this.ele.nativeElement, "keyup")
      .debounceTime(300)
      .pluck("target", "value")
      // 去掉无效关键字
      .filter((text) => !!text)
      .switchMap(key => this.http.get(this.searchByKeyword + key))
      .subscribe(res => {
        this.onSearch.emit(res);
      });
  }
}
  • 使用

需求创建一个可以随意拖拽的元素

import {
  Directive,
  ElementRef,
  Renderer2,
  Input,
  HostBinding
} from "@angular/core";
import { fromEvent } from "rxjs/observable/fromEvent";
import { merge } from "rxjs/observable/merge";

import "rxjs/add/operator/switchMap";
import "rxjs/add/operator/map";
import "rxjs/add/operator/takeUntil";
import "rxjs/add/operator/throttle";
import "rxjs/add/operator/pairwise";
import "rxjs/add/operator/do";

@Directive({
  selector: "[dragToMove]"
})
export class DragToMoveDirective {
  @HostBinding("style.position") _position: string;
  @HostBinding("style.left.px") _left: number;
  @HostBinding("style.top.px") _top: number;

  @Input("dragToMove")
  set position(val) {
    if (val) {
      this._position = val;
    }
  }

  get position() {
    return this._position || "absolute";
  }
  constructor(public ele: ElementRef, public render: Renderer2) {}

  ngOnInit() {
    this.drag()
      .do((pos: any) => {
        this._position = this.position;
        this._left = pos.left;
        this._top = pos.top;
      })
      .subscribe(pos => {});
  }

  mousedrag() {
    return this.mousedown().switchMap((md: MouseEvent) => {
      const startX = md.offsetX;
      const startY = md.offsetY;
      return this.mousemove()
        .map((mm: MouseEvent) => {
          mm.preventDefault();
          return {
            left: mm.clientX - startX,
            top: mm.clientY - startY
          };
        })
        .takeUntil(this.mouseup());
    });
  }

  touchdrag() {
    return this.touchstart().switchMap((ts: TouchEvent) => {
      const rect = this.getBoundingClientRect();
      const startX = ts.targetTouches[0].clientX - rect.left;
      const startY = ts.targetTouches[0].clientY - rect.top;
      return this.touchmove()
        .map((tm: TouchEvent) => {
          tm.preventDefault();
          return {
            left: tm.targetTouches[0].clientX - startX,
            top: tm.targetTouches[0].clientY - startY
          };
        })
        .takeUntil(this.touchend());
    });
  }

  mousemove() {
    return fromEvent(document, "mousemove");
  }

  touchmove() {
    return fromEvent(document, "touchmove");
  }

  mousedown() {
    return fromEvent(this.ele.nativeElement, "mousedown");
  }

  touchstart() {
    return fromEvent(this.ele.nativeElement, "touchstart");
  }

  mouseup() {
    return fromEvent(document, "mouseup");
  }

  touchend() {
    return fromEvent(document, "touchend");
  }

  drag() {
    return merge(this.mousedrag(), this.touchdrag());
  }

  getBoundingClientRect() {
    return this.ele.nativeElement.getBoundingClientRect();
  }
}
  • 使用
我是可以拖拽的

需求四 onDestory时停止监听

在angular中,使用Observable时,当组件注销的时候,是要进行一些清理操作的。
普通的做法是把所有的观察着保存在一个数组或者map之类的变量里,onDestory时,遍历unsubscribe
这种方式非常不实用,不雅观。下面我们看一个正确的姿势。

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, merge, timer, interval } from 'rxjs';
import { takeUntil, filter, map } from 'rxjs/operators';
@Component({
  selector: 'ramda',
  templateUrl: './ramda.component.html',
  styleUrls: ['./ramda.component.scss']
})
export class RamdaComponent implements OnInit, OnDestroy {
  // 某订阅事件1
  sub: Subject = new Subject();
  // 某订阅事件2
  sub2: Subject = new Subject();
  // 某订阅事件3
  sub3: Subject = new Subject();
  // 某订阅事件4
  sub4: Subject = new Subject();
  // 需要取消订阅开关
  needDestory: boolean = false;
  constructor() {}

  ngOnDestroy() {
    this.needDestory = true;
  }

  ngOnInit() {
    this.needDestory = false;
    let takeUtil$ = merge(this.sub, this.sub2, this.sub3, this.sub4).pipe(
      filter(val => this.needDestory)
    );
    let sub = this.sub.pipe(takeUntil(takeUtil$)).subscribe(res => {
      // 得到想要的结果 0,1,2,3,4,5s后模拟注销组件,2s后模拟重新回来,5,6,7,8
      this.log(res);
    });
    let sub2 = this.sub2.pipe(takeUntil(takeUtil$)).subscribe(res => {
    // 得到想要的结果 100,101,102,103,104,5s后模拟注销组件,2s后模拟重新回来,105,106,107,108
      this.log(res);
    });
    // 模拟事件发布
    interval(1000)
      .pipe(takeUntil(takeUtil$))
      .subscribe(res => {
        this.sub.next(res);
        this.sub2.next(100 + res);
      });

    timer(5000).subscribe(res => {
      this.log('5s后模拟注销组件');
      this.ngOnDestroy();
      timer(200).subscribe(res => {
        this.log('2s后模拟重新回来');
        this.needDestory = false;
      });
    });
  }

  log(msg) {
    console.log(msg);
  }
}

小结

过滤

  • take 截取前N个
  • takeUntil 截取流直到流发出
  • takeWhile 截取流直到false
  • skip 跳过前N个
  • skipUntil 跳过直到流发出
  • skipWhild 跳过直到false
  • single 取一个
  • sample 取样
  • last 最后一个
  • first 取第一个或第一个满足条件的值
  • filter 过滤符合条件
  • debounce 动态时间段取一个
  • debounceTime 一定时间段取一个
  • ignoreElements 忽略其他只保留complement和error
  • throttle 节流
  • throttleTime 经过多少时间后取最新值

转换

  • pluck 属性选择
  • buffer 指定流发出是发出
  • bufferCount 达到制定长度后发出
  • bufferTime 达到制定时间后发出
  • bufferToggle
  • bufferWhen
  • groupBy 分组
  • partition 分割
  • scan 时间推移

你有什么需求,留言一起解决!

你可能感兴趣的:(用需求理解rxjs之操作符)