Trying new pinch to zoom mode
This commit is contained in:
parent
4b901a87d7
commit
1c9812c3a8
10
app.vue
10
app.vue
|
|
@ -6,6 +6,8 @@ function toggleLeftPanel(_: Event): void {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
const { path } = useRoute();
|
||||||
|
|
||||||
const { data: tags } = await useAsyncData('descriptions', queryContent('/tags').findOne);
|
const { data: tags } = await useAsyncData('descriptions', queryContent('/tags').findOne);
|
||||||
|
|
||||||
provide('tags/descriptions', tags);
|
provide('tags/descriptions', tags);
|
||||||
|
|
@ -14,7 +16,7 @@ onMounted(() => {
|
||||||
icon = document.querySelector('.site-nav-bar .clickable-icon');
|
icon = document.querySelector('.site-nav-bar .clickable-icon');
|
||||||
icon?.removeEventListener('click', toggleLeftPanel);
|
icon?.removeEventListener('click', toggleLeftPanel);
|
||||||
icon?.addEventListener('click', toggleLeftPanel);
|
icon?.addEventListener('click', toggleLeftPanel);
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -29,9 +31,9 @@ onMounted(() => {
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="gap-3">
|
<div class="gap-3">
|
||||||
<NuxtLink class="site-nav-bar-text" aria-label="Accueil" :href="'/'">Accueil</NuxtLink>
|
<NuxtLink class="site-nav-bar-text" aria-label="Accueil" :href="'/'" ><img src="" /></NuxtLink>
|
||||||
<NuxtLink class="site-nav-bar-text" aria-label="Systeme" :href="'/explorer'">Systeme</NuxtLink>
|
<NuxtLink class="site-nav-bar-text" aria-label="Systeme" :href="'/explorer'" :class="{'mod-active': path.startsWith('/explorer')}">Systeme</NuxtLink>
|
||||||
<NuxtLink class="site-nav-bar-text" aria-label="Outils" :href="'/tools'">Outils</NuxtLink>
|
<NuxtLink class="site-nav-bar-text" aria-label="Outils" :href="'/tools'" :class="{'mod-active': path.startsWith('/tools')}">Outils</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -51,24 +51,24 @@
|
||||||
|
|
||||||
.gap-1 > *
|
.gap-1 > *
|
||||||
{
|
{
|
||||||
padding-left: .4em;
|
margin-left: .4em;
|
||||||
padding-right: .4em;
|
margin-right: .4em;
|
||||||
}
|
}
|
||||||
.gap-1 > *:first-child, .gap-2 > *:first-child, .gap-3 > *:first-child
|
.gap-1 > *:first-child, .gap-2 > *:first-child, .gap-3 > *:first-child
|
||||||
{
|
{
|
||||||
padding-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
.gap-1 > *:last-child, .gap-2 > *:last-child, .gap-3 > *:last-child
|
.gap-1 > *:last-child, .gap-2 > *:last-child, .gap-3 > *:last-child
|
||||||
{
|
{
|
||||||
padding-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
.gap-2 > *
|
.gap-2 > *
|
||||||
{
|
{
|
||||||
padding-left: .8em;
|
margin-left: .8em;
|
||||||
padding-right: .8em;
|
margin-right: .8em;
|
||||||
}
|
}
|
||||||
.gap-3 > *
|
.gap-3 > *
|
||||||
{
|
{
|
||||||
padding-left: 1.2em;
|
margin-left: 1.2em;
|
||||||
padding-right: 1.2em;
|
margin-right: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
--site-name-color-hover: var(--text-muted);
|
--site-name-color-hover: var(--text-muted);
|
||||||
--site-name-font: inherit;
|
--site-name-font: inherit;
|
||||||
--site-name-size: 18px;
|
--site-name-size: 18px;
|
||||||
--site-name-weight: var(--font-medium);
|
--site-name-weight: var(--font-normal);
|
||||||
--site-menu-icon-color: var(--text-faint);
|
--site-menu-icon-color: var(--text-faint);
|
||||||
--site-menu-icon-color-hover: var(--text-normal);
|
--site-menu-icon-color-hover: var(--text-normal);
|
||||||
--site-menu-icon-size: 24px;
|
--site-menu-icon-size: 24px;
|
||||||
|
|
@ -4983,3 +4983,8 @@ body {
|
||||||
.site-nav-bar > div:last-child {
|
.site-nav-bar > div:last-child {
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.site-nav-bar-text.mod-active {
|
||||||
|
padding-bottom: .5em;
|
||||||
|
border-bottom: 3px solid var(--nav-item-border-color-active);
|
||||||
|
}
|
||||||
|
|
@ -31,10 +31,12 @@ const results = computed(() => {
|
||||||
</div>
|
</div>
|
||||||
<Teleport to="body" v-if="input !== ''">
|
<Teleport to="body" v-if="input !== ''">
|
||||||
<div class="search-results" :style="{top: (pos.bottom + 4) + 'px', left: pos.left + 'px', width: pos.width + 'px'}">
|
<div class="search-results" :style="{top: (pos.bottom + 4) + 'px', left: pos.left + 'px', width: pos.width + 'px'}">
|
||||||
<div class="suggestion-item" v-if="results.length > 0" v-for="result of results" :key="result._path" @mouseenter="(e) => (e.target as HTMLElement).classList.add('is-selected')" @mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')" @mousedown="navigateTo('/explorer' + result._path); input = ''">
|
<div class="suggestion-item" v-if="results.length > 0" v-for="result of results" :key="result._path" @mouseenter="(e) => (e.target as HTMLElement).classList.add('is-selected')" @mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')" @mousedown.prevent="navigateTo('/explorer' + result._path); input = ''">
|
||||||
<div class="suggestion-content">
|
<div class="suggestion-content">
|
||||||
<div class="suggestion-title">
|
<div class="suggestion-title">
|
||||||
|
<ProseA :href="result._path">
|
||||||
{{ result.title.substring(0, clear(result.title).indexOf(clear(input))) }}<span class="suggestion-highlight">{{ result.title.substring(clear(result.title).indexOf(clear(input)), clear(result.title).indexOf(clear(input)) + clear(input).length + 1) }}</span>{{ result.title.substring(clear(result.title).indexOf(clear(input)) + clear(input).length + 1) }}
|
{{ result.title.substring(0, clear(result.title).indexOf(clear(input))) }}<span class="suggestion-highlight">{{ result.title.substring(clear(result.title).indexOf(clear(input)), clear(result.title).indexOf(clear(input)) + clear(input).length + 1) }}</span>{{ result.title.substring(clear(result.title).indexOf(clear(input)) + clear(input).length + 1) }}
|
||||||
|
</ProseA>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ function darken(rgb: string): boolean
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const classes: any = { 'canvas-node-group': props.node.type === 'group', 'is-themed': props.node.color !== undefined, 'mod-canvas-color-custom': (props.node?.color?.startsWith('#') ?? false) };
|
const classes: Record<string, boolean> = { 'canvas-node-group': props.node.type === 'group', 'is-themed': props.node.color !== undefined, 'mod-canvas-color-custom': (props.node?.color?.startsWith('#') ?? false) };
|
||||||
const size = Math.max(props.node.width, props.node.height);
|
const size = Math.max(props.node.width, props.node.height);
|
||||||
|
|
||||||
if(props.node.color !== undefined)
|
if(props.node.color !== undefined)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ let centerX = ref(0), centerY = ref(0), canvas = ref<HTMLDivElement>();
|
||||||
let minX = ref(+Infinity), minY = ref(+Infinity), maxX = ref(-Infinity), maxY = ref(-Infinity);
|
let minX = ref(+Infinity), minY = ref(+Infinity), maxX = ref(-Infinity), maxY = ref(-Infinity);
|
||||||
let bbox = ref<DOMRect>();
|
let bbox = ref<DOMRect>();
|
||||||
|
|
||||||
|
let lastPinchLength = 0;
|
||||||
|
|
||||||
let _minX = +Infinity, _minY = +Infinity, _maxX = -Infinity, _maxY = -Infinity;
|
let _minX = +Infinity, _minY = +Infinity, _maxX = -Infinity, _maxY = -Infinity;
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
@ -66,7 +68,7 @@ const onPointerDown = (event: PointerEvent) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPointerMove = (event: PointerEvent) => {
|
const onPointerMove = (event: PointerEvent) => {
|
||||||
if (event.isPrimary === false) return;
|
if (event.isPrimary === false || dragging === false) return;
|
||||||
dispX.value -= (posX - event.clientX) / zoom.value;
|
dispX.value -= (posX - event.clientX) / zoom.value;
|
||||||
dispY.value -= (posY - event.clientY) / zoom.value;
|
dispY.value -= (posY - event.clientY) / zoom.value;
|
||||||
|
|
||||||
|
|
@ -79,11 +81,39 @@ const onPointerUp = (event: PointerEvent) => {
|
||||||
dragging = false;
|
dragging = false;
|
||||||
document.removeEventListener('pointermove', onPointerMove);
|
document.removeEventListener('pointermove', onPointerMove);
|
||||||
document.removeEventListener('pointerup', onPointerUp);
|
document.removeEventListener('pointerup', onPointerUp);
|
||||||
document.removeEventListener('pointercancel', onPointerUp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onWheel = (event: WheelEvent) => {
|
const onWheel = (event: WheelEvent) => {
|
||||||
zoom.value = clamp(zoom.value * 1 + (event.deltaY * -0.001), minZoom.value, 3);
|
zoom.value = clamp(zoom.value + (event.deltaY * -0.001), minZoom.value, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onTouchStart = (event: TouchEvent) => {
|
||||||
|
if(event.touches?.length === 2)
|
||||||
|
{
|
||||||
|
dragging = false;
|
||||||
|
lastPinchLength = length(event.touches[0].clientX, event.touches[0].clientY, event.touches[1].clientX, event.touches[1].clientY);
|
||||||
|
|
||||||
|
document.addEventListener('touchmove', onTouchMove);
|
||||||
|
document.addEventListener('touchend', onTouchEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onTouchEnd = (event: TouchEvent) => {
|
||||||
|
if(event.touches?.length !== 2)
|
||||||
|
dragging = true;
|
||||||
|
|
||||||
|
document.removeEventListener('touchmove', onTouchMove);
|
||||||
|
document.removeEventListener('touchend', onTouchEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onTouchMove = (event: TouchEvent) => {
|
||||||
|
if(event.touches?.length === 2)
|
||||||
|
{
|
||||||
|
const l = length(event.touches[0].clientX, event.touches[0].clientY, event.touches[1].clientX, event.touches[1].clientY);
|
||||||
|
zoom.value = clamp(zoom.value + ((lastPinchLength - l) * -0.01), minZoom.value, 3);
|
||||||
|
|
||||||
|
lastPinchLength = l;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reset = (_: MouseEvent) => {
|
const reset = (_: MouseEvent) => {
|
||||||
|
|
@ -98,6 +128,9 @@ function clamp(x: number, min: number, max: number): number {
|
||||||
return min;
|
return min;
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
function length(x1: number, y1: number, x2: number, y2: number): number {
|
||||||
|
return Math.sqrt((x2 - x1)^2 + (y2 - y1)^2);
|
||||||
|
}
|
||||||
function edgePos(side: 'bottom' | 'top' | 'left' | 'right', pos: { x: number, y: number }, n: number): { x: number, y: number } {
|
function edgePos(side: 'bottom' | 'top' | 'left' | 'right', pos: { x: number, y: number }, n: number): { x: number, y: number } {
|
||||||
switch (side) {
|
switch (side) {
|
||||||
case "left":
|
case "left":
|
||||||
|
|
@ -180,7 +213,7 @@ function getCenter(n: { x: number, y: number }, i: { x: number, y: number }, r:
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="canvas" @pointerdown="onPointerDown" @wheel.passive="onWheel" @touchstart.prevent="" @dragstart.prevent=""
|
<div ref="canvas" @pointerdown="onPointerDown" @wheel.passive="onWheel" @touchstart.prevent="onTouchStart" @dragstart.prevent=""
|
||||||
class="canvas-wrapper node-insert-event mod-zoomed-out"
|
class="canvas-wrapper node-insert-event mod-zoomed-out"
|
||||||
:style="{ '--zoom-multiplier': (1 / Math.pow(zoom, 0.7)) }">
|
:style="{ '--zoom-multiplier': (1 / Math.pow(zoom, 0.7)) }">
|
||||||
<div class="canvas-controls" style="z-index: 999;">
|
<div class="canvas-controls" style="z-index: 999;">
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ export default defineNuxtConfig({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
router: {
|
||||||
|
options: {
|
||||||
|
scrollBehaviorType: 'smooth'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
css: ['~/assets/common.css', '~/assets/global.css'],
|
css: ['~/assets/common.css', '~/assets/global.css'],
|
||||||
|
|
||||||
content: {
|
content: {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue