improve pinch to zoom
This commit is contained in:
@@ -230,31 +230,30 @@ export function localeCompare(a: string, b: string): number {
|
|||||||
export function implementPinchZoom(canvas: HTMLElement) {
|
export function implementPinchZoom(canvas: HTMLElement) {
|
||||||
let pinching = false;
|
let pinching = false;
|
||||||
let dragging = false;
|
let dragging = false;
|
||||||
let scrolling = false;
|
|
||||||
let lastElement: HTMLElement | undefined = undefined;
|
let lastElement: HTMLElement | undefined = undefined;
|
||||||
|
|
||||||
let initialDistance: number,
|
let initialDistance: number,
|
||||||
currentZoom: number,
|
currentZoom: number,
|
||||||
initialZoom: number,
|
initialZoom: number,
|
||||||
currentX: number,
|
currentX: number,
|
||||||
currentY: number,
|
currentY: number,
|
||||||
initialX: number,
|
initialX: number,
|
||||||
initialY: number,
|
initialY: number,
|
||||||
initialTouchX: number,
|
initialTouchX: number,
|
||||||
initialTouchY: number;
|
initialTouchY: number;
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
initialDistance = 0;
|
initialDistance = 0;
|
||||||
|
|
||||||
currentZoom = 1;
|
currentZoom = 1;
|
||||||
initialZoom = 1;
|
initialZoom = 1;
|
||||||
|
|
||||||
currentX = 0;
|
currentX = 0;
|
||||||
currentY = 0;
|
currentY = 0;
|
||||||
initialX = 0;
|
initialX = 0;
|
||||||
initialY = 0;
|
initialY = 0;
|
||||||
initialTouchX = 0;
|
initialTouchX = 0;
|
||||||
initialTouchY = 0;
|
initialTouchY = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
@@ -264,60 +263,44 @@ export function implementPinchZoom(canvas: HTMLElement) {
|
|||||||
|
|
||||||
const element = (): HTMLElement | null => canvas.querySelector(".zoom");
|
const element = (): HTMLElement | null => canvas.querySelector(".zoom");
|
||||||
const distance = (e: TouchEvent) =>
|
const distance = (e: TouchEvent) =>
|
||||||
Math.hypot(
|
Math.hypot(
|
||||||
e.touches[0].pageX - e.touches[1].pageX,
|
e.touches[0].pageX - e.touches[1].pageX,
|
||||||
e.touches[0].pageY - e.touches[1].pageY,
|
e.touches[0].pageY - e.touches[1].pageY,
|
||||||
);
|
);
|
||||||
const point = (e: TouchEvent, direction: "x" | "y", i: number) =>
|
const point = (e: TouchEvent, direction: "x" | "y", i: number) =>
|
||||||
e.touches[i][direction == "x" ? "clientX" : "clientY"];
|
e.touches[i][direction == "x" ? "clientX" : "clientY"];
|
||||||
const midpoint = (e: TouchEvent, direction: "x" | "y") =>
|
const midpoint = (e: TouchEvent, direction: "x" | "y") =>
|
||||||
(point(e, direction, 0) + point(e, direction, 1)) / 2;
|
(point(e, direction, 0) + point(e, direction, 1)) / 2;
|
||||||
|
|
||||||
function apply(factor: number, _offset?: [number, number]) {
|
function apply(factor: number, _offset?: [number, number]) {
|
||||||
const el = element();
|
const el = element();
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
if (factor < MIN) return apply(MIN, _offset);
|
if (factor < MIN) return apply(MIN, _offset);
|
||||||
if (factor > MAX) return apply(5, _offset);
|
if (factor > MAX) return apply(5, _offset);
|
||||||
|
|
||||||
let x: number, y: number;
|
let x: number, y: number;
|
||||||
if (_offset) [x, y] = _offset;
|
if (_offset) [x, y] = _offset;
|
||||||
else {
|
else {
|
||||||
x = currentX;
|
x = currentX;
|
||||||
y = currentY;
|
y = currentY;
|
||||||
}
|
}
|
||||||
el.style.transform = `scale(${factor.toString()}) translate(${x}px, ${y}px)`;
|
el.style.transform = `scale(${factor.toString()}) translate(${x}px, ${y}px)`;
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
currentZoom = factor;
|
currentZoom = factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.addEventListener("wheel", (event: WheelEvent) => {
|
canvas.addEventListener("wheel", (event: WheelEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (!scrolling) {
|
initialZoom = currentZoom;
|
||||||
initialZoom = currentZoom;
|
initialX = currentX;
|
||||||
initialX = currentX;
|
initialY = currentY;
|
||||||
initialY = currentY;
|
|
||||||
scrolling = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const canvasWidth = canvas.offsetWidth;
|
apply(currentZoom - event.deltaY * 0.005);
|
||||||
const canvasHeight = canvas.offsetHeight;
|
|
||||||
const initialWidth = canvasWidth * initialZoom;
|
|
||||||
const initialHeight = canvasHeight * initialZoom;
|
|
||||||
const currentWidth = canvasWidth * currentZoom;
|
|
||||||
const currentHeight = canvasHeight * currentZoom;
|
|
||||||
|
|
||||||
const differenceWidth = currentWidth - initialWidth;
|
|
||||||
const differenceHeight = currentHeight - initialHeight;
|
|
||||||
|
|
||||||
apply(currentZoom - event.deltaY * 0.005, [initialX - differenceWidth, initialY - differenceHeight]);
|
|
||||||
});
|
});
|
||||||
canvas.addEventListener("scrollend", () => {
|
|
||||||
scrolling = false;
|
|
||||||
})
|
|
||||||
canvas.addEventListener("scroll", (event: Event) => {
|
canvas.addEventListener("scroll", (event: Event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.addEventListener("mousedown", (event: MouseEvent) => {
|
canvas.addEventListener("mousedown", (event: MouseEvent) => {
|
||||||
@@ -378,14 +361,8 @@ export function implementPinchZoom(canvas: HTMLElement) {
|
|||||||
if (event.touches.length < 2) return;
|
if (event.touches.length < 2) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const currentDistance = distance(event);
|
const currentDistance = distance(event);
|
||||||
const midX = midpoint(event, "x");
|
|
||||||
const midY = midpoint(event, "y");
|
|
||||||
const ratio = currentDistance / initialDistance;
|
const ratio = currentDistance / initialDistance;
|
||||||
const difference = currentDistance - initialDistance;
|
apply(initialZoom * ratio);
|
||||||
apply(initialZoom * ratio, [
|
|
||||||
initialX + (midX - difference - initialTouchX) / initialZoom,
|
|
||||||
initialY + (midY - difference - initialTouchY) / initialZoom,
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,19 +25,6 @@
|
|||||||
padding: var(--content-padding);
|
padding: var(--content-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat-page > div > #main > #task-page .zoom {
|
|
||||||
display: flex;
|
|
||||||
align-items: start;
|
|
||||||
gap: var(--large-gap);
|
|
||||||
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
|
|
||||||
transform-origin: top left;
|
|
||||||
|
|
||||||
padding: var(--content-padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
#board-content .main-content {
|
#board-content .main-content {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import ConnectionModel from "../../Model/Global/connectionModel";
|
|||||||
import ChatListModel from "../../Model/Chat/chatListModel";
|
import ChatListModel from "../../Model/Chat/chatListModel";
|
||||||
|
|
||||||
export default class CoreViewModel {
|
export default class CoreViewModel {
|
||||||
readonly BUILD = "Build 26.09.24.B";
|
readonly BUILD = "Build 26.09.24.C";
|
||||||
|
|
||||||
translations: Translations;
|
translations: Translations;
|
||||||
|
|
||||||
|
|||||||
@@ -97,3 +97,18 @@ body[theme="dark"] #background-wrapper #grass-2 {
|
|||||||
--color-1: hsl(110, 60%, 30%);
|
--color-1: hsl(110, 60%, 30%);
|
||||||
--color-2: hsl(110, 50%, 24%);
|
--color-2: hsl(110, 50%, 24%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Zoom */
|
||||||
|
.zoom {
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
gap: var(--large-gap);
|
||||||
|
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
transform-origin: center;
|
||||||
|
|
||||||
|
padding: var(--content-padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user