I had the same issue with scaling a full scene with drag and drop using CDK material with Angular 11.
The dragConstrainPoint solution did not work for my context.
The idea is really to avoid transform scale or zoom, has it mess with the mouse and objects positions, making it a nightmare with multiplayer drag & drop.
My solution works well in multiplayer having different scene scales
Solution:
I have used CSS variable --scale
on a wrapper div, with dynamic scale calculation
<div class="game-wrapper" style="{{'--scale:'+ gameScale}}">
In Angular the scene scale is calculated like this:
...
private _unsubscriber$: Subject<any> = new Subject();
public screenWidth$: BehaviorSubject<number> = new BehaviorSubject(null);
public screenHeight$: BehaviorSubject<number> = new BehaviorSubject(null);
public gameScale: number = 1;
public constructor(
@Inject(DOCUMENT) private document: any
) { }
...
ngOnInit(): void {
//Manage screen width
this._setScreenResolution(window.innerWidth, window.innerHeight);
fromEvent(window, 'resize')
.pipe(
debounceTime(1000),
takeUntil(this._unsubscriber$)
).subscribe((evt: any) => {
this._setScreenResolution(evt.target.innerWidth, evt.target.innerHeight);
});
}
ngOnDestroy() {
this._unsubscriber$.next();
this._unsubscriber$.complete();
}
private _setScreenResolution(width: number, height: number): void {
this.screenWidth$.next(width);
this.screenHeight$.next(height);
let scaleX = windowWidth / this.sceneWidth;
let scaleY = windowHeight / this.sceneHeight;
let scale = scaleX;
if(scaleX > scaleY) {
scale = scaleY;
}
//console.log("getGameScale", scale.toFixed(3));
scale = parseFloat(scale.toFixed(3));
this.gameScale = scale;
}
In the HTML templates and CSS, at each time pixels are involved, we add the calc().
Here some exemples of CSS with those calculs
width: calc(18px * var(--scale));
padding: calc(1px * var(-scale)) calc(6px * var(-scale));
background-size: calc(30px * var(-scale)) calc(30px * var(-scale)), cover;
margin: calc(20px * var(-scale)) calc(30px * var(-scale));
bottom: calc(300px * var(--scale));
border-radius: calc(4px * var(--scale));
font-size: calc(21px * var(--scale));
left: calc(10px * var(--scale));
top: calc(4px * var(--scale));
box-shadow: 0 0 0 calc(4px * var(--scale)) rgba(0,0,0,0.1) inset;
border: calc(2px * var(--scale)) solid #6898f3;
height: calc(32px * var(--scale));
min-height: calc(26px * var(--scale));
line-height: calc(42px * var(--scale));
For the local CDK drag move and drag end values to be well sent to other multiplayers,
you need to scale them by dividing by the scale:
dragMoved(event: CdkDragMove) {
let {offsetLeft, offsetTop} = event.source.element.nativeElement;
let {x, y} = event.distance;
x = x / this.gameScale;
y = y / this.gameScale;
offsetLeft = offsetLeft / this.gameScale;
offsetTop = offsetTop / this.gameScale;
//Remove scene offset from calcul (ex. for aside left menu)
let xpos = ((offsetLeft + x) - this.offset.x);
let ypos = ((offsetTop + y) - this.offset.y);
let position: any = {x: xpos, y: ypos};
//The position is then sent to a multiplayer management relay message...
}
//same for drag end + do not forget to reset the transform 3D at the end of the function
dragEnded(event: CdkDragEnd) {
...
//The position at drag end is set on the object and for all other players
//Reset translate3D
//event.source._dragRef.setFreeDragPosition({x: 0, y: 0});
event.source._dragRef.reset();
}
Voilà!