1. 指令代码
首先展示核心指令代码。这段实现中,我们通过自定义指令 appDrag 让元素具备拖拽能力,逻辑清晰,并使用 @HostListener 绑定事件,代码简洁高效。
import { Directive, ElementRef, OnInit, HostListener } from '@angular/core';
@Directive({
selector: '[appDrag]'
})
export class DragDirective implements OnInit {
constructor(public el: ElementRef) {
}
public isDown = false;
public disX; // 记录鼠标点击事件的位置 X
public disY; // 记录鼠标点击事件的位置 Y
private totalOffsetX = 0; // 记录总偏移量 X轴
private totalOffsetY = 0; // 记录总偏移量 Y轴
// 点击事件
@HostListener('mousedown', ['$event']) onMousedown(event) {
this.isDown = true;
this.disX = event.clientX;
this.disY = event.clientY;
}
// 监听document移动事件事件
@HostListener('document:mousemove', ['$event']) onMousemove(event) {
// 判断该元素是否被点击了。
if (this.isDown) {
this.el.nativeElement.style.left = this.totalOffsetX + event.clientX - this.disX + 'px';
this.el.nativeElement.style.top = this.totalOffsetY + event.clientY - this.disY + 'px';
}
}
// 监听document离开事件
@HostListener('document:mouseup', ['$event']) onMouseup(event) {
// 只用当元素移动过了,离开函数体才会触发。
if (this.isDown) {
console.log('fail');
this.totalOffsetX += event.clientX - this.disX;
this.totalOffsetY += event.clientY - this.disY;
this.isDown = false;
}
}
ngOnInit() {
this.el.nativeElement.style.position = 'relative';
}
}
指令的核心逻辑:鼠标按下时记录初始坐标,移动时计算偏移量并实时更新元素位置,松开鼠标时累加总偏移量并重置按下状态。其中 totalOffsetX 和 totalOffsetY 用于解决多次拖拽的位置累积问题——否则每次松开后重新拖拽,元素会跳回起始位置。这一细节在初版实现中容易被忽略,值得特别注意。
2. 使用
指令编写完成后,如何应用到项目中?首先,在对应的 NgModule 中将其注册到 declarations 数组里,告知 Angular 该指令可供使用。

接着,直接在需要拖拽的组件标签上添加 appDrag 属性指令即可。例如一个 ,按照上述逻辑,该 div 瞬间就拥有了鼠标拖拽移动的能力。
整体流程非常简单:先定义指令,再注册,最后挂载到元素上。以后其他组件若需要拖拽功能,只需复用同一个指令,无需重复编写事件绑定逻辑。这才是 Angular 指令的正确用法与最佳实践。
