Skip to content

Commit 3c9bc4b

Browse files
authored
Merge pull request gridstack#2515 from elmehdiamlou/fix-scale
fix(scale): update drag & resize when the gridstack parent is scaled
2 parents a6113cf + 5a44c8c commit 3c9bc4b

File tree

3 files changed

+102
-25
lines changed

3 files changed

+102
-25
lines changed

demo/transform.html

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,25 @@
1313
<body>
1414
<div class="container-fluid">
1515
<h1>Transform Parent demo</h1>
16-
<p>example where the grid parent has a translate(50px, 100px) <s>scale(0.5, 0.5)</s> (scale not working yet see #1275)</p>
16+
<p>example where the grid parent has a translate(50px, 100px) and a scale(<span id="scale-x"></span>, <span id="scale-y"></span>)</p>
1717
<div>
1818
<a class="btn btn-primary" onClick="addNewWidget()" href="#">Add Widget</a>
19-
<!-- <a class="btn btn-primary" onClick="zoomIn()" href="#">Zoom in</a>
20-
<a class="btn btn-primary" onClick="zoomOut()" href="#">Zoom out</a> -->
19+
<a class="btn btn-primary" onClick="zoomIn()" href="#">Zoom in</a>
20+
<a class="btn btn-primary" onClick="zoomOut()" href="#">Zoom out</a>
21+
<a class="btn btn-primary" onClick="increaseScaleX()" href="#">Increase Scale X</a>
22+
<a class="btn btn-primary" onClick="decreaseScaleX()" href="#">Decrease Scale X</a>
23+
<a class="btn btn-primary" onClick="increaseScaleY()" href="#">Increase Scale Y</a>
24+
<a class="btn btn-primary" onClick="decreaseScaleY()" href="#">Decrease Scale Y</a>
2125
</div>
2226
<br><br>
23-
<!-- <div style="transform: translate(50px, 100px) scale(var(--global-scale), var(--global-scale)); transform-origin: 0 0;"> -->
24-
<div style="transform: translate(50px, 100px)">
27+
<div style="transform: translate(50px, 100px) scale(var(--global-scale-x), var(--global-scale-y)); transform-origin: 0 0;">
2528
<div class="grid-stack"></div>
2629
</div>
2730
</div>
2831
<script src="events.js"></script>
2932
<script type="text/javascript">
30-
let scale = 0.5;
33+
let scaleX = 0.5;
34+
let scaleY = 0.5;
3135

3236
let grid = GridStack.init({float: true});
3337
addEvents(grid);
@@ -57,20 +61,55 @@ <h1>Transform Parent demo</h1>
5761
};
5862

5963
const updateScaleCssVariable = () => {
60-
document.body.style.setProperty('--global-scale', `${scale}`);
64+
document.body.style.setProperty('--global-scale-x', `${scaleX}`);
65+
document.body.style.setProperty('--global-scale-y', `${scaleY}`);
66+
document.getElementById("scale-x").textContent = scaleX.toFixed(2);
67+
document.getElementById("scale-y").textContent = scaleY.toFixed(2);
6168
}
6269

6370
zoomIn = function() {
64-
const scaleStep = scale < 1 ? 0.05 : 0.1;
65-
scale += scaleStep;
71+
const scaleStep = scaleX < 1 ? 0.05 : 0.1;
72+
scaleX += scaleStep;
73+
scaleY += scaleStep;
6674
updateScaleCssVariable();
6775
}
6876

6977
zoomOut = function() {
70-
const scaleStep = scale < 1 ? 0.05 : 0.1;
71-
scale -= scaleStep;
78+
if(scaleX >= 0.2 && scaleY >= 0.2) {
79+
const scaleStep = scaleX < 1 ? 0.05 : 0.1;
80+
scaleX -= scaleStep;
81+
scaleY -= scaleStep;
82+
updateScaleCssVariable();
83+
}
84+
}
85+
86+
increaseScaleX = function() {
87+
const scaleStep = scaleX < 1 ? 0.05 : 0.1;
88+
scaleX += scaleStep;
89+
updateScaleCssVariable();
90+
}
91+
92+
decreaseScaleX = function() {
93+
if(scaleX >= 0.2) {
94+
const scaleStep = scaleX < 1 ? 0.05 : 0.1;
95+
scaleX -= scaleStep;
96+
updateScaleCssVariable();
97+
}
98+
}
99+
100+
increaseScaleY = function() {
101+
const scaleStep = scaleX < 1 ? 0.05 : 0.1;
102+
scaleY += scaleStep;
72103
updateScaleCssVariable();
73104
}
105+
106+
decreaseScaleY = function() {
107+
if(scaleY >= 0.2) {
108+
const scaleStep = scaleX < 1 ? 0.05 : 0.1;
109+
scaleY -= scaleStep;
110+
updateScaleCssVariable();
111+
}
112+
}
74113

75114
updateScaleCssVariable();
76115

src/dd-draggable.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ interface DragOffset {
3333
offsetTop: number;
3434
}
3535

36+
interface DragScaleReciprocal {
37+
x: number;
38+
y: number;
39+
}
40+
3641
type DDDragEvent = 'drag' | 'dragstart' | 'dragstop';
3742

3843
// make sure we are not clicking on known object that handles mouseDown
@@ -50,6 +55,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
5055
/** @internal */
5156
protected dragOffset: DragOffset;
5257
/** @internal */
58+
protected dragScale: DragScaleReciprocal = { x: 1, y: 1 };
59+
/** @internal */
5360
protected dragElementOriginStyle: Array<string>;
5461
/** @internal */
5562
protected dragEl: HTMLElement;
@@ -329,8 +336,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
329336
// }
330337
const style = this.helper.style;
331338
const offset = this.dragOffset;
332-
style.left = e.clientX + offset.offsetLeft - containmentRect.left + 'px';
333-
style.top = e.clientY + offset.offsetTop - containmentRect.top + 'px';
339+
style.left = (e.clientX + offset.offsetLeft - containmentRect.left) * this.dragScale.x + 'px';
340+
style.top = (e.clientY + offset.offsetTop - containmentRect.top) * this.dragScale.y + 'px';
334341
}
335342

336343
/** @internal */
@@ -367,7 +374,10 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
367374
parent.removeChild(testEl);
368375
xformOffsetX = testElPosition.left;
369376
xformOffsetY = testElPosition.top;
370-
// TODO: scale ?
377+
this.dragScale = {
378+
x: 1 / testElPosition.width,
379+
y: 1 / testElPosition.height
380+
};
371381
}
372382

373383
const targetOffset = el.getBoundingClientRect();
@@ -376,8 +386,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
376386
top: targetOffset.top,
377387
offsetLeft: - event.clientX + targetOffset.left - xformOffsetX,
378388
offsetTop: - event.clientY + targetOffset.top - xformOffsetY,
379-
width: targetOffset.width,
380-
height: targetOffset.height
389+
width: targetOffset.width * this.dragScale.x,
390+
height: targetOffset.height * this.dragScale.y
381391
};
382392
}
383393

@@ -388,8 +398,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
388398
const offset = this.helper.getBoundingClientRect();
389399
return {
390400
position: { //Current CSS position of the helper as { top, left } object
391-
top: offset.top - containmentRect.top,
392-
left: offset.left - containmentRect.left
401+
top: (offset.top - containmentRect.top) * this.dragScale.y,
402+
left: (offset.left - containmentRect.left) * this.dragScale.x
393403
}
394404
/* not used by GridStack for now...
395405
helper: [this.helper], //The object arr representing the helper that's being dragged.

src/dd-resizable.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ export interface DDResizableOpt {
2424
resize?: (event: Event, ui: DDUIData) => void;
2525
}
2626

27+
interface RectScaleReciprocal {
28+
x: number;
29+
y: number;
30+
}
31+
2732
export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt<DDResizableOpt> {
2833

2934
// have to be public else complains for HTMLElementExtendOpt ?
@@ -35,6 +40,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
3540
/** @internal */
3641
protected originalRect: Rect;
3742
/** @internal */
43+
protected rectScale: RectScaleReciprocal = { x: 1, y: 1 };
44+
/** @internal */
3845
protected temporalRect: Rect;
3946
/** @internal */
4047
protected scrollY: number;
@@ -217,6 +224,26 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
217224
protected _setupHelper(): DDResizable {
218225
this.elOriginStyleVal = DDResizable._originStyleProp.map(prop => this.el.style[prop]);
219226
this.parentOriginStylePosition = this.el.parentElement.style.position;
227+
228+
const parent = this.el.parentElement;
229+
const testEl = document.createElement('div');
230+
Utils.addElStyles(testEl, {
231+
opacity: '0',
232+
position: 'fixed',
233+
top: 0 + 'px',
234+
left: 0 + 'px',
235+
width: '1px',
236+
height: '1px',
237+
zIndex: '-999999',
238+
});
239+
parent.appendChild(testEl);
240+
const testElPosition = testEl.getBoundingClientRect();
241+
parent.removeChild(testEl);
242+
this.rectScale = {
243+
x: 1 / testElPosition.width,
244+
y: 1 / testElPosition.height
245+
};
246+
220247
if (getComputedStyle(this.el.parentElement).position.match(/static/)) {
221248
this.el.parentElement.style.position = 'relative';
222249
}
@@ -278,9 +305,9 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
278305
/** @internal constrain the size to the set min/max values */
279306
protected _constrainSize(oWidth: number, oHeight: number): Size {
280307
const maxWidth = this.option.maxWidth || Number.MAX_SAFE_INTEGER;
281-
const minWidth = this.option.minWidth || oWidth;
308+
const minWidth = this.option.minWidth / this.rectScale.x || oWidth;
282309
const maxHeight = this.option.maxHeight || Number.MAX_SAFE_INTEGER;
283-
const minHeight = this.option.minHeight || oHeight;
310+
const minHeight = this.option.minHeight / this.rectScale.y || oHeight;
284311
const width = Math.min(maxWidth, Math.max(minWidth, oWidth));
285312
const height = Math.min(maxHeight, Math.max(minHeight, oHeight));
286313
return { width, height };
@@ -297,7 +324,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
297324
if (!this.temporalRect) return this;
298325
Object.keys(this.temporalRect).forEach(key => {
299326
const value = this.temporalRect[key];
300-
this.el.style[key] = value - containmentRect[key] + 'px';
327+
const scaleReciprocal = key === 'width' || key === 'left' ? this.rectScale.x : key === 'height' || key === 'top' ? this.rectScale.y : 1;
328+
this.el.style[key] = (value - containmentRect[key]) * scaleReciprocal + 'px';
301329
});
302330
return this;
303331
}
@@ -322,12 +350,12 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
322350
const rect = this.temporalRect || newRect;
323351
return {
324352
position: {
325-
left: rect.left - containmentRect.left,
326-
top: rect.top - containmentRect.top
353+
left: (rect.left - containmentRect.left) * this.rectScale.x,
354+
top: (rect.top - containmentRect.top) * this.rectScale.y
327355
},
328356
size: {
329-
width: rect.width,
330-
height: rect.height
357+
width: rect.width * this.rectScale.x,
358+
height: rect.height * this.rectScale.y
331359
}
332360
/* Gridstack ONLY needs position set above... keep around in case.
333361
element: [this.el], // The object representing the element to be resized

0 commit comments

Comments
 (0)