Skip to content

Commit

Permalink
Incremental Progress on Constraint Testing
Browse files Browse the repository at this point in the history
  • Loading branch information
bmartinson committed May 13, 2019
1 parent f077571 commit 693844a
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 68 deletions.
Expand Up @@ -8,6 +8,9 @@ export class NgxDraggableBoundsCheckEvent {
public readonly right: boolean;
public readonly bottom: boolean;
public readonly left: boolean;
public readonly constrainedCenter: DOMPoint | undefined;
public readonly isConstrainedX: boolean;
public readonly isConstrainedY: boolean;

/**
* Read only property that indicates if one or more of the boundaries have been collided with.
Expand All @@ -18,11 +21,35 @@ export class NgxDraggableBoundsCheckEvent {
return !!this.top || !!this.right || !!this.bottom || !!this.left;
}

constructor(top: boolean, right: boolean, bottom: boolean, left: boolean) {
/**
* Constructs the bounds check event with default properties.
*
* @param top Whether the top edge has been breached or not.
* @param right Whether the right edge has been breached or not.
* @param bottom Whether the bottom edge has been breached or not.
* @param left Whether the left edge has been breached or not.
* @param elP0 The center point of the element if it were to be constrained by these bounds.
* @param isConstrainedX Whether the element should be constrained in the X direction or not..
* @param isConstrainedY Whether the element should be constrained din the Y direction or not.
*/
constructor(
top: boolean,
right: boolean,
bottom: boolean,
left: boolean,
elP0: DOMPoint,
isConstrainedX: boolean,
isConstrainedY: boolean,
) {
this.top = (!!top) ? top : false;
this.right = (!!right) ? right : false;
this.bottom = (!!bottom) ? bottom : false;
this.left = (!!left) ? left : false;
if (!!elP0) {
this.constrainedCenter = elP0;
}
this.isConstrainedX = (!!isConstrainedX) ? isConstrainedX : false;
this.isConstrainedY = (!!isConstrainedY) ? isConstrainedY : false;
}

}
Expand Up @@ -25,6 +25,12 @@ export class NgxDraggableMoveEvent {
return this._position;
}

