Skip to content

Commit fdcb913

Browse files
committed
Add snap elementOrign and restrict elementRect
interact('*').snap({ elementOrigin: { x: 0, y: 0 } }); elementOrigin.x/y are scalars specifying the position on the element to which snapping should be relative. {x: 0, y: 0} is the top left {x: 0.5, y: 0.5} is the center {x: 1, y: 1} is the bottom right interact('*').restrict({ elementRect: { top: 0, left: 0, bottom: 1, right: 1 } }); For the left and right properties, 0 means the very left of the element and 1 means the very right. For top and bottom, 0 means the top of the element and 1 means the bottom. { top: 0.25, left: 0.25, bottom: 0.75, right: 0.75 } would result in a quarter of the element being allowed to hang over the restriction edges.
1 parent c041895 commit fdcb913

File tree

1 file changed

+80
-22
lines changed

1 file changed

+80
-22
lines changed

interact.js

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
tapTime = 0, // time of the most recent tap event
7272
prevTap = null,
7373

74+
startOffset = { left: 0, right: 0, top: 0, bottom: 0 },
75+
7476
tmpXY = {}, // reduce object creation in getXY()
7577

7678
inertiaStatus = {
@@ -162,8 +164,10 @@
162164
anchors : [],
163165
paths : [],
164166

167+
elementOrigin: { x: 0, y: 0 },
168+
165169
arrayTypes : /^anchors$|^paths$|^actions$/,
166-
objectTypes : /^grid$|^gridOffset$/,
170+
objectTypes : /^grid$|^gridOffset$|^elementOrigin$/,
167171
stringTypes : /^mode$/,
168172
numberTypes : /^range$/,
169173
boolTypes : /^endOnly$/
@@ -1041,6 +1045,22 @@
10411045
return (axis === 'xy' || thisAxis === 'xy' || thisAxis === axis);
10421046
}
10431047

