mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Use d3-zoom to handle zoom and pan
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
"core-js": "^3.21.0",
|
||||
"csv-parse": "^5.3.0",
|
||||
"d3": "3",
|
||||
"d3-zoom": "^3.0.0",
|
||||
"date-fns": "^2.28.0",
|
||||
"date-fns-tz": "^1.3.3",
|
||||
"decode-uri-component": "^0.2.1",
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<script setup>
|
||||
import { zoom, zoomIdentity } from "d3-zoom";
|
||||
import { ref, onMounted, templateRef } from "vue";
|
||||
import { pointer, select } from "d3-selection";
|
||||
|
||||
const props = defineProps({
|
||||
zoom: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
minZoom: {
|
||||
type: Number,
|
||||
default: 0.2,
|
||||
},
|
||||
maxZoom: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["transform"]);
|
||||
|
||||
const viewport = ref(null);
|
||||
const transform = { x: 0, y: 0, zoom: props.zoom };
|
||||
let isZoomingOrPanning = false;
|
||||
const panOnScrollSpeed = 0.5;
|
||||
|
||||
const eventToTransform = (transform) => {
|
||||
return `translate(${transform.x}px, ${transform.y}px) scale(${transform.k})`;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const d3Zoom = zoom().scaleExtent([props.minZoom, props.maxZoom]);
|
||||
const d3Selection = select(viewport.value).call(d3Zoom);
|
||||
const d3ZoomHandler = d3Selection.on("wheel.zoom");
|
||||
const updatedTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom);
|
||||
d3Zoom.transform(d3Selection, updatedTransform);
|
||||
|
||||
d3Selection
|
||||
.on("wheel", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
const currentZoom = d3Selection.property("__zoom").k || 1;
|
||||
|
||||
// if (event.ctrlKey && zoomOnPinch) {
|
||||
// // taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
|
||||
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10;
|
||||
const point = pointer(event);
|
||||
const zoom = currentZoom * 2 ** pinchDelta;
|
||||
d3Zoom.scaleTo(d3Selection, zoom, point);
|
||||
|
||||
// return
|
||||
// }
|
||||
|
||||
// increase scroll speed in firefox
|
||||
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
||||
// const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
|
||||
// const deltaX = event.deltaX * deltaNormalize;
|
||||
// const deltaY = event.deltaY * deltaNormalize;
|
||||
|
||||
// d3Zoom.translateBy(
|
||||
// d3Selection,
|
||||
// -(deltaX / currentZoom) * panOnScrollSpeed,
|
||||
// -(deltaY / currentZoom) * panOnScrollSpeed
|
||||
// );
|
||||
})
|
||||
.on("wheel.zoom", null);
|
||||
|
||||
d3Zoom.on("start", (event) => {
|
||||
isZoomingOrPanning = true;
|
||||
emit("transform", event.transform);
|
||||
});
|
||||
|
||||
d3Zoom.on("end", (event) => {
|
||||
isZoomingOrPanning = false;
|
||||
emit("transform", event.transform);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div ref="viewport">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
@@ -7,47 +7,46 @@
|
||||
@reset-all="onResetAll"
|
||||
@update:pan="onPan" />
|
||||
<div ref="el" class="canvas-viewport" @drop.prevent>
|
||||
<PinchScrollZoom
|
||||
<d3-zoom
|
||||
id="canvas-container"
|
||||
ref="zoomControl"
|
||||
:width="position.width"
|
||||
:height="position.height"
|
||||
:within="false"
|
||||
minScale="0.1"
|
||||
maxScale="10"
|
||||
throttleDelay="16"
|
||||
wheelVelocity="0.0015"
|
||||
@transform="onTransform"
|
||||
@dragging="onDrag"
|
||||
@scaling="onScale">
|
||||
<svg class="canvas-svg" width="100%" height="100%" ref="svg">
|
||||
<g class="zoomable">
|
||||
<svg :width="position.width" :height="position.height" class="canvas-svg">
|
||||
<svg>
|
||||
<raw-connector v-if="draggingConnection" :position="draggingConnection"></raw-connector>
|
||||
<terminal-connector
|
||||
v-for="connection in connections"
|
||||
:key="connection.id"
|
||||
:connection="connection"></terminal-connector>
|
||||
</g>
|
||||
</svg>
|
||||
</svg>
|
||||
<WorkflowNode
|
||||
v-for="(step, key) in steps"
|
||||
:id="key"
|
||||
:key="key"
|
||||
:name="step.name"
|
||||
:type="step.type"
|
||||
:content-id="step.content_id"
|
||||
:step="step"
|
||||
:datatypes-mapper="datatypesMapper"
|
||||
:get-manager="getManager"
|
||||
:activeNodeId="activeNodeId"
|
||||
:root-offset="position"
|
||||
:scale="zoomLevel"
|
||||
@pan-by="onPan"
|
||||
@stopDragging="onStopDragging"
|
||||
@onDragConnector="onDragConnector"
|
||||
@onActivate="onActivate"
|
||||
@onRemove="onRemove"
|
||||
v-on="$listeners" />
|
||||
</PinchScrollZoom>
|
||||
<div ref="nodes" class="nodeArea">
|
||||
<WorkflowNode
|
||||
v-for="(step, key) in steps"
|
||||
:id="key"
|
||||
:key="key"
|
||||
:name="step.name"
|
||||
:type="step.type"
|
||||
:content-id="step.content_id"
|
||||
:step="step"
|
||||
:datatypes-mapper="datatypesMapper"
|
||||
:get-manager="getManager"
|
||||
:activeNodeId="activeNodeId"
|
||||
:root-offset="position"
|
||||
:scale="zoomLevel"
|
||||
@pan-by="onPan"
|
||||
@stopDragging="onStopDragging"
|
||||
@onDragConnector="onDragConnector"
|
||||
@onActivate="onActivate"
|
||||
@onRemove="onRemove"
|
||||
v-on="$listeners" />
|
||||
</div>
|
||||
</d3-zoom>
|
||||
</div>
|
||||
<Minimap
|
||||
:nodes="nodes"
|
||||
@@ -65,10 +64,9 @@ import WorkflowNode from "./Node";
|
||||
import RawConnector from "./Connector";
|
||||
import TerminalConnector from "./TerminalConnector";
|
||||
import Minimap from "./Minimap.vue";
|
||||
import SvgPanZoom from "vue-svg-pan-zoom";
|
||||
import { reactive, ref } from "vue";
|
||||
import { useElementBounding } from "@vueuse/core";
|
||||
import PinchScrollZoom, { PinchScrollZoomEmitData } from "@coddicat/vue-pinch-scroll-zoom";
|
||||
import D3Zoom from "./D3Zoom.vue";
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
@@ -77,13 +75,12 @@ export default {
|
||||
return { el, position };
|
||||
},
|
||||
components: {
|
||||
D3Zoom,
|
||||
RawConnector,
|
||||
SvgPanZoom,
|
||||
TerminalConnector,
|
||||
WorkflowNode,
|
||||
Minimap,
|
||||
ZoomControl,
|
||||
PinchScrollZoom,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -92,6 +89,7 @@ export default {
|
||||
svgpanzoom: null,
|
||||
transform: null,
|
||||
pan: { x: 0, y: 0 },
|
||||
transform: "",
|
||||
};
|
||||
},
|
||||
props: {
|
||||
@@ -113,6 +111,11 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onTransform(transform) {
|
||||
const cssTransform = `translate(${transform.x}px, ${transform.y}px) scale(${transform.k})`;
|
||||
this.transform = transform.toString();
|
||||
this.$refs.nodes.style.transform = cssTransform;
|
||||
},
|
||||
onPan(pan) {
|
||||
console.log("onPan", pan);
|
||||
this.pan = pan;
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
// not sure if hack or not ...
|
||||
overflow: visible;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nodeArea {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
left: 0px
|
||||
}
|
||||
|
||||
.nodeArea {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
.workflow-client {
|
||||
.workflow-panel {
|
||||
|
||||
+63
-1
@@ -3079,7 +3079,7 @@ char-regex@^1.0.2:
|
||||
resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz"
|
||||
integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
|
||||
|
||||
chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^2.0.0, chokidar@^3.5.1, chokidar@^3.5.3:
|
||||
chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^2.0.0, chokidar@^3.5.1, chokidar@^3.5.2, chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
@@ -3675,6 +3675,68 @@ custom-event@~1.0.0:
|
||||
resolved "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz"
|
||||
integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==
|
||||
|
||||
"d3-color@1 - 3":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
|
||||
integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
|
||||
|
||||
"d3-dispatch@1 - 3":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
|
||||
integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
|
||||
|
||||
"d3-drag@2 - 3":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
|
||||
integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
|
||||
dependencies:
|
||||
d3-dispatch "1 - 3"
|
||||
d3-selection "3"
|
||||
|
||||
"d3-ease@1 - 3":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
|
||||
integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
|
||||
|
||||
"d3-interpolate@1 - 3":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
|
||||
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
|
||||
dependencies:
|
||||
d3-color "1 - 3"
|
||||
|
||||
"d3-selection@2 - 3", d3-selection@3:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
|
||||
integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
|
||||
|
||||
"d3-timer@1 - 3":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
|
||||
integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
|
||||
|
||||
"d3-transition@2 - 3":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
|
||||
integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
|
||||
dependencies:
|
||||
d3-color "1 - 3"
|
||||
d3-dispatch "1 - 3"
|
||||
d3-ease "1 - 3"
|
||||
d3-interpolate "1 - 3"
|
||||
d3-timer "1 - 3"
|
||||
|
||||
d3-zoom@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
|
||||
integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
|
||||
dependencies:
|
||||
d3-dispatch "1 - 3"
|
||||
d3-drag "2 - 3"
|
||||
d3-interpolate "1 - 3"
|
||||
d3-selection "2 - 3"
|
||||
d3-transition "2 - 3"
|
||||
|
||||
d3@3:
|
||||
version "3.5.17"
|
||||
resolved "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz"
|
||||
|
||||
Reference in New Issue
Block a user