/**
* Constructs the move event with specified property values.
*
* @param target The target HTMLElement that was moved.
* @param position The position of the target HTMLElement.
*/
constructor(target: HTMLElement, position: DOMPoint) {
if (!!target) {
this._target = target;
Expand Down
@@ -1,7 +1,13 @@
import { Directive, ElementRef, EventEmitter, HostListener, Inject, Input, OnInit, Output, Renderer2 } from "@angular/core";
import { NgxDraggableBoundsCheckEvent } from "../classes/ngx-draggable-bounds-check-event";
import { NgxDraggableMoveEvent } from "../classes/ngx-draggable-move-event";
import { isPointInsideBounds, getTransformedCoordinate, rotatePoint, ElementHandle } from "../helpers/ngx-draggable-dom-math";
import {
isPointInsideBounds,
getTransformedCoordinate,
rotatePoint,
ElementHandle,
getBoundingBox,
} from "../helpers/ngx-draggable-dom-math";
import { getRotationForElement, getTotalRotationForElement, getTransformMatrixForElement } from "../helpers/ngx-draggable-dom-utilities";

const MAX_SAFE_Z_INDEX = 16777271;
Expand All @@ -28,7 +34,6 @@ export class NgxDraggableDomDirective implements OnInit {
private clientMoving: DOMPoint;
private oldClientPosition: DOMPoint;
private original: DOMPoint;
private naturalPosition: DOMPoint;
private oldTrans: DOMPoint;
private tempTrans: DOMPoint;
private oldZIndex: string;
Expand Down Expand Up @@ -70,6 +75,51 @@ export class NgxDraggableDomDirective implements OnInit {
return !!this.allowDrag;
}

/**
* Read only property that returns the width of the element in a normalized 0 degree rotation orientation.
*
* @return The true width of the element.
*/
private get elWidth(): number {
if (!this.el.nativeElement) {
return 0;
}

return this.el.nativeElement.offsetWidth;
}

/**
* Read only property that returns the height of the element in a normalized 0 degree rotation orientation.
*
* @return The true height of the element.
*/
private get elHeight(): number {
if (!this.el.nativeElement) {
return 0;
}

return this.el.nativeElement.offsetHeight;
}

/**
* Calculates and returns the element's center point based on the bounding element's bounding rectangle.
*
* @return A DOMPoint that represents the center point of the element.
*/
private get elCenter(): DOMPoint {
if (!this.el.nativeElement) {
return new DOMPoint(0, 0);
}

// get the bounding box of the element
const elBounds: ClientRect = (this.el.nativeElement as HTMLElement).getBoundingClientRect();

return new DOMPoint(
elBounds.left + (elBounds.width / 2),
elBounds.top + (elBounds.height / 2),
);
}

constructor(@Inject(ElementRef) private el: ElementRef, @Inject(Renderer2) private renderer: Renderer2) {
this.started = new EventEmitter<NgxDraggableMoveEvent>();
this.stopped = new EventEmitter<NgxDraggableMoveEvent>();
Expand All @@ -78,7 +128,7 @@ export class NgxDraggableDomDirective implements OnInit {

this.constrainByBounds = this.moving = this.constrainedX = this.constrainedY = false;
this.allowDrag = true;
this.oldClientPosition = this.original = this.naturalPosition = null;
this.oldClientPosition = this.original = null;
this.oldZIndex = this.oldPosition = "";
this.computedRotation = 0;
this.clientMoving = new DOMPoint(0, 0);
Expand Down Expand Up @@ -246,7 +296,7 @@ export class NgxDraggableDomDirective implements OnInit {
*/
public reset(): void {
this.moving = this.constrainedX = this.constrainedY = false;
this.oldClientPosition = this.original = this.naturalPosition = null;
this.oldClientPosition = this.original = null;
this.oldZIndex = this.oldPosition = "";

// reset all stored positions without defining a new object
Expand All @@ -268,15 +318,15 @@ export class NgxDraggableDomDirective implements OnInit {
* @param y The y position to move the element to.
*/
private moveTo(x: number, y: number): void {
let boundsResponse: NgxDraggableBoundsCheckEvent;
let boundsCheck: NgxDraggableBoundsCheckEvent;
let matrix: number[];
let transform: string;
let boundary: ClientRect;
let elBounds: ClientRect;

if (this.original) {
// check the bounds
boundsResponse = this.boundsCheck();
boundsCheck = this.boundsCheck();

// calculate the new translation
this.tempTrans.x = x - this.original.x;
Expand All @@ -299,42 +349,30 @@ export class NgxDraggableDomDirective implements OnInit {
elBounds = this.el.nativeElement.getBoundingClientRect();

// if the bounds were checked, adjust the positioning of the element to prevent dragging outside the bounds
if (boundsResponse) {
if (this.constrainByBounds) {
// get the bounding client rectangles for the boundary element
boundary = this.bounds.getBoundingClientRect();

// check to constrain in the x direction
// if ((!boundsResponse.left && boundsResponse.right && this.clientMoving.x <= 0) ||
// this.naturalPosition.x + transX < boundary.left) {
// transX = boundary.left - this.naturalPosition.x;
// this.constrainedX = true;
// } else if ((boundsResponse.left && !boundsResponse.right && this.clientMoving.x >= 0) ||
// this.naturalPosition.x + elBounds.width + transX > boundary.left + boundary.width) {
// transX = boundary.right - elBounds.width - this.naturalPosition.x;
// this.constrainedX = true;
// }

// check to constrain in the y direction
// if ((!boundsResponse.top && boundsResponse.bottom && this.clientMoving.y <= 0) ||
// this.naturalPosition.y + transY < boundary.top) {
// transY = boundary.top - this.naturalPosition.y;
// this.tempTrans.y = transY;
// this.constrainedY = true;
// } else if ((boundsResponse.top && !boundsResponse.bottom && this.clientMoving.y >= 0) ||
// this.naturalPosition.y + elBounds.height + transY > boundary.top + boundary.height) {
// transY = boundary.bottom - elBounds.height - this.naturalPosition.y;
// this.constrainedY = true;
// }

// if we constrained in one of the directions, update that direction's tempTrans value for putBack
if (this.constrainedX) {
this.tempTrans.x = transX;
}
if (this.constrainedY) {
this.tempTrans.y = transY;
}
}
if (boundsCheck) {
console.log("bounds checking", boundsCheck.isConstrainedX, boundsCheck.isConstrainedY);

// if (this.constrainByBounds) {
// // track what direction we are constraining
// this.constrainedX = boundsCheck.isConstrainedX;
// this.constrainedY = boundsCheck.isConstrainedY;

// // if we are constrained in at least on direction, calculate the new position
// if (this.constrainedX || this.constrainedY) {
// // get the center point of the element
// const elCenter: DOMPoint = this.elCenter;

// // if we constrained in one of the directions, update that direction's tempTrans value for putBack
// if (this.constrainedX) {
// // transX -= (elCenter.x + transX) - boundsCheck.constrainedCenter.x;
// // transX -= (elCenter.x) - boundsCheck.constrainedCenter.x;
// this.tempTrans.x = transX;
// }
// if (this.constrainedY) {
// this.tempTrans.y = transY;
// }
// }
// }
}

// if it is possible, get the transform from the computed style and modify the matrix to maintain transform properties
Expand Down Expand Up @@ -370,16 +408,16 @@ export class NgxDraggableDomDirective implements OnInit {
this.curTrans.y = transY;

// emit the output of the bounds check
if (boundsResponse) {
this.edge.emit(boundsResponse);
if (boundsCheck) {
this.edge.emit(boundsCheck);
}

// emit the current translation
this.moved.emit(new NgxDraggableMoveEvent(this.el.nativeElement as HTMLElement, this.curTrans));
}

// clean up memory
boundsResponse = matrix = transform = elBounds = boundary = null;
boundsCheck = matrix = transform = elBounds = boundary = null;
}

/**
Expand Down Expand Up @@ -437,14 +475,6 @@ export class NgxDraggableDomDirective implements OnInit {
this.renderer.addClass(this.handle ? this.handle : this.el.nativeElement, "ngx-dragging");
}

// track the natural position of the element (the window relative position of the element)
if (!this.naturalPosition) {
this.naturalPosition = new DOMPoint(
this.el.nativeElement.getBoundingClientRect().left,
this.el.nativeElement.getBoundingClientRect().top,
);
}

// clean up memory
position = null;
}
Expand All @@ -467,9 +497,9 @@ export class NgxDraggableDomDirective implements OnInit {

// if the user wants bounds checking, do a check and emit the boundaries if bounds have been hit
if (this.bounds) {
const boundsResponse: NgxDraggableBoundsCheckEvent = this.boundsCheck();
if (boundsResponse) {
this.edge.emit(boundsResponse);
const boundsCheck: NgxDraggableBoundsCheckEvent = this.boundsCheck();
if (boundsCheck) {
this.edge.emit(boundsCheck);
}
}

Expand Down Expand Up @@ -518,7 +548,8 @@ export class NgxDraggableDomDirective implements OnInit {
}

// generate the bounds dimensional information
let boundsBounds: ClientRect = this.bounds.getBoundingClientRect();
let normalizedBoundsBounds: DOMRect;
let boundsBounds: ClientRect = (this.bounds as HTMLElement).getBoundingClientRect();
let boundsWidth: number = this.bounds.offsetWidth;
let boundsHeight: number = this.bounds.offsetHeight;
let boundsRotation: number = getRotationForElement(this.bounds);
Expand All @@ -537,14 +568,16 @@ export class NgxDraggableDomDirective implements OnInit {
let checkBounds: DOMRect = new DOMRect(boundsTL.x, boundsTL.y, boundsWidth, boundsHeight);

// generate the elements dimensional information
let normalizedElBounds: DOMRect;
let elBounds: ClientRect = (this.el.nativeElement as HTMLElement).getBoundingClientRect();
let elWidth: number = this.el.nativeElement.offsetWidth;
let elHeight: number = this.el.nativeElement.offsetHeight;
let elWidth: number = this.elWidth;
let elHeight: number = this.elHeight;
let elRotation: number = getTotalRotationForElement(this.el.nativeElement);
let elP0: DOMPoint = new DOMPoint(
elBounds.left + (elBounds.width / 2),
elBounds.top + (elBounds.height / 2),
);
let normalizedElP0: DOMPoint = rotatePoint(elP0, boundsP0, -boundsRotation);

// generate all four points of the element that we will need to check
let elTL: DOMPoint = getTransformedCoordinate(elP0, elWidth, elHeight, elRotation, ElementHandle.TL);
Expand Down Expand Up @@ -582,28 +615,60 @@ export class NgxDraggableDomDirective implements OnInit {
isBROutside && elBR.x <= checkBounds.left ||
isBLOutside && elBL.x <= checkBounds.left;

// define variables to store the displacement of the element to constrain it within the bounds
let displaceX = 0;
let displaceY = 0;

// if we are to constrain by the bounds, calculate the displacement of the element to keep it within the bounds
if (!!this.constrainByBounds) {
// calculate the constraining displacement if the element fits within the height of the bounds
if (elHeight < boundsHeight) {
if (!!this.constrainByBounds && isTopEdgeCollided || isRightEdgeCollided || isBottomEdgeCollided || isLeftEdgeCollided) {
// get the bounding box for the normalized element
normalizedElBounds = getBoundingBox(normalizedElP0, elWidth, elHeight, elRotation);

}
// get the bounding box for the normalized boundaries
normalizedBoundsBounds = getBoundingBox(boundsP0, boundsWidth, boundsHeight, 0);

// calculate the constraining displacement if the element fits within the width of the bounds
if (elWidth < boundsWidth) {
if (normalizedBoundsBounds.left >= normalizedElBounds.left) {
// the element is off to the left so we must displace back right
displaceX = normalizedBoundsBounds.left - normalizedElBounds.left;
} else if (normalizedBoundsBounds.left + normalizedBoundsBounds.width <= normalizedElBounds.left + normalizedElBounds.width) {
// the element is off to the right so we must displace back left
displaceX = (normalizedBoundsBounds.left + normalizedBoundsBounds.width) - (normalizedElBounds.left + normalizedElBounds.width);
}
}

// calculate the constraining displacement if the element fits within the height of the bounds
if (elHeight < boundsHeight) {
if (normalizedBoundsBounds.top >= normalizedElBounds.top) {
// the element is off to the top so we must displace back down
displaceY = normalizedBoundsBounds.top - normalizedElBounds.top;
} else if (normalizedBoundsBounds.top + normalizedBoundsBounds.height <= normalizedElBounds.top + normalizedElBounds.height) {
// the element is off to the bottom so we must displace back up
displaceY = (normalizedBoundsBounds.top + normalizedBoundsBounds.height) - (normalizedElBounds.top + normalizedElBounds.height);
}
}
}

// displace the normalized center point of the element
let constrainedElP0: DOMPoint = new DOMPoint(normalizedElP0.x + displaceX, normalizedElP0.y + displaceY);

// rotate the constrained and normalized point back by the bounds rotation so we find its true placement
constrainedElP0 = rotatePoint(constrainedElP0, boundsP0, boundsRotation);

// clean up memory
elTL = elTR = elBR = elBL = isTLOutside = isTROutside = isBROutside = isBLOutside = elBounds = elWidth = elHeight =
elRotation = elP0 = checkBounds = boundsBounds = boundsWidth = boundsHeight = boundsRotation = boundsP0 = null;
elRotation = elP0 = checkBounds = boundsBounds = boundsWidth = boundsHeight = boundsRotation = boundsP0 =
normalizedElBounds = normalizedElP0 = normalizedBoundsBounds = null;

return new NgxDraggableBoundsCheckEvent(
isTopEdgeCollided,
isRightEdgeCollided,
isBottomEdgeCollided,
isLeftEdgeCollided,
constrainedElP0,
displaceX !== 0,
displaceY !== 0,
);
}

Expand Down
Expand Up @@ -52,7 +52,7 @@ export function getRotationForElement(el: HTMLElement): number {
const matrix: number[] = getTransformMatrixForElement(el);

// calculate the rotation in degrees based on the transform matrix
return (Math.acos(matrix[0]) * 180) / Math.PI;
return (Math.asin(matrix[1]) * 180) / Math.PI;
}

/**
Expand Down

0 comments on commit 693844a

Please sign in to comment.