mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-02 16:52:31 +00:00
global map cursor logic
This commit is contained in:
@@ -7,6 +7,7 @@ import { addSelectItem, selectItem, selection } from "$lib/components/file-list/
|
|||||||
import { ListTrackSegmentItem, type ListItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList";
|
import { ListTrackSegmentItem, type ListItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList";
|
||||||
import type { Waypoint } from "gpx";
|
import type { Waypoint } from "gpx";
|
||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
|
import { resetCursor, setGrabbingCursor, setPointerCursor } from "$lib/utils";
|
||||||
|
|
||||||
let defaultWeight = 5;
|
let defaultWeight = 5;
|
||||||
let defaultOpacity = 0.6;
|
let defaultOpacity = 0.6;
|
||||||
@@ -121,8 +122,8 @@ export class GPXLayer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.map.on('click', this.fileId, this.selectOnClickBinded);
|
this.map.on('click', this.fileId, this.selectOnClickBinded);
|
||||||
this.map.on('mouseenter', this.fileId, toPointerCursor);
|
this.map.on('mouseenter', this.fileId, setPointerCursor);
|
||||||
this.map.on('mouseleave', this.fileId, toDefaultCursor);
|
this.map.on('mouseleave', this.fileId, resetCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get(directionMarkers)) {
|
if (get(directionMarkers)) {
|
||||||
@@ -194,12 +195,12 @@ export class GPXLayer {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
marker.on('dragstart', () => {
|
marker.on('dragstart', () => {
|
||||||
this.map.getCanvas().style.cursor = 'grabbing';
|
setGrabbingCursor();
|
||||||
marker.getElement().style.cursor = 'grabbing';
|
marker.getElement().style.cursor = 'grabbing';
|
||||||
this.hideWaypointPopup();
|
this.hideWaypointPopup();
|
||||||
});
|
});
|
||||||
marker.on('dragend', (e) => {
|
marker.on('dragend', (e) => {
|
||||||
this.map.getCanvas().style.cursor = '';
|
resetCursor();
|
||||||
marker.getElement().style.cursor = '';
|
marker.getElement().style.cursor = '';
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
return produce(file, (draft) => {
|
return produce(file, (draft) => {
|
||||||
@@ -229,8 +230,8 @@ export class GPXLayer {
|
|||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
this.map.off('click', this.fileId, this.selectOnClickBinded);
|
this.map.off('click', this.fileId, this.selectOnClickBinded);
|
||||||
this.map.off('mouseenter', this.fileId, toPointerCursor);
|
this.map.off('mouseenter', this.fileId, setPointerCursor);
|
||||||
this.map.off('mouseleave', this.fileId, toDefaultCursor);
|
this.map.off('mouseleave', this.fileId, resetCursor);
|
||||||
this.map.off('style.load', this.updateBinded);
|
this.map.off('style.load', this.updateBinded);
|
||||||
|
|
||||||
if (this.map.getLayer(this.fileId + '-direction')) {
|
if (this.map.getLayer(this.fileId + '-direction')) {
|
||||||
@@ -346,11 +347,3 @@ export class GPXLayer {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toPointerCursor() {
|
|
||||||
get(map).getCanvas().style.cursor = 'pointer';
|
|
||||||
}
|
|
||||||
|
|
||||||
function toDefaultCursor() {
|
|
||||||
get(map).getCanvas().style.cursor = '';
|
|
||||||
}
|
|
||||||
|
@@ -11,6 +11,7 @@ import { dbUtils, type GPXFileWithStatistics } from "$lib/db";
|
|||||||
import { selection } from "$lib/components/file-list/Selection";
|
import { selection } from "$lib/components/file-list/Selection";
|
||||||
import { ListFileItem, ListTrackSegmentItem } from "$lib/components/file-list/FileList";
|
import { ListFileItem, ListTrackSegmentItem } from "$lib/components/file-list/FileList";
|
||||||
import { currentTool, Tool } from "$lib/stores";
|
import { currentTool, Tool } from "$lib/stores";
|
||||||
|
import { resetCursor, setCrosshairCursor, setGrabbingCursor } from "$lib/utils";
|
||||||
|
|
||||||
export class RoutingControls {
|
export class RoutingControls {
|
||||||
active: boolean = false;
|
active: boolean = false;
|
||||||
@@ -78,6 +79,7 @@ export class RoutingControls {
|
|||||||
this.map.on('move', this.toggleAnchorsForZoomLevelAndBoundsBinded);
|
this.map.on('move', this.toggleAnchorsForZoomLevelAndBoundsBinded);
|
||||||
this.map.on('click', this.appendAnchorBinded);
|
this.map.on('click', this.appendAnchorBinded);
|
||||||
this.map.on('mousemove', this.fileId, this.showTemporaryAnchorBinded);
|
this.map.on('mousemove', this.fileId, this.showTemporaryAnchorBinded);
|
||||||
|
setCrosshairCursor();
|
||||||
|
|
||||||
this.fileUnsubscribe = this.file.subscribe(this.updateControls.bind(this));
|
this.fileUnsubscribe = this.file.subscribe(this.updateControls.bind(this));
|
||||||
}
|
}
|
||||||
@@ -126,6 +128,7 @@ export class RoutingControls {
|
|||||||
this.map.off('click', this.appendAnchorBinded);
|
this.map.off('click', this.appendAnchorBinded);
|
||||||
this.map.off('mousemove', this.fileId, this.showTemporaryAnchorBinded);
|
this.map.off('mousemove', this.fileId, this.showTemporaryAnchorBinded);
|
||||||
this.map.off('mousemove', this.updateTemporaryAnchorBinded);
|
this.map.off('mousemove', this.updateTemporaryAnchorBinded);
|
||||||
|
resetCursor();
|
||||||
|
|
||||||
this.fileUnsubscribe();
|
this.fileUnsubscribe();
|
||||||
}
|
}
|
||||||
@@ -152,13 +155,13 @@ export class RoutingControls {
|
|||||||
let lastDragEvent = 0;
|
let lastDragEvent = 0;
|
||||||
marker.on('dragstart', (e) => {
|
marker.on('dragstart', (e) => {
|
||||||
lastDragEvent = Date.now();
|
lastDragEvent = Date.now();
|
||||||
this.map.getCanvas().style.cursor = 'grabbing';
|
setGrabbingCursor();
|
||||||
element.classList.remove('cursor-pointer');
|
element.classList.remove('cursor-pointer');
|
||||||
element.classList.add('cursor-grabbing');
|
element.classList.add('cursor-grabbing');
|
||||||
});
|
});
|
||||||
marker.on('dragend', (e) => {
|
marker.on('dragend', (e) => {
|
||||||
lastDragEvent = Date.now();
|
lastDragEvent = Date.now();
|
||||||
this.map.getCanvas().style.cursor = '';
|
resetCursor();
|
||||||
element.classList.remove('cursor-grabbing');
|
element.classList.remove('cursor-grabbing');
|
||||||
element.classList.add('cursor-pointer');
|
element.classList.add('cursor-pointer');
|
||||||
this.moveAnchor(anchor);
|
this.moveAnchor(anchor);
|
||||||
|
@@ -2,61 +2,91 @@ import { type ClassValue, clsx } from "clsx";
|
|||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { cubicOut } from "svelte/easing";
|
import { cubicOut } from "svelte/easing";
|
||||||
import type { TransitionConfig } from "svelte/transition";
|
import type { TransitionConfig } from "svelte/transition";
|
||||||
|
import { get } from "svelte/store";
|
||||||
|
import { map } from "./stores";
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
type FlyAndScaleParams = {
|
type FlyAndScaleParams = {
|
||||||
y?: number;
|
y?: number;
|
||||||
x?: number;
|
x?: number;
|
||||||
start?: number;
|
start?: number;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const flyAndScale = (
|
export const flyAndScale = (
|
||||||
node: Element,
|
node: Element,
|
||||||
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
|
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
|
||||||
): TransitionConfig => {
|
): TransitionConfig => {
|
||||||
const style = getComputedStyle(node);
|
const style = getComputedStyle(node);
|
||||||
const transform = style.transform === "none" ? "" : style.transform;
|
const transform = style.transform === "none" ? "" : style.transform;
|
||||||
|
|
||||||
const scaleConversion = (
|
const scaleConversion = (
|
||||||
valueA: number,
|
valueA: number,
|
||||||
scaleA: [number, number],
|
scaleA: [number, number],
|
||||||
scaleB: [number, number]
|
scaleB: [number, number]
|
||||||
) => {
|
) => {
|
||||||
const [minA, maxA] = scaleA;
|
const [minA, maxA] = scaleA;
|
||||||
const [minB, maxB] = scaleB;
|
const [minB, maxB] = scaleB;
|
||||||
|
|
||||||
const percentage = (valueA - minA) / (maxA - minA);
|
const percentage = (valueA - minA) / (maxA - minA);
|
||||||
const valueB = percentage * (maxB - minB) + minB;
|
const valueB = percentage * (maxB - minB) + minB;
|
||||||
|
|
||||||
return valueB;
|
return valueB;
|
||||||
};
|
};
|
||||||
|
|
||||||
const styleToString = (
|
const styleToString = (
|
||||||
style: Record<string, number | string | undefined>
|
style: Record<string, number | string | undefined>
|
||||||
): string => {
|
): string => {
|
||||||
return Object.keys(style).reduce((str, key) => {
|
return Object.keys(style).reduce((str, key) => {
|
||||||
if (style[key] === undefined) return str;
|
if (style[key] === undefined) return str;
|
||||||
return str + `${key}:${style[key]};`;
|
return str + `${key}:${style[key]};`;
|
||||||
}, "");
|
}, "");
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
duration: params.duration ?? 200,
|
duration: params.duration ?? 200,
|
||||||
delay: 0,
|
delay: 0,
|
||||||
css: (t) => {
|
css: (t) => {
|
||||||
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
|
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
|
||||||
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
|
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
|
||||||
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
|
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
|
||||||
|
|
||||||
return styleToString({
|
return styleToString({
|
||||||
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
|
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
|
||||||
opacity: t
|
opacity: t
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
easing: cubicOut
|
easing: cubicOut
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let previousCursors: string[] = [];
|
||||||
|
export function setCursor(cursor: string) {
|
||||||
|
let m = get(map);
|
||||||
|
if (m) {
|
||||||
|
previousCursors.push(m.getCanvas().style.cursor);
|
||||||
|
m.getCanvas().style.cursor = cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetCursor() {
|
||||||
|
let m = get(map);
|
||||||
|
if (m) {
|
||||||
|
m.getCanvas().style.cursor = previousCursors.pop() ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setPointerCursor() {
|
||||||
|
setCursor('pointer');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setGrabbingCursor() {
|
||||||
|
setCursor('grabbing');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCrosshairCursor() {
|
||||||
|
setCursor('crosshair');
|
||||||
|
}
|
Reference in New Issue
Block a user