Box bounds
Prevent child box from scrolling past edges of parent
by Leolloyd Andrade
HTML
<div class="parent">
<div class="grid">
</div>
</div>
SCSS
.parent {
width: 300px;
height: 300px;
position: relative;
border: 1px solid red;
background: yellow;
overflow: hidden;
}
.grid {
width: 500px;
height: 500px;
position: absolute;
//border: 1px solid blue;
background: #ddd;
}
JavaScript
const qs = (selector, parent = document) => parent.querySelector(selector);
const gridEl = qs('.grid');
const parentEl = qs('.parent');
const position = {
mouseDownX: 0,
mouseDownY: 0,
mouseMoveX: 0,
mouseMoveY: 0,
startX: 0, // start position. translate is applied relative to this.
startY: 0,
};
let isPanning = false;
let ticking = false;
gridEl.addEventListener('mousedown', (e) => {
e.stopPropagation();
position.mouseDownX = e.clientX;
position.mouseDownY = e.clientY;
const bounds = gridEl.getBoundingClientRect();
const parentBounds = parentEl.getBoundingClientRect();
position.startX = bounds.left - parentBounds.left;
position.startY = bounds.top - parentBounds.top;
isPanning = true;
});
gridEl.addEventListener('mousemove', (e) => {
e.stopPropagation();
if (!isPanning) return;
position.mouseMoveX = e.clientX;
position.mouseMoveY = e.clientY;
// rAF code adapted from https://css-tricks.com/debouncing-throttling-explained-examples/
if (!ticking) window.requestAnimationFrame(render);
ticking = true;
});
gridEl.addEventListener('mouseup', (e) => {
e.stopPropagation();
isPanning = false;
ticking = false;
});
const calcDistance = (oldPos, newPos) => {
const xDiff = newPos.x - oldPos.x;
const yDiff = newPos.y - oldPos.y;
return {
xDiff,
yDiff
};
};
const render = () => {
ticking = false;
const pos = position;
const oldPos = {
x: pos.mouseDownX,
y: pos.mouseDownY
};
const newPos = {
x: pos.mouseMoveX,
y: pos.mouseMoveY
};
let {
xDiff,
yDiff
} = calcDistance(oldPos, newPos);
xDiff += pos.startX;
yDiff += pos.startY;
gridEl.style.transform = `translate(${xDiff}px, ${yDiff}px)`;
};