Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/src/.vuepress/components/HtmlDemo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@
:touchEnabled="touchEnabled" :minZoom="0.3" :maxZoom="2" :dblClickZoomStep="0.4" :wheelZoomStep="0.01"
:enableControllButton="enableControllButton" :enableWheelOnKey="documentFlow ? 'Control' : undefined"
@zoom="showEvent" @panned="showEvent">
<VueZoomable
style="width: 500px; height: 500px; border: 1px solid black"
:zoomEnabled="zoomEnabled"
:panEnabled="panEnabled"
selector="#boxes"
:dblClickEnabled="dbClickEnabled"
:wheelEnabled="mouseWheelZoomEnabled"
:touchEnabled="touchEnabled"
:minZoom="0.3"
:maxZoom="2"
:dblClickZoomStep="0.4"
:wheelZoomStep="0.01"
:enableControllButton="enableControllButton"
:wheelKey="documentFlow ? 'Control' : undefined"
@zoom="showEvent"
@panned="showEvent"
>
<div id="boxes">
<div>
<div></div>
Expand Down
270 changes: 168 additions & 102 deletions docs/src/.vuepress/components/ReactivityDemo.vue

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions docs/src/.vuepress/components/SvgDemo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@
:minZoom="0.3" :maxZoom="2" :dblClickZoomStep="0.4" :wheelZoomStep="0.01"
:enableWheelOnKey="documentFlow ? 'Control' : undefined" :enableControllButton="enableControllButton"
@zoom="showEvent" @panned="showEvent">
<VueZoomable
style="width: 500px; height: 500px; border: 1px solid black"
selector="#container1"
:zoomEnabled="zoomEnabled"
:panEnabled="panEnabled"
:initialPanX="100"
:initialPanY="120"
:initialZoom="1.5"
:svgChild="true"
:dblClickEnabled="dbClickEnabled"
:wheelEnabled="mouseWheelZoomEnabled"
:touchEnabled="touchEnabled"
:minZoom="0.3"
:maxZoom="2"
:dblClickZoomStep="0.4"
:wheelZoomStep="0.01"
:wheelKey="documentFlow ? 'Control' : undefined"
:enableControllButton="enableControllButton"
@zoom="showEvent"
@panned="showEvent"
>
<svg class="mysvg" v-if="visible">
<g id="container1">
<circle x="10" y="10" r="50" />
Expand Down
82 changes: 48 additions & 34 deletions src/components/ScrollOverlay.vue
Original file line number Diff line number Diff line change
@@ -1,67 +1,81 @@
<!--
<!--
This is heavily inspired by:
https://developers.google.com/maps/documentation/javascript/examples/control-default

Thanks google <333
-->

<template>
<div class="overlay" :class="{ hidden: hideOverlay }">
<p>Use '{{ props.enableWheelOnKey }}' + 'scroll' to zoom</p>
<div class="scroll-overlay">
<div
v-if="props.wheelEnabled"
class="overlay"
:class="{ hidden: hideOverlay }"
>
<p>Use '{{ props.wheelKey }}' + 'scroll' to zoom</p>
</div>
<slot />
</div>
</template>

<script setup lang="ts">
import { inject, ref, Ref } from 'vue';
import { inject, ref, Ref } from "vue";

const props = defineProps({
enableWheelOnKey: {
type: String,
default: undefined,
}
})
wheelKey: {
type: String,
default: undefined,
},
wheelEnabled: {
type: Boolean,
},
});

interface Injection {
hideOverlay: Ref<boolean>
hideOverlay: Ref<boolean>;
}

const { hideOverlay } = inject<Injection>('hideOverlay') as Injection;
const { hideOverlay } = inject<Injection>("hideOverlay") as Injection;
</script>

<style scoped>
.scroll-overlay {
position: relative;
}
.overlay {
position: absolute;
position: absolute;
z-index: 9999;

top: 0;
right: 0;
bottom: 0;
left: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;

background: rgba(0, 0, 0, 0.45);
pointer-events: none;
background: rgba(0, 0, 0, 0.45);
pointer-events: none;

opacity: 1;
transition-duration: .3s;
opacity: 1;
transition-duration: 0.3s;
}

.overlay.hidden {
transition-duration: .8s;
opacity: 0;
transition-duration: 0.8s;
opacity: 0;
}

.overlay p {
color: white;
font-family: sans-serif;
font-size: 22px;
color: white;
font-family: sans-serif;
font-size: 22px;

position: relative;
margin: 0;
top: 50%;
-o-transform: translateY(-50%);
transform: translateY(-50%);
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
position: relative;
margin: 0;
top: 50%;
-o-transform: translateY(-50%);
transform: translateY(-50%);
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);

text-align: center;
text-align: center;
}
</style>
</style>
87 changes: 51 additions & 36 deletions src/components/VueZoomable.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
<template>
<div ref="container" class="container" :class="$style.container" @mousedown="onMouseDown" @dblclick="mouse.onDblClick"
@touchstart="touch.onTouchStart" @wheel="wheel.onWheel" @mouseleave="onMouseLeave" @mouseenter="onMouseEnter">
<ControllButtons v-if="props.enableControllButton" @button-home="button.onHome" @button-pan="button.onPan"
@button-zoom="button.onZoom" @mousedown="updateHideOverlay(true);"></ControllButtons>
<slot></slot>
</div>
<ScrollOverlay :wheel-key="wheelKey" :wheel-enabled="wheelEnabled">
<div
ref="container"
class="container"
:class="$style.container"
@mousedown="onMouseDown"
@dblclick="mouse.onDblClick"
@touchstart="touch.onTouchStart"
@wheel="wheel.onWheel"
@mouseleave="onMouseLeave"
@mouseenter="onMouseEnter"
>
<ControllButtons
v-if="props.enableControllButton"
@button-home="button.onHome"
@button-pan="button.onPan"
@button-zoom="button.onZoom"
@mousedown="updateHideOverlay(true)"
></ControllButtons>
<slot></slot>
</div>
</ScrollOverlay>
</template>

<script setup lang="ts">
import { computed, ref, Ref, createApp, onMounted, watch } from 'vue';
import { computed, ref, Ref, onMounted, watch, provide } from "vue";
import { useMouse } from "../composables/useMouse";
import { useTouch } from "../composables/useTouch";
import { useWheel } from "../composables/useWheel";
import { useButtons } from "../composables/useButtons";
import ControllButtons from "./ControllButtons.vue";
import ScrollOverlay from './ScrollOverlay.vue';
import ScrollOverlay from "./ScrollOverlay.vue";

const hideOverlay: Ref<boolean> = ref(true);

Expand Down Expand Up @@ -97,14 +113,16 @@ let props = defineProps({
type: Number,
default: 0.1,
},
enableWheelOnKey: {
wheelKey: {
type: String,
default: undefined,
}
},
});

let container = ref();
let transformTarget = computed<HTMLElement>(() => container.value?.querySelector(props.selector))
let transformTarget = computed<HTMLElement>(() =>
container.value?.querySelector(props.selector)
);

let zoom = ref(props.minZoom);
if (props.initialZoom >= props.minZoom && props.initialZoom <= props.maxZoom) {
Expand Down Expand Up @@ -133,8 +151,9 @@ watch(
pan.value.x = props.pan.x;
pan.value.y = props.pan.y;
}
}
, { deep: true });
},
{ deep: true }
);

function emit(name: string, event: ZoomableEvent) {
if (name === "zoom") {
Expand Down Expand Up @@ -165,38 +184,29 @@ watch(
}
);

onMounted(() => {
const placeholder = document.createElement('div');
const scrollOverlayApp = createApp(ScrollOverlay, { enableWheelOnKey: props.enableWheelOnKey });

// needs to be injected before it is mounted
scrollOverlayApp.provide("hideOverlay", { hideOverlay });

scrollOverlayApp.mount(placeholder)
container.value.appendChild(placeholder);

setTransform();
});

provide("hideOverlay", { hideOverlay });

const pressedKeys: Ref<Set<String>> = ref(new Set<String>());

onMounted(() => {
window.addEventListener(
'wheel',
event => {
if (!isInContainer.value || props.enableWheelOnKey !== "Control") return;
"wheel",
(event) => {
if (!isInContainer.value || props.wheelKey !== "Control") return;
if (event.ctrlKey) event.preventDefault();
}, { passive: false },
},
{ passive: false }
);

// track the keys, which are currently pressed
document.addEventListener('keydown', (event) => {
document.addEventListener("keydown", (event) => {
pressedKeys.value.add(event.key);
if (event.key === props.enableWheelOnKey) hideOverlay.value = true;
if (event.key === props.wheelKey) hideOverlay.value = true;
});
document.addEventListener('keyup', (event) => { pressedKeys.value.delete(event.key); });
})
document.addEventListener("keyup", (event) => {
pressedKeys.value.delete(event.key);
});
});

// track if the mouse is in the container
const isInContainer = ref(false);
Expand All @@ -210,8 +220,12 @@ function onMouseLeave() {
isInContainer.value = false;
}

function showOverlay() { hideOverlay.value = false; }
function updateHideOverlay(newHideOverlay: boolean) { hideOverlay.value = newHideOverlay; }
function showOverlay() {
hideOverlay.value = false;
}
function updateHideOverlay(newHideOverlay: boolean) {
hideOverlay.value = newHideOverlay;
}

let mouse = useMouse(props, emit, pan, zoom, updateHideOverlay);
let touch = useTouch(props, emit, pan, zoom, updateHideOverlay);
Expand All @@ -228,6 +242,7 @@ function onMouseDown(event: MouseEvent) {
.container {
overflow: hidden;
position: relative;
min-height: 100%;

transition: transform 0.1s ease-out;

Expand Down
49 changes: 26 additions & 23 deletions src/composables/useWheel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,33 @@ import { Ref } from "vue";
import { useMove } from "./move";

export function useWheel(
props: any,
emit: any,
pan: Ref<{ x: number, y: number }>,
zoom: Ref<number>,
pressedKeys: Ref<Set<String>>,
showOverlay: Function) {
props: any,
emit: any,
pan: Ref<{ x: number; y: number }>,
zoom: Ref<number>,
pressedKeys: Ref<Set<String>>,
showOverlay: Function
) {
const { changeZoom } = useMove(props, emit, pan, zoom, showOverlay);

const { changeZoom } = useMove(props, emit, pan, zoom, showOverlay);


function onWheel(ev: WheelEvent) {
// check if all conditions are met to scroll
if (!props.wheelEnabled || !props.zoomEnabled) return;
if (props.enableWheelOnKey !== undefined && !pressedKeys.value.has(props.enableWheelOnKey)) {
showOverlay();
return;
}

// normalizes the value of ev.deltaY to either be 1 or -1 and multiplies it with the double click zoom step?
const deltaZoom = props.dblClickZoomStep * ev.deltaY / Math.abs(ev.deltaY);
changeZoom(deltaZoom, "wheel");
function onWheel(ev: WheelEvent) {
// check if all conditions are met to scroll
if (!props.wheelEnabled || !props.zoomEnabled) return;
if (
props.wheelKey !== undefined &&
!pressedKeys.value.has(props.wheelKey)
) {
showOverlay();
return;
}

return {
onWheel,
}
// normalizes the value of ev.deltaY to either be 1 or -1 and multiplies it with the double click zoom step?
const deltaZoom =
(props.dblClickZoomStep * ev.deltaY) / Math.abs(ev.deltaY);
changeZoom(deltaZoom, "wheel");
}

return {
onWheel,
};
}
Loading