以百分比计算 getBoundingClientRect
Calculating getBoundingClientRect in percentage
我已经在使用 getBoundingClientRect() 计算左侧和顶部位置,使用子元素 wrt 到 px 中的父元素,并从一个组件传递到另一个组件,并且能够将子元素放置在预期位置,但是我想子元素响应父元素,为此我需要 % 中的顶部和左侧位置
我已经尝试从控制台将值和单位从 px 更改为 % 并且能够获得所需的响应速度,但是尝试在代码中以百分比计算并没有按预期工作,它没有将子元素放在正确的位置,尽管响应速度实现了。
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
}
在之前的代码中我只做 viewRect.left - movableClientRect.left ,所以值以像素为单位,现在我尝试除以 viewRect.left 然后 * 100 将其转换为% 但百分比值未正确放置子元素。
更新计算位置的指令
movable.directive
在此我计算子元素的 x 和 y pos
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};
}
}
}
我在其中计算子元素的左侧和顶部并将其分配给 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);
}
}
如果您已经有了相对于容器元素的位置(以像素为单位),那么您只需将此值(以像素为单位)除以容器的大小(以像素为单位),然后乘以 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>
我已经在使用 getBoundingClientRect() 计算左侧和顶部位置,使用子元素 wrt 到 px 中的父元素,并从一个组件传递到另一个组件,并且能够将子元素放置在预期位置,但是我想子元素响应父元素,为此我需要 % 中的顶部和左侧位置 我已经尝试从控制台将值和单位从 px 更改为 % 并且能够获得所需的响应速度,但是尝试在代码中以百分比计算并没有按预期工作,它没有将子元素放在正确的位置,尽管响应速度实现了。
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
}
在之前的代码中我只做 viewRect.left - movableClientRect.left ,所以值以像素为单位,现在我尝试除以 viewRect.left 然后 * 100 将其转换为% 但百分比值未正确放置子元素。
更新计算位置的指令
movable.directive 在此我计算子元素的 x 和 y pos
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};
}
}
}
我在其中计算子元素的左侧和顶部并将其分配给 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);
}
}
如果您已经有了相对于容器元素的位置(以像素为单位),那么您只需将此值(以像素为单位)除以容器的大小(以像素为单位),然后乘以 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>