1048+
function checkSnap (interactable, action) {
1049+
var options = interactable.options;
1050+
1051+
action = action || prepared;
1052+
1053+
return (options.snapEnabled && indexOf(options.snap.actions, action) !== -1);
1054+
}
1055+
1056+
function checkRestrict (interactable, action) {
1057+
var options = interactable.options;
1058+
1059+
action = action || prepared;
1060+
1061+
return options.restrictEnabled && options.restrict[action];
1062+
}
1063+
10441064
function collectDrops (event, element) {
10451065
var drops = [],
10461066
elements = [],
@@ -1323,7 +1343,7 @@
13231343
client.x -= origin.x;
13241344
client.y -= origin.y;
13251345

1326-
if (options.snapEnabled && indexOf(options.snap.actions, action) !== -1) {
1346+
if (checkSnap(target) && !(phase === 'start' && options.snap.elementOrigin)) {
13271347

13281348
this.snap = {
13291349
range : snapStatus.range,
@@ -1345,7 +1365,7 @@
13451365
}
13461366
}
13471367

1348-
if (target.options.restrict[action] && restrictStatus.restricted) {
1368+
if (checkRestrict(target) && restrictStatus.restricted) {
13491369
page.x += restrictStatus.dx;
13501370
page.y += restrictStatus.dy;
13511371
client.x += restrictStatus.dx;
@@ -1980,6 +2000,16 @@
19802000
status.realX = page.x;
19812001
status.realY = page.y;
19822002

2003+
var elementOffsetX = 0,
2004+
elementOffsetY = 0;
2005+
2006+
if (snap.elementOrigin) {
2007+
var rect = target.getRect();
2008+
2009+
elementOffsetX = startOffset.left + (rect.width * snap.elementOrigin.x);
2010+
elementOffsetY = startOffset.top + (rect.height * snap.elementOrigin.y);
2011+
}
2012+
19832013
// change to infinite range when range is negative
19842014
if (snap.range < 0) { snap.range = Infinity; }
19852015

@@ -2017,8 +2047,8 @@
20172047

20182048
range = typeof anchor.range === 'number'? anchor.range: snap.range;
20192049

2020-
dx = anchor.x - page.x;
2021-
dy = anchor.y - page.y;
2050+
dx = anchor.x - page.x + elementOffsetX;
2051+
dy = anchor.y - page.y + elementOffsetY;
20222052
distance = hypot(dx, dy);
20232053

20242054
inRange = distance < range;
@@ -2063,11 +2093,11 @@
20632093
status.dy = closest.dy;
20642094
}
20652095
else if (snap.mode === 'grid') {
2066-
var gridx = Math.round((page.x - snap.gridOffset.x) / snap.grid.x),
2067-
gridy = Math.round((page.y - snap.gridOffset.y) / snap.grid.y),
2096+
var gridx = Math.round((page.x - snap.gridOffset.x - elementOffsetX) / snap.grid.x),
2097+
gridy = Math.round((page.y - snap.gridOffset.y - elementOffsetY) / snap.grid.y),
20682098

2069-
newX = gridx * snap.grid.x + snap.gridOffset.x,
2070-
newY = gridy * snap.grid.y + snap.gridOffset.y;
2099+
newX = gridx * snap.grid.x + snap.gridOffset.x + elementOffsetX,
2100+
newY = gridy * snap.grid.y + snap.gridOffset.y + elementOffsetY;
20712101

20722102
dx = newX - page.x;
20732103
dy = newY - page.y;
@@ -2092,10 +2122,14 @@
20922122
}
20932123

20942124
function setRestriction (event, status) {
2095-
var action = interact.currentAction() || prepared,
2096-
restriction = target && target.options.restrict[action],
2125+
var restrict = target && target.options.restrict,
2126+
restriction = restrict && restrict[prepared],
20972127
page;
20982128

2129+
if (!restriction) {
2130+
return status;
2131+
}
2132+
20992133
status = status || restrictStatus;
21002134

21012135
page = status.useStatusXY
@@ -2111,8 +2145,16 @@
21112145
status.dy = 0;
21122146
status.restricted = false;
21132147

2114-
if (!action || !restriction) {
2115-
return status;
2148+
var elementOffset = {};
2149+
2150+
if (restrict.elementRect) {
2151+
var elementRect = target.getRect();
2152+
2153+
elementOffset.left = startOffset.left - (elementRect.width * restrict.elementRect.left);
2154+
elementOffset.top = startOffset.top - (elementRect.height * restrict.elementRect.top);
2155+
2156+
elementOffset.right = startOffset.right - (elementRect.width * (1 - restrict.elementRect.right));
2157+
elementOffset.bottom = startOffset.bottom - (elementRect.height * (1 - restrict.elementRect.bottom));
21162158
}
21172159

21182160
var rect;
@@ -2147,8 +2189,8 @@
21472189
}
21482190
}
21492191

2150-
status.dx = Math.max(Math.min(rect.right , page.x), rect.left) - page.x;
2151-
status.dy = Math.max(Math.min(rect.bottom, page.y), rect.top ) - page.y;
2192+
status.dx = Math.max(Math.min(rect.right - elementOffset.right , page.x), rect.left + elementOffset.left) - page.x;
2193+
status.dy = Math.max(Math.min(rect.bottom - elementOffset.bottom, page.y), rect.top + elementOffset.top ) - page.y;
21522194
status.restricted = true;
21532195

21542196
return status;
@@ -2264,22 +2306,28 @@
22642306
}
22652307

22662308
if (prepared && target) {
2267-
var shouldRestrict = target.options.restrictEnabled && (!target.options.restrict.endOnly || preEnd),
2309+
var shouldRestrict = checkRestrict(target) && (!target.options.restrict.endOnly || preEnd),
22682310
starting = !(dragging || resizing || gesturing),
22692311
snapEvent = starting? downEvent: event;
22702312

22712313
if (starting) {
22722314
prevEvent = downEvent;
2315+
2316+
var rect = target.getRect() || { left: 0, right: 0, top: 0, bottom: 0 };
2317+
2318+
startOffset.left = startCoords.pageX - rect.left;
2319+
startOffset.top = startCoords.pageY - rect.top;
2320+
2321+
startOffset.right = rect.right - startCoords.pageX;
2322+
startOffset.bottom = rect.bottom - startCoords.pageY;
22732323
}
22742324

22752325
if (!shouldRestrict) {
22762326
restrictStatus.restricted = false;
22772327
}
22782328

22792329
// check for snap
2280-
if (target.options.snapEnabled
2281-
&& indexOf(target.options.snap.actions, prepared) !== -1
2282-
&& (!target.options.snap.endOnly || preEnd)) {
2330+
if (checkSnap(target) && (!target.options.snap.endOnly || preEnd)) {
22832331

22842332
setSnapping(snapEvent);
22852333

@@ -2775,7 +2823,7 @@
27752823
}
27762824
}
27772825

2778-
if (target.options.restrictEnabled && target.options.restrict.endOnly) {
2826+
if (checkRestrict(target) && target.options.restrict.endOnly) {
27792827
var restrict = setRestriction(event, statusObject);
27802828

27812829
inertiaStatus.modifiedXe += restrict.dx;
@@ -2788,8 +2836,8 @@
27882836
return;
27892837
}
27902838

2791-
if ((target.options.snapEnabled && target.options.snap.endOnly)
2792-
|| (target.options.restrictEnabled && target.options.restrict.endOnly)) {
2839+
if ((checkSnap(target) && target.options.snap.endOnly)
2840+
|| (checkRestrict(target) && target.options.restrict.endOnly)) {
27932841
// fire a move event at the snapped coordinates
27942842
pointerMove(event, true);
27952843
}
@@ -3516,6 +3564,7 @@
35163564
snap.grid = this.validateSetting('snap', 'grid' , options.grid);
35173565
snap.gridOffset = this.validateSetting('snap', 'gridOffset', options.gridOffset);
35183566
snap.anchors = this.validateSetting('snap', 'anchors' , options.anchors);
3567+
snap.elementOrigin = this.validateSetting('snap', 'elementOrigin' , options.elementOrigin);
35193568

35203569
this.options.snapEnabled = true;
35213570
this.options.snap = snap;
@@ -3879,6 +3928,10 @@
38793928
newRestrictions.endOnly = newValue.endOnly;
38803929
}
38813930

3931+
if (newValue.elementRect instanceof Object) {
3932+
newRestrictions.elementRect = newValue.elementRect;
3933+
}
3934+
38823935
this.options.restrictEnabled = true;
38833936
this.options.restrict = newRestrictions;
38843937
}
@@ -4769,6 +4822,7 @@
47694822
if (options.anchors instanceof Array ) { snap.anchors = options.anchors; }
47704823
if (options.grid instanceof Object) { snap.grid = options.grid; }
47714824
if (options.gridOffset instanceof Object) { snap.gridOffset = options.gridOffset; }
4825+
if (options.elementOrigin instanceof Object) { snap.elementOrigin = options.elementOrigin; }
47724826

47734827
return interact;
47744828
}
@@ -4988,6 +5042,10 @@
49885042
if (typeof newValue.endOnly === 'boolean') {
49895043
defaults.endOnly = newValue.endOnly;
49905044
}
5045+
5046+
if (newValue.elementRect instanceof Object) {
5047+
defaults.elementRect = newValue.elementRect;
5048+
}
49915049
}
49925050

49935051
else if (newValue === null) {

0 commit comments

Comments
 (0)