typescript RxJS组合pointerdown和pointermove事件

amrnrhlw  于 2023-11-20  发布在  TypeScript
关注(0)|答案(1)|浏览(99)

我在SVG元素上实现了SVG图像元素的拖放。这个想法是当pointerdown事件被触发时,pointermove事件被触发,所选的SVG图像元素被拖到指针位置,直到pointerup事件被触发。我使用RxJS fromEvent creational observable。

import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
    import { fromEvent, Observable, Subscription, Subject } from 'rxjs'
    import { RouterComponent, Router } from '../router/router.component';
    import { ToolbarComponent } from '../toolbar/toolbar.component';
    import { LinkComponent } from '../link/link.component';
    import * as d3 from 'd3';
    import { of } from 'rxjs';

    @Component({
      selector: 'app-test',
      templateUrl: './test.component.html',
      styleUrls: ['./test.component.css']
    })
    export class TestComponent implements AfterViewInit {

      @ViewChild("svg") svgElement: ElementRef<SVGSVGElement>;

      svg: SVGSVGElement;

      moveObjectPointerDown: Subscription;
      moveObjectPointerMove: Subscription;
      moveObjectPointerUp: Subscription;

      linkPointerDown: Subscription;
      linkPointerMove: Subscription;
      linkPointerUp: Subscription;

      @ViewChild(ToolbarComponent) toolbarComponent: ToolbarComponent;
      @ViewChild(RouterComponent) routerComponent: RouterComponent;
      @ViewChild(LinkComponent) linkComponent: LinkComponent;

      constructor() {
      }

      ngAfterViewInit() {
        this.svg = this.svgElement.nativeElement;

        this.subscribeMoveMode();

        this.toolbarComponent.createRouter$.subscribe(() => {
          var router = this.routerComponent.create();
          this.routerComponent.add(router);
        });

        this.toolbarComponent.createHost$.subscribe(() => {
        });
      }

      unsubscribeMoveMode(): void {
        this.moveObjectPointerDown.unsubscribe();
        this.moveObjectPointerMove.unsubscribe();
        this.moveObjectPointerUp.unsubscribe();
      }

      unsubscribeLinkMode(): void {
        this.linkPointerDown.unsubscribe();
        this.linkPointerMove.unsubscribe();
        this.linkPointerUp.unsubscribe();
      }

      subscribeMoveMode(): void {

        var selectedElement: any;
        var offsetX: number;
        var offsetY: number;

        this.moveObjectPointerDown = fromEvent(this.svg, "pointerdown").subscribe((event: any) => {
          if (event.target.classList.contains("router")) {
            selectedElement = event.target;
            let targetPositionX = selectedElement.getAttributeNS(null, 'x');
            let targetPositionY = selectedElement.getAttributeNS(null, 'y');
            let mousePositionX = event.clientX;
            let mousePositionY = event.clientY;
            let ctm = this.svg.getScreenCTM();
            mousePositionX -= ctm!.e;
            mousePositionY -= ctm!.f;
            offsetX = mousePositionX - targetPositionX;
            offsetY = mousePositionY - targetPositionY;
          }
        });

        this.moveObjectPointerMove = fromEvent(this.svg, "pointermove").subscribe((event: any) => {
          if (selectedElement) {
            let mousePositionX = event.clientX;
            let mousePositionY = event.clientY;
            let ctm = this.svg.getScreenCTM();
            mousePositionX -= ctm!.e;
            mousePositionY -= ctm!.f;
            mousePositionX -= offsetX;
            mousePositionY -= offsetY;
            selectedElement.setAttributeNS(null, 'x', mousePositionX);
            selectedElement.setAttributeNS(null, 'y', mousePositionY);

            // update position of router
            this.routerComponent.update(selectedElement.id, mousePositionX, mousePositionY);
            
            //this.routerObserver.update(selectedElement.id, selectedElement.x.baseVal.value, selectedElement.y.baseVal.value);
            for (let linkID of this.routerComponent.getById(selectedElement.id)!.links) {
              this.linkComponent.update(linkID, mousePositionX + 25 - 1, mousePositionY + 25 - 1);
            }
          }
          event.preventDefault();
        });

        this.moveObjectPointerUp = fromEvent(this.svg, "pointerup").subscribe((event: any) => {
          selectedElement = null;
        });
      }

      subscribeLinkMode(): void {
        
        var selectedLineElement: any;
        var selectedRouterElement1: any;
        var selectedRouterElement2: any;
        
        this.linkPointerDown = fromEvent(this.svg, "pointerdown").subscribe((event: any) => {     
          selectedRouterElement1 = event.target;
          if (selectedRouterElement1.classList.contains("router")) {
            var router: Router | undefined = this.routerComponent.getById(selectedRouterElement1.id);
            if (router != undefined) {
              selectedLineElement = this.linkComponent.create(router.x + 25, router.y + 25, router.x + 25, router.y + 25);
            }
          }
        });

        this.linkPointerMove = fromEvent(this.svg, "pointermove").subscribe((event: any) => {

          if (selectedLineElement) {
            let ctm = this.svg.getScreenCTM();
            var mousePositionX = event.clientX;
            var mousePositionY = event.clientY;
            mousePositionX -= ctm!.e;
            mousePositionY -= ctm!.f;
            selectedLineElement = this.linkComponent.update(selectedLineElement.id, mousePositionX - 1, mousePositionY - 1);
          }
          event.preventDefault();
        });

        this.linkPointerUp = fromEvent(this.svg, "pointerup").subscribe((event: any) => {
          if (event.target.classList.contains("router")) {
            selectedRouterElement2 = event.target;
            var r1 = this.routerComponent.getById(selectedRouterElement1.id)!;
            var r2 = this.routerComponent.getById(selectedRouterElement2.id)!;
            if (r1.connectedTo.indexOf(r2) == -1 && r2.connectedTo.indexOf(r1) == -1) {
              var newLink = this.linkComponent.create(r1.x + 25, r1.y + 25, r2.x + 25, r2.y + 25);
              this.routerComponent.addLink(r1.id, newLink.id, r2);
              this.routerComponent.addLink(r2.id, newLink.id, r1);
            }
          }

          if (selectedLineElement) {
            this.linkComponent.remove(selectedLineElement.id);
            selectedRouterElement1 = null;
            selectedRouterElement2 = null;
            selectedLineElement = null;
          }
        });
      }
    }

字符串
我的问题是,有没有更聪明的方法来使用RxJS组合运算符来做到这一点?而不是有三个独立的可观测值与本地共享变量(selectedElement,offsetX,offsetY)。我可以使用combineLatest,或withLatestFrom,或其他吗?

ou6hu8tu

ou6hu8tu1#

不要重新发明轮子。换句话说,当尝试实现一个新功能时,寻找一个现有的实现。另外,确保你信任源代码作者,以避免恶意软件。这将为你保存大量的时间。
对于这种拖放特性,您可以利用Angular Material UI component library
请按照以下步骤操作:

  • 安装所需的依赖项。为此,您可以安装Component Dev Kit (CDK)或Angular Material库(包括CDK)
  • 仅CDK(更简单、更快):npm i @angular/cdk
  • 角材质:ng add @angular/material
  • 导入所需模块:
import { DragDropModule } from '@angular/cdk/drag-drop';
// Other imports...

@NgModule({
  imports: [CommonModule, DragDropModule, ...],
  // ...
})

字符串
对于独立的应用程序(Angular v17之后的默认设置),没有NgModule s。所以,你可以使用这个:

import { DragDropModule } from '@angular/cdk/drag-drop';
// Other imports...

@Component({
  standalone: true,
  imports: [CommonModule, DragDropModule, ...],
  // ...
})

  • 最后一步:将cdkDrag指令添加到元素(本例中为SVG image):
<svg width="200" height="150">
  <rect width="100%" height="100%" fill="#e0e0e0" />
  <image href="favicon.ico" width="100" height="75" cdkDrag />
</svg>


使用DragDropModule可以做很多事情。访问Drag and Drop页面了解更多信息。

相关问题