Skip to content

Commit

Permalink
Properly support zooming
Browse files Browse the repository at this point in the history
Known issues:
- Zooming will fight with tools that don't cancel after 1 second
- The scrollbar takes up a small amount of width, which means there will always
  be a bit of horizontal scrolling.

TODO: Move more tool logic to the tool themselves, including zooming.
  • Loading branch information
Semphriss committed Jun 14, 2024
1 parent 5133c5c commit bf02bb2
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
2 changes: 2 additions & 0 deletions www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
overflow: scroll;
margin-bottom: 64px;
margin-top: 32px;
/* Prevent the default zooming mechanism */
touch-action: pan-x pan-y;
}

#loading {
Expand Down
70 changes: 70 additions & 0 deletions www/js/main.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -285,5 +285,75 @@ docname.addEventListener('keypress', (e) => {
const resizeJob = new DelayJob(() => { if (doc) doc.refresh(); });
window.addEventListener('resize', () => void resizeJob.run());

// Zooming
let currentZoom = 1.0;
let currentLen = null;
let currentPanMid = null;
const scrollTarget = document.body.parentElement;

pages.addEventListener('touchstart', e => {
if (e.touches.length == 2) {
const offset = { x: e.touches[0].pageX - e.touches[1].pageX,
y: e.touches[0].pageY - e.touches[1].pageY };
currentLen = Math.sqrt(Math.pow(offset.x, 2) + Math.pow(offset.y, 2));

// All code below is to initialize currentPanMid for panning
const midpoint = { x: (e.touches[0].pageX + e.touches[1].pageX) / 2,
y: (e.touches[0].pageY + e.touches[1].pageY) / 2 };
const currScroll = { x: scrollTarget.scrollLeft,
y: scrollTarget.scrollTop };
const currScrollMid = { x: midpoint.x - currScroll.x,
y: midpoint.y - currScroll.y };
currentPanMid = currScrollMid;
}
});

pages.addEventListener('touchmove', e => {
if (e.touches.length == 2 && currentLen) {
const offset = { x: e.touches[0].pageX - e.touches[1].pageX,
y: e.touches[0].pageY - e.touches[1].pageY };
const newLen = Math.sqrt(Math.pow(offset.x, 2) + Math.pow(offset.y, 2));
const oldZoom = currentZoom;
currentZoom *= newLen / currentLen;
currentZoom = Math.max(currentZoom, 1.0);

// Without this midpoint math madness, zooming will send the user towards
// or away from the origin point at the complete top of the document.
const currScroll = { x: scrollTarget.scrollLeft,
y: scrollTarget.scrollTop };
const newScroll = { x: currScroll.x * currentZoom / oldZoom,
y: currScroll.y * currentZoom / oldZoom };

// The above is enough to zoom relatively to the top left corner of the
// screen, but we want to zoom relatively to the middle of the pinch.
const midpoint = { x: (e.touches[0].pageX + e.touches[1].pageX) / 2,
y: (e.touches[0].pageY + e.touches[1].pageY) / 2 };
const currScrollMid = { x: midpoint.x - currScroll.x,
y: midpoint.y - currScroll.y };
const newScrollMid = { x: currScrollMid.x * currentZoom / oldZoom,
y: currScrollMid.y * currentZoom / oldZoom, };
const newMidOffset = { x: newScrollMid.x - currScrollMid.x,
y: newScrollMid.y - currScrollMid.y };
newScroll.x += newMidOffset.x;
newScroll.y += newMidOffset.y;

// While we're at it, enable panning with two fingers.
newScroll.x -= currScrollMid.x - currentPanMid.x;
newScroll.y -= currScrollMid.y - currentPanMid.y;

currentLen = newLen;
currentPanMid = currScrollMid;
pages.style.width = (currentZoom * 100) + 'vw';
document.body.parentElement.scrollLeft = newScroll.x;
document.body.parentElement.scrollTop = newScroll.y;
}
});

pages.addEventListener('touchend', e => {
if (e.touches.length < 2) {
currentLen = null;
}
});

// Initialize the app
showMenu();

0 comments on commit bf02bb2

Please sign in to comment.