123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- export interface ElementInfo{
- el: HTMLElement | null;
- left: number;
- top: number;
- parentLeft: number;
- parentTop: number;
- width: number;
- height: number;
- // 鼠标点击位置与元素位置的差值
- diffX: number;
- diffY: number;
- }
- export interface MouseInfo{
- x: number;
- y: number;
- }
- export interface MouseListener{
- (mouse: MouseInfo, el: ElementInfo): void;
- }
- // 元素拖动
- function getElementLeft(el: HTMLElement): number{
- let left = el.offsetLeft || 0;
- if(el.parentElement){
- left += getElementLeft(el.parentElement);
- }
- return left;
- }
- function getElementTop(el: HTMLElement): number{
- let top = el.offsetTop || 0;
- if(el.parentElement){
- top += getElementTop(el.parentElement);
- }
- return top;
- }
- function getElementDistanceToViewportEdge(element: HTMLElement): { top: number, right: number, bottom: number, left: number } {
- const rect = element.getBoundingClientRect();
- const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
- const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
- // 兼容性处理
- const scrollX = window.scrollX || window.pageXOffset || document.documentElement.scrollLeft;
- const scrollY = window.scrollY || window.pageYOffset || document.documentElement.scrollTop;
- const top = rect.top - scrollY;
- const right = viewportWidth - rect.right + scrollX;
- const bottom = viewportHeight - rect.bottom + scrollY;
- const left = rect.left - scrollX;
- return { top, right, bottom, left };
- }
- // 获取元素滚动条位置
- function getScrollLeft(el: HTMLElement): number{
- return el.scrollLeft || 0;
- }
- function getScrollTop(el: HTMLElement): number{
- return el.scrollTop || 0;
- }
- /**
- * 鼠标位置基于滚动条偏移
- * @param mouseInfo 直接修改此对象
- * @param el
- */
- function comMouseInfo(mouseInfo: MouseInfo, el?: HTMLElement | null): MouseInfo{
- // 父元素有滚动条的情况下,需要减去滚动条的偏移量
- if (el) {
- const scrollX = getScrollLeft(el);
- const scrollY = getScrollTop(el);
- // console.log(`scrollX: ${scrollX} scrollY: ${scrollY}`);
- mouseInfo.x += scrollX;
- mouseInfo.y += scrollY;
- }
- return mouseInfo;
- }
- export enum CollisionDirection {
- None = 0,
- Top = 1,
- Down = 2,
- Left = 3,
- Right = 4
- }
- export interface CollisionResult {
- colliding: boolean;
- direction: CollisionDirection;
- }
- export interface Rect {
- x: number;
- y: number;
- width: number;
- height: number;
- }
- export function detectCollisionDirection(rect1: Rect, rect2: Rect): CollisionResult {
- const horizontalOverlap = Math.min(rect1.x + rect1.width, rect2.x + rect2.width) - Math.max(rect1.x, rect2.x);
- const verticalOverlap = Math.min(rect1.y + rect1.height, rect2.y + rect2.height) - Math.max(rect1.y, rect2.y);
- if (horizontalOverlap > 0 && verticalOverlap > 0) {
- if (horizontalOverlap > verticalOverlap) {
- return {
- colliding: true,
- direction: rect1.y < rect2.y ? CollisionDirection.Top : CollisionDirection.Down
- };
- } else {
- return {
- colliding: true,
- direction: rect1.x < rect2.x ? CollisionDirection.Left : CollisionDirection.Right
- };
- }
- }
- return { colliding: false, direction: CollisionDirection.None };
- }
- export class Drag{
- constructor(el: HTMLElement) {
- // 绑定事件
- this.el = el;
- this.bindEvent();
- }
- el: HTMLElement;
- isMove: boolean = false;
- // 延迟时间, 同一时间内的合并为同一
- parent: ElementInfo = {
- el: null,
- left: 0,
- top: 0,
- parentTop: 0,
- parentLeft: 0,
- width: 0,
- height: 0,
- diffX: 0,
- diffY: 0
- };
- thisInfo: ElementInfo = {
- el: null,
- left: 0,
- top: 0,
- parentTop: 0,
- parentLeft: 0,
- width: 0,
- height: 0,
- diffX: 0,
- diffY: 0
- };
- startX: number = 0;
- startY: number = 0;
- public static Event = {
- moveStart: "moveStart",
- move: "move",
- moveEnd: "mouseEnd"
- }
- //
- private bindEvent() {
- const el = this.el;
- el.addEventListener('mousedown', this.downEvent)
- // todo 触摸支持
- let parent = el.parentElement;
- if (!parent) {
- parent = document.body;
- }
- const parentDistance = getElementDistanceToViewportEdge(parent);
- this.parent = {
- el: parent,
- left: parentDistance.left,
- top: parentDistance.top,
- parentTop: 0,
- parentLeft: 0,
- width: parent.offsetWidth,
- height: parent.offsetHeight,
- diffX: 0,
- diffY: 0
- }
- this.thisInfo = {
- el: el,
- left: getElementLeft(el),
- top: getElementTop(el),
- parentTop: this.parent.top,
- parentLeft: this.parent.left,
- width: el.offsetWidth,
- height: el.offsetHeight,
- diffX: 0,
- diffY: 0
- }
- }
- downEvent = (e: MouseEvent) => {
- if(this.isMove){
- return
- }
- // 获取鼠标位置
- const x = e.clientX;
- const y = e.clientY;
- this.isMove = true;
- // 计算元素偏移差值
- const diffX = e.offsetX;
- const diffY = e.offsetY;
- let mouseInfo: MouseInfo = {
- x: x,
- y: y
- }
- this.startX = x;
- this.startY = y;
- this.thisInfo = {
- ...this.thisInfo,
- diffX: diffX,
- diffY: diffY
- }
- if (this.parent.el) {
- const parentDistance = getElementDistanceToViewportEdge(this.parent.el)
- this.thisInfo.parentTop = parentDistance.top
- this.thisInfo.parentLeft = parentDistance.left
- comMouseInfo(mouseInfo, this.parent.el);
- }
- this.moveStart && this.moveStart(mouseInfo, this.thisInfo);
- setTimeout(()=>{
- document.addEventListener('pointermove', this.mouseMove);
- }, 100)
- document.addEventListener('mouseup', this.mouseUp);
- }
- mouseUp = (e: MouseEvent) => {
- this.isMove = false;
- // 获取鼠标位置
- const x = e.clientX;
- const y = e.clientY;
- let mouseInfo: MouseInfo = {
- x: x,
- y: y
- }
- // 解除事件绑定
- document.removeEventListener('pointermove', this.mouseMove);
- document.removeEventListener('mouseup', this.mouseUp);
- this.moveEnd && this.moveEnd(mouseInfo , this.thisInfo);
- }
- mouseMove = (e: MouseEvent) => {
- if (!this.isMove){
- // 已经被取消
- document.removeEventListener('pointermove', this.mouseMove);
- return
- }
- // 获取鼠标位置
- const x = e.pageX;
- const y = e.pageY;
- let mouseInfo: MouseInfo = {
- x: x,
- y: y
- }
- comMouseInfo(mouseInfo, this.parent.el);
- this.move && this.move(mouseInfo, this.thisInfo);
- }
- moveStart: MouseListener | null = null;
- move: MouseListener | null = null;
- moveEnd: MouseListener | null = null;
- public on(eventName: string, callback: MouseListener) {
- switch(eventName) {
- case Drag.Event.moveStart:
- this.moveStart = callback;
- break;
- case Drag.Event.move:
- this.move = callback;
- break;
- case Drag.Event.moveEnd:
- this.moveEnd = callback;
- break;
- default:
- throw new Error('Invalid event name');
- }
- }
- public off(eventName: string) {
- switch(eventName) {
- case Drag.Event.moveStart:
- this.moveStart = null;
- break;
- case Drag.Event.move:
- this.move = null;
- break;
- case Drag.Event.moveEnd:
- this.moveEnd = null;
- break;
- default:
- throw new Error('Invalid event name');
- }
- }
- public destroy() {
- // 移除事件
- this.el.removeEventListener('mousedown', this.downEvent);
- document.removeEventListener('mouseup', this.mouseUp);
- document.removeEventListener('mousemove', this.mouseMove);
- this.off(Drag.Event.moveStart);
- this.off(Drag.Event.move);
- this.off(Drag.Event.moveEnd);
- }
- }
|