working (but messy) pan & zoom without scrollbar and new note on click

This commit is contained in:
azivner 2018-11-11 23:21:43 +01:00
parent 525f167127
commit 02fd8f8133
3 changed files with 88 additions and 36 deletions

View File

@ -4,9 +4,10 @@ import linkService from "./link.js";
import libraryLoader from "./library_loader.js"; import libraryLoader from "./library_loader.js";
import treeService from "./tree.js"; import treeService from "./tree.js";
import contextMenuWidget from "./context_menu.js"; import contextMenuWidget from "./context_menu.js";
import infoService from "./info.js";
const $component = $("#note-detail-relation-map"); const $component = $("#note-detail-relation-map");
const $relationMapCanvas = $("#relation-map-canvas"); const $relationMapContainer = $("#relation-map-container");
const $addChildNotesButton = $("#relation-map-add-child-notes"); const $addChildNotesButton = $("#relation-map-add-child-notes");
const $createChildNote = $("#relation-map-create-child-note"); const $createChildNote = $("#relation-map-create-child-note");
const $zoomInButton = $("#relation-map-zoom-in"); const $zoomInButton = $("#relation-map-zoom-in");
@ -124,33 +125,18 @@ async function loadNotesAndRelations() {
}); });
} }
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
console.log(rect);
console.log(canvas);
console.log(`(${evt.clientX} - ${rect.left}) / (${rect.right} - ${rect.left}) * ${canvas.width}`);
return {
x: (evt.clientX - rect.left) / (rect.right - rect.left) * canvas.width,
y: (evt.clientY - rect.top) / (rect.bottom - rect.top) * canvas.height
};
}
function initPanZoom() { function initPanZoom() {
if (pzInstance) { if (pzInstance) {
return; return;
} }
pzInstance = panzoom($relationMapCanvas[0], { pzInstance = panzoom($relationMapContainer[0], {
maxZoom: 2, maxZoom: 2,
minZoom: 0.1, minZoom: 0.1,
smoothScroll: false, smoothScroll: false,
onMouseDown: function(event) { onMouseDown: function(event) {
if (clipboard) { if (clipboard) {
const {x, y} = getMousePos($relationMapCanvas[0].getContext("2d"), event); const {x, y} = getMousePos(event);
console.log(x, y); console.log(x, y);
@ -165,22 +151,27 @@ function initPanZoom() {
} }
}); });
pzInstance.on('transform', () => {
jsPlumbInstance.setZoom(getZoom());
updateTransform();
});
if (mapData.transform) { if (mapData.transform) {
pzInstance.zoomTo(0, 0, mapData.transform.scale); pzInstance.zoomTo(0, 0, mapData.transform.scale);
pzInstance.moveTo(mapData.transform.x, mapData.transform.y); pzInstance.moveTo(mapData.transform.x, mapData.transform.y);
} }
pzInstance.on('zoom', function (e) { function updateTransform() {
mapData.transform = pzInstance.getTransform(); const newTransform = pzInstance.getTransform();
saveData(); if (JSON.stringify(newTransform) !== JSON.stringify(mapData.transform)) {
}); mapData.transform = newTransform;
pzInstance.on('panend', function (e) { saveData();
mapData.transform = pzInstance.getTransform(); }
}
saveData();
}, true);
$zoomInButton.click(() => pzInstance.zoomTo(0, 0, 1.2)); $zoomInButton.click(() => pzInstance.zoomTo(0, 0, 1.2));
$zoomOutButton.click(() => pzInstance.zoomTo(0, 0, 0.8)); $zoomOutButton.click(() => pzInstance.zoomTo(0, 0, 0.8));
@ -192,7 +183,7 @@ function cleanup() {
jsPlumbInstance.deleteEveryEndpoint(); jsPlumbInstance.deleteEveryEndpoint();
// without this we still end up with note boxes remaining in the canvas // without this we still end up with note boxes remaining in the canvas
$relationMapCanvas.empty(); $relationMapContainer.empty();
} }
if (pzInstance) { if (pzInstance) {
@ -213,7 +204,7 @@ function initJsPlumbInstance () {
Connector: "StateMachine", Connector: "StateMachine",
ConnectionOverlays: uniDirectionalOverlays, ConnectionOverlays: uniDirectionalOverlays,
HoverPaintStyle: { stroke: "#777", strokeWidth: 1 }, HoverPaintStyle: { stroke: "#777", strokeWidth: 1 },
Container: "relation-map-canvas" Container: "relation-map-container"
}); });
jsPlumbInstance.registerConnectionType("uniDirectional", { anchor:"Continuous", connector:"StateMachine", overlays: uniDirectionalOverlays }); jsPlumbInstance.registerConnectionType("uniDirectional", { anchor:"Continuous", connector:"StateMachine", overlays: uniDirectionalOverlays });
@ -223,7 +214,7 @@ function initJsPlumbInstance () {
jsPlumbInstance.bind("connection", connectionCreatedHandler); jsPlumbInstance.bind("connection", connectionCreatedHandler);
// so that canvas is not panned when clicking/dragging note box // so that canvas is not panned when clicking/dragging note box
$relationMapCanvas.on('mousedown touchstart', '.note-box, .connection-label', e => e.stopPropagation()); $relationMapContainer.on('mousedown touchstart', '.note-box, .connection-label', e => e.stopPropagation());
} }
function connectionContextMenuHandler(connection, event) { function connectionContextMenuHandler(connection, event) {
@ -291,7 +282,7 @@ async function connectionCreatedHandler(info, originalEvent) {
connection.getOverlay("label").setLabel(name); connection.getOverlay("label").setLabel(name);
} }
$relationMapCanvas.on("contextmenu", ".note-box", e => { $relationMapContainer.on("contextmenu", ".note-box", e => {
const contextMenuItems = [ const contextMenuItems = [
{title: "Remove note", cmd: "remove", uiIcon: "trash"}, {title: "Remove note", cmd: "remove", uiIcon: "trash"},
{title: "Edit title", cmd: "edit-title", uiIcon: "pencil"}, {title: "Edit title", cmd: "edit-title", uiIcon: "pencil"},
@ -427,7 +418,7 @@ $addChildNotesButton.click(async () => {
let clipboard = null; let clipboard = null;
$createChildNote.click(async () => { $createChildNote.click(async () => {
const title = prompt("Enter title of new note", "new note"); const title = "new note" || prompt("Enter title of new note", "new note");
if (!title.trim()) { if (!title.trim()) {
return; return;
@ -438,6 +429,8 @@ $createChildNote.click(async () => {
target: 'into' target: 'into'
}); });
infoService.showMessage("Click on canvas to place new note");
// reloading tree so that the new note appears there // reloading tree so that the new note appears there
// no need to wait for it to finish // no need to wait for it to finish
treeService.reload(); treeService.reload();
@ -445,6 +438,62 @@ $createChildNote.click(async () => {
clipboard = { id: note.noteId, title }; clipboard = { id: note.noteId, title };
}); });
function getZoom() {
const matrixRegex = /matrix\((-?\d*\.?\d+),\s*0,\s*0,\s*-?\d*\.?\d+,\s*-?\d*\.?\d+,\s*-?\d*\.?\d+\)/;
const matches = $relationMapContainer.css('transform').match(matrixRegex);
return matches[1];
}
function dragover_handler(ev) {
ev.preventDefault();
// Set the dropEffect to move
//ev.dataTransfer.dropEffect = "move";
console.log("DRAGOVER");
}
function drop_handler(ev) {
ev.preventDefault();
console.log("DROP!", ev);
}
function getMousePos(evt) {
const rect = $relationMapContainer[0].getBoundingClientRect();
const zoom = getZoom();
return {
x: (evt.clientX - rect.left) / zoom,
y: (evt.clientY - rect.top) / zoom
};
}
$relationMapContainer.click(function(event) {
console.log("HEY!");
console.log(event.clientX, event.clientY);
if (clipboard) {
const {x, y} = getMousePos(event);
console.log(x, y);
createNoteBox(clipboard.id, clipboard.title, x, y);
//mapData.notes.push({ id: clipboard.id, x, y });
clipboard = null;
}
return true;
});
$component.on("drop", drop_handler);
$component.on("dragover", dragover_handler);
export default { export default {
show, show,
getContent: () => JSON.stringify(mapData), getContent: () => JSON.stringify(mapData),

View File

@ -1,6 +1,5 @@
#note-detail-relation-map { #note-detail-relation-map {
height: 100%; height: 100%;
overflow: hidden;
} }
/* this is removing dotted border around focused/active relation map */ /* this is removing dotted border around focused/active relation map */
@ -8,8 +7,10 @@
outline: none; outline: none;
} }
#relation-map-canvas { #relation-map-wrapper {
position: absolute; /* needs to be absolute otherwise connections will be misplaced */ position: relative;
overflow: hidden !important;
height: 800px;
} }
.note-box { .note-box {

View File

@ -19,5 +19,7 @@
id="relation-map-zoom-out"></button> id="relation-map-zoom-out"></button>
</div> </div>
<div id="relation-map-canvas"></div> <div id="relation-map-wrapper">
<div id="relation-map-container"></div>
</div>
</div> </div>