I am already calculating left and top positions using getBoundingClientRect() of child element wrt to parent in px and passinig from one component to another , and able to place the child elements on the expected positions , however I want to make child elements responsive wrt to parent element , for that I need top and left positions in % I already tried changing values and unit from px to % from console and was able to get desired responsiveness, however trying to calculate in percentage in the code is not working as expected, it doesn't place the child element on correct position, though responsiveness is achieved.
minX: viewRect.left - movableClientRect.left + movable.position.x,
maxX: viewRect.right - movableClientRect.right + movable.position.x,
minY: viewRect.top - movableClientRect.top + movable.position.y,
maxY: viewRect.bottom - movableClientRect.bottom + movable.position.y
this.dataService.setData(this.zonePosition =
{
x : (viewRect.left - movableClientRect.left)/viewRect.left ,
y: (viewRect.top - movableClientRect.top)/viewRect.top
}
In the earlier code I was only doing viewRect.left - movableClientRect.left , so values were in pixel , and now I tried dividing by viewRect.left and then * 100 to convert it into % but the percentage value doesn't place the child elements correctly.
Updating with the directives for calculating the position
movable.directive In this I calculate the x and y pos of the child element
interface Position {
x: number;
y: number;
}
@Directive({
selector: '[appMovable]'
})
export class MovableDirective extends DraggableDirective {
@HostBinding('style.transform') get transform(): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(
`translateX(${this.position.x}%) translateY(${this.position.y}%)`
);
}
@HostBinding('class.movable') movable = true;
position: Position = {x: 0, y: 0};
zonePosition = {x:0, y:0, id:""}
private startPosition: Position;
@Input('appMovableReset') reset = false;
constructor(private sanitizer: DomSanitizer, public element: ElementRef,
private dataService: DataService) {
super(element);
}
@HostListener('dragStart', ['$event'])
onDragStart(event: PointerEvent) {
this.startPosition = {
x: event.clientX - this.position.x,
y: event.clientY - this.position.y
}
}
@HostListener('dragMove', ['$event'])
onDragMove(event: PointerEvent) {
this.position.x = event.clientX - this.startPosition.x;
this.position.y = event.clientY - this.startPosition.y;
}
@HostListener('dragEnd', ['$event'])
onDragEnd(event: PointerEvent) {
if (this.reset) {
this.position = {x: 0, y: 0};
}
}
}
The directive in which I am calculating the left and top of the child element and assigning it to dataService
Directive({
selector: '[appMovableArea]'
})
export class MovableAreaDirective implements AfterContentInit {
@ContentChildren(MovableDirective) movables: QueryList<MovableDirective>;
private boundaries: Boundaries;
private subscriptions: Subscription[] = [];
zonePosition = {x:0, y:0, id:""}
constructor(private element: ElementRef, private dataService: DataService)
{}
ngAfterContentInit(): void {
this.movables.changes.subscribe(() => {
this.subscriptions.forEach(s => s.unsubscribe());
this.movables.forEach(movable => {
this.subscriptions.push(movable.dragStart.subscribe(() =>
this.measureBoundaries(movable)));
this.subscriptions.push(movable.dragMove.subscribe(() =>
this.maintainBoundaries(movable)));
});
});
this.movables.notifyOnChanges();
}
private measureBoundaries(movable: MovableDirective) {
const viewRect: ClientRect =
this.element.nativeElement.getBoundingClientRect();
const movableClientRect: ClientRect =
movable.element.nativeElement.getBoundingClientRect();
this.dataService.setData(this.zonePosition= {x : (viewRect.left -
movableClientRect.left)/viewRect.left , y: (viewRect.top -
movableClientRect.top)/viewRect.top, id: "" })
this.boundaries = {
minX: viewRect.left - movableClientRect.left + movable.position.x,
maxX: viewRect.right - movableClientRect.right + movable.position.x,
minY: viewRect.top - movableClientRect.top + movable.position.y,
maxY: viewRect.bottom - movableClientRect.bottom + movable.position.y
};
}
private maintainBoundaries(movable: MovableDirective) {
movable.position.x = Math.max(this.boundaries.minX,
movable.position.x);
movable.position.x = Math.min(this.boundaries.maxX, movable.position.x);
movable.position.y = Math.max(this.boundaries.minY, movable.position.y);
movable.position.y = Math.min(this.boundaries.maxY, movable.position.y);
}
}
If you already have the position in pixels relative to the container element, then all you need is to divide this value in px by the size of the container in pixel and multiply this by 100:
perc_x = px_x / px_width * 100;
perc_y = px_y / px_height * 100;
elem = document.querySelector('.content');
container = document.querySelector('.container');
const mouse = {
x: null,
y: null,
down: false
};
let will_draw = false;
elem.onmousedown = e => {
mouse.down = true;
};
onmouseup = e => {
mouse.down = false;
};
document.onmousemove = e => {
if(!mouse.down) { return; }
const container_rect = container.getBoundingClientRect();
// relative to container, in px
mouse.x = e.clientX - container_rect.left;
mouse.y = e.clientY - container_rect.top;
if(!will_draw) {
requestAnimationFrame(draw);
will_draw = true;
}
}
function draw() {
will_draw = false;
const { width, height} = container.getBoundingClientRect();
const perc_x = mouse.x / width * 100;
const perc_y = mouse.y / height * 100;
// -5 to center (elem has its width set to 10%)
elem.style.setProperty('left', (perc_x - 5) + '%');
// -5 to center (elem has its height set to 10%)
elem.style.setProperty('top', (perc_y - 5) + '%');
}
.container {
width: 80vw;
height: 80vh;
border: 1px solid;
position: relative;
}
.content {
width: 10%;
height: 10%;
position: absolute;
top: 45%;
left: 45%;
background: green;
}
<div class="container">
<div class="content">
</div>
</div>