onnx-web-upscale / src /composables /useCompareStage.ts
notaneimu's picture
update preview, dowscale options
7083032
import { computed, onMounted, onUnmounted, reactive, type Ref } from "vue";
import { clamp, round } from "../lib/onnxHelpers";
export function useCompareStage(
compareStageRef: Ref<HTMLElement | null>,
hasBaseImage: Ref<boolean>,
hasOverlayImage: Ref<boolean>,
) {
const state = reactive({
splitPercent: 50,
zoom: 1,
originX: 50,
originY: 50,
dragging: false,
panning: false,
panStartX: 0,
panStartY: 0,
panOriginStartX: 50,
panOriginStartY: 50,
});
const adjustedSplitPercent = computed(() => {
const splitRatio = state.splitPercent / 100;
const originRatio = state.originX / 100;
const zoom = Math.max(1, state.zoom);
const adjustedRatio = (splitRatio - (1 - zoom) * originRatio) / zoom;
return round(clamp(adjustedRatio, 0, 1) * 100) ?? 50;
});
const styleVars = computed(() => ({
"--split": `${state.splitPercent}%`,
"--split-adjusted": `${adjustedSplitPercent.value}%`,
"--zoom": String(state.zoom),
"--origin-x": `${state.originX}%`,
"--origin-y": `${state.originY}%`,
}));
function startCompareDrag(event: PointerEvent) {
if (!hasBaseImage.value || !compareStageRef.value) {
return;
}
if (event.button === 2 && state.zoom > 1) {
event.preventDefault();
state.panning = true;
state.panStartX = event.clientX;
state.panStartY = event.clientY;
state.panOriginStartX = state.originX;
state.panOriginStartY = state.originY;
compareStageRef.value.setPointerCapture?.(event.pointerId);
compareStageRef.value.style.cursor = "grabbing";
return;
}
if (event.button === 0 && hasOverlayImage.value) {
state.dragging = true;
compareStageRef.value.setPointerCapture?.(event.pointerId);
updateSplitFromPointer(event);
}
}
function updateSplitFromPointer(event: PointerEvent) {
const rect = compareStageRef.value?.getBoundingClientRect();
if (!rect?.width) {
return;
}
const ratio = (event.clientX - rect.left) / rect.width;
state.splitPercent = clamp(ratio * 100, 0, 100);
}
function updatePanFromPointer(event: PointerEvent) {
const rect = compareStageRef.value?.getBoundingClientRect();
if (!rect?.width || !rect.height) {
return;
}
const deltaX = event.clientX - state.panStartX;
const deltaY = event.clientY - state.panStartY;
const zoom = state.zoom;
const deltaXPercent = ((deltaX / rect.width) / zoom) * 100;
const deltaYPercent = ((deltaY / rect.height) / zoom) * 100;
state.originX = clamp(state.panOriginStartX - deltaXPercent, 0, 100);
state.originY = clamp(state.panOriginStartY - deltaYPercent, 0, 100);
}
function handlePointerMove(event: PointerEvent) {
if (state.panning) {
updatePanFromPointer(event);
return;
}
if (state.dragging) {
updateSplitFromPointer(event);
}
}
function stopCompareDrag() {
if (state.panning) {
state.panning = false;
if (compareStageRef.value) {
compareStageRef.value.style.cursor = "";
}
return;
}
state.dragging = false;
}
function preventContextMenu(event: MouseEvent) {
if (state.zoom > 1) {
event.preventDefault();
}
}
function handleWheel(event: WheelEvent) {
if (!hasBaseImage.value) {
return;
}
const rect = compareStageRef.value?.getBoundingClientRect();
if (!rect?.width || !rect.height) {
return;
}
event.preventDefault();
const mouseX = (event.clientX - rect.left) / rect.width;
const mouseY = (event.clientY - rect.top) / rect.height;
const currentZoom = state.zoom;
const originXN = state.originX / 100;
const originYN = state.originY / 100;
const contentX = (mouseX - originXN * (1 - currentZoom)) / currentZoom;
const contentY = (mouseY - originYN * (1 - currentZoom)) / currentZoom;
const multiplier = event.deltaY < 0 ? 1.12 : 0.9;
const newZoom = clamp((round(currentZoom * multiplier) ?? currentZoom), 1, 6);
let newOriginXN = originXN;
let newOriginYN = originYN;
if (newZoom !== 1) {
newOriginXN = (mouseX - contentX * newZoom) / (1 - newZoom);
newOriginYN = (mouseY - contentY * newZoom) / (1 - newZoom);
}
state.originX = clamp(newOriginXN * 100, 0, 100);
state.originY = clamp(newOriginYN * 100, 0, 100);
state.zoom = newZoom;
}
onMounted(() => {
window.addEventListener("pointermove", handlePointerMove);
window.addEventListener("pointerup", stopCompareDrag);
});
onUnmounted(() => {
window.removeEventListener("pointermove", handlePointerMove);
window.removeEventListener("pointerup", stopCompareDrag);
});
return {
styleVars,
zoomScale: computed(() => state.zoom),
zoomLabel: computed(() => `${Math.round(state.zoom * 100)}%`),
startCompareDrag,
preventContextMenu,
handleWheel,
};
}