0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Angularでカスタムディレクティブを作ってみた(ドラッグスクロール)

Posted at

業務でドラッグスクロールしたいという要望がありライブラリを探してみたんですが、色々あって独自で作ることになりました。

せっかくなので、いろんな場面で使いまわせるようにディレクティブで作ってみたので、その際のメモを残しておきます。

ディレクティブとは

ディレクティブには2種類あります

今回は独自の属性ディレクティブを作って行きます

カスタム属性ディレクティブを作る

ディレクティブクラスの作成

  • @Directiveアノテーションを付与したディレクティブクラスを作成
  • selectorに指定した名称がhtmlで指定する属性になります
    • []で囲うのを忘れないように!
drag-scroll.directive.ts
import { Directive, HostListener, ElementRef } from '@angular/core';

@Directive({
    selector: '[dragScroll]'
})
export class DragScrollDirective {
    private position: { top: number, left: number, x: number, y: number };

    constructor(private el: ElementRef) { }

    get element() { return this.el.nativeElement as HTMLElement; }

    @HostListener('mousedown', ['$event']) onMouseDown(event: MouseEvent) {
        if (!this.element) { return; }

        // クリック時の位置を保持
        this.position = {
            top: this.element.scrollTop,
            left: this.element.scrollLeft,
            x: event.clientX,
            y: event.clientY,
        };

        return false;
    }

    @HostListener('mousemove', ['$event']) onMouseMove(event: MouseEvent) {
        if (this.position) {
            if (event.clientX && event.clientY) {
                // マウスの移動距離を算出
                const moveX = event.clientX - this.position.x;
                const moveY = event.clientY - this.position.y;
                // 移動距離だけスクロール
                this.element.scrollLeft = this.position.left - moveX;
                this.element.scrollTop = this.position.top - moveY;

                return false;
            }
        }
        return true;
    }
    @HostListener('mouseup') onMouseUp() { this.position = null; }
    @HostListener('mouseleave') onMouseLeave() { this.position = null; }
}

中身の処理は以下をやってるだけです。

  • クリック時にクリック位置を保持
    • 通常のmousedownイベントを伝搬しないようにfalseを返してます
      • これをしないとマウスを動かしたときに範囲選択されてしまって挙動がおかしくなることがあったので
  • クリックしたままマウスを動かしたら、動かした分だけスクロール
    • これもスクロールする場合はクリック時と同様にfalseを返してイベント伝搬しないようにしています
  • クリック終了時およびカーソルがDOMの外に出た場合にクリック位置を破棄

モジュール化

  • いろんな画面で使いまわせるようにモジュール化します
    • ※別にモジュール化しなくても使えます
      • 宣言は1か所でしかできないため、複数画面で使いたい場合はモジュール化が必須です。
drag-scroll.module.ts
import { NgModule } from '@angular/core';
import { DragScrollDirective } from './drag-scroll.directive';

@NgModule({
    declarations: [DragScrollDirective],
    exports: [DragScrollDirective],
})
export class DragScrollModule { }

カスタムディレクティブのインポート

  • 作成したモジュールを使いたい場所のモジュールでインポート
    • モジュール化していない場合はdeclarationsで直接宣言
app.module.ts
・・・
import { DragScrollModule } from './shared/directives/drag-scroll/drag-scroll.module';

@NgModule({
  ・・・
  imports: [
    ・・・
    DragScrollModule,
  ],
  ・・・
})
export class AppModule { }

HTMLへの組み込み

  • htmlで対象のタグにカスタムディレクティブを指定
app.component.html
<div dragScroll style="max-width: 300px; max-height: 300px; overflow: auto;">
    <div style="width: 500px; height: 500px;">
        test
    </div>
</div>

ついでにドラッグスクロール要素内にドラッグ可能な要素を置いてみる

以下のようにdraggable="true"の要素を置くだけだと、ドラッグスクロールが勝ってしまいます

app.component.html
<div dragScroll style="max-width: 300px; max-height: 300px; overflow: auto;">
    <div style="width: 500px; height: 500px;">
        test
        <div draggable="true" style="width: 100px; height: 100px; background-color: red">
            draggable
        </div>
    </div>
</div>

そのため、draggable要素のmousedownで一工夫してあげましょう

app.component.html
<div dragScroll style="max-width: 300px; max-height: 300px; overflow: auto;">
    <div style="width: 500px; height: 500px;">
        test
        <div draggable="true" (mousedown)="onMouseDown($event)" style="width: 100px; height: 100px; background-color: red">
            draggable
        </div>
    </div>
</div>
app.component.ts
    onMouseDown(event: MouseEvent) {
        // 親要素へのイベント伝搬を止める
        event.stopImmediatePropagation();
    }

このようにdraggable要素のmousedownにてstopImmediatePropagationを呼ぶことで親要素へイベントが伝搬されないため、dragScrollのmousedownはハンドリングされなくなり、ちゃんとドラッグ出来るようになります。

最後に

割と簡単にカスタムディレクティブが作れました!
なお、今回作ったものはGitHubにアップしてます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?