const initializeMinimap = (args = {}, scrawl) => {Accessible GUI-based simple canvas editor
Related files:
This module creates a large Cell wrapper which we can use to host the entitys to be used in the scene, alongside a draggable, keyboard accessible minimap for navigating around the Cell. The module will also add the following zones to the supplied Canvas wrapper:
ALT + arrows keystrokes, and can be hidden/revealed using the ALT + backspace keystrokeInputs to the initializeMinimap function
canvas - SC canvas wrapper object (required)mainDimensions - a Number, or an array of [width, height] Numbers, setting the size of the scene Cell which will be returned to the calling code (default: 1600)mapDimensions - a Number, or an array of [width, height] Numbers, setting the size of minimap that appears on the scene Cell (default: 200)Output from the initializeMinimap function - is an object containing the following attributes:
checkForMinimapChanges - a function that can be invoked by an animation object, to adjust responsively to changes in the host DOM canvas element’s dimensions (responsive canvas)mainCell - the scene Cell wrapper objectkillCanvasMinimap - kill function, to remove everything associated with the scene Cell and minimap from the SC libraryconst initializeMinimap = (args = {}, scrawl) => {Check we have required arguments/values
if (scrawl == null) throw new Error('SC entity navigation module error: missing Scrawl-canvas object argument');
/* eslint-disable-next-line */
let { mainDimensions, mapDimensions, supportedKey, canvas } = args;
let argsCheck = '';
if (canvas == null) argsCheck += ' Canvas wrapper;';
if (argsCheck.length) throw new Error(`SC canvas minimap module error: missing arguments -${argsCheck}`);Magic numbers
let mainDimensionX = 1600,
mainDimensionY = 1600,
mapDimensionX = 200,
mapDimensionY = 200;
if (mainDimensions) {
if (Array.isArray(mainDimensions)){
mainDimensionX = mainDimensions[0] || 1600;
mainDimensionY = mainDimensions[1] || 1600;
}
else if (mainDimensions.toFixed) {
mainDimensionX = mainDimensions || 1600;
mainDimensionY = mainDimensions || 1600;
}
}
if (mapDimensions) {
if (Array.isArray(mapDimensions)){
mapDimensionX = mapDimensions[0] || 200;
mapDimensionY = mapDimensions[1] || 200;
}
else if (mapDimensions.toFixed) {
mapDimensionX = mapDimensions || 200;
mapDimensionY = mapDimensions || 200;
}
}
const mainMapRatioX = mainDimensionX / mapDimensionX,
mainMapRatioY = mainDimensionY / mapDimensionY;
let [displayWidth, displayHeight] = canvas.get("dimensions");
let frameWidth = (displayWidth / mainDimensionX) * mapDimensionX;
let frameHeight = (displayHeight / mainDimensionY) * mapDimensionY;
canvas.set({
includeInTabNavigation: true,
}); const mainCell = canvas.buildCell({
name: "main-cell",
dimensions: [mainDimensionX, mainDimensionY],
shown: false,
});Rely on the main code to populate the main Cell with entitys, and the functionality required to interact with them. We only care here about displaying the main map in the canvas element
Display the main Cell in the base Cell
const mainCellPicture = scrawl.makePicture({
name: "main-cell-picture",
group: canvas.base.name,
asset: "main-cell",
dimensions: [displayWidth, displayHeight],
copyDimensions: [displayWidth, displayHeight]
});Build out the smaller map Cell (default: 200px x 200px)
const pivotGroup = scrawl.makeGroup({
name: "map-cell-pivot-group",
host: canvas.base.name
});
const mapPivot = scrawl.makeBlock({
name: "map-cell-pivot",
group: "map-cell-pivot-group",
start: [displayWidth - mapDimensionX, 0],
dimensions: [mapDimensionX, mapDimensionY],
method: "none"
});Functionality so we can drag-drop the map Cell around the base Cell
const mapDragZone = scrawl.makeDragZone({
zone: canvas,
collisionGroup: "map-cell-pivot-group",
coordinateSource: canvas.base,
endOn: ["up", "leave"],
preventTouchDefaultWhenDragging: true,
processingOrder: 21,
});
const mapCell = canvas.buildCell({
name: "map-cell",
dimensions: [mapDimensionX, mapDimensionY],We pivot the map Cell to the draggable Block entity
pivot: "map-cell-pivot",
lockTo: "pivot",
backgroundColor: "white",The map Cell needs to compile after the large Cell
compileOrder: 1
});Now we can copy the large Cell into the map Cell
scrawl.makePicture({
name: "map-cell-picture",
group: "map-cell",
asset: "main-cell",
dimensions: ["100%", "100%"],
copyDimensions: ["100%", "100%"],
lineWidth: 4,
method: "fillThenDraw",
});Add the draggable map frame
const myFrameGroup = scrawl.makeGroup({
name: "map-cell-frame-group",
host: mapCell.name
});
const frame = scrawl.makeBlock({
name: "map-cell-frame",
group: "map-cell-frame-group",
dimensions: [frameWidth, frameHeight],
strokeStyle: "red",
lineWidth: 2,
method: "draw"
});Functionality so we can drag-drop the frame around the map Cell
const checkPermittedFramePosition = (x, y) => {
if (x < 0 || y < 0) return false;
if (x + frameWidth > mapDimensionX) return false;
if (y + frameHeight > mapDimensionY) return false;
return true;
};
const updateFrames = (x, y) => {
const newX = x * mainMapRatioX,
newY = y * mainMapRatioY;Adjust the position of the Picture wrt to the frame in the map
mainCellPicture.set({
copyStartX: newX,
copyStartY: newY
});Adjust the position of the large Cell wrt the base Cell
mainCell.set({
startX: -newX,
startY: -newY
});
};
const exitDrag = () => {
let [x, y] = frame.get('position');
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + frameWidth > mapDimensionX) x = mapDimensionX - frameWidth;
if (y + frameHeight > mapDimensionY) y = mapDimensionY - frameHeight;
frame.set({
start: [x, y],
});
updateFrames(x, y);
};
const checkMinimapFrameDrag = () => {
const [x, y] = frame.get('position');
if (checkPermittedFramePosition(x, y)) updateFrames(x, y);
else frameDragZone('exit');
};
const frameDragZone = scrawl.makeDragZone({
zone: canvas,
collisionGroup: "map-cell-frame-group",
coordinateSource: mapCell,
endOn: ["up", "leave"],
updateWhileMoving: checkMinimapFrameDrag,
updateOnPrematureExit: exitDrag,
preventTouchDefaultWhenDragging: true,
exposeCurrentArtefact: true,
processingOrder: 20,
});
const checkForMinimapChanges = () => {
const [w, h] = canvas.get("dimensions");
if (w !== displayWidth || h !== displayHeight) {
displayWidth = w;
displayHeight = h;
frameWidth = (displayWidth / mainDimensionX) * mapDimensionX;
frameHeight = (displayHeight / mainDimensionY) * mapDimensionY;
mainCellPicture.set({
dimensions: [displayWidth, displayHeight],
copyDimensions: [displayWidth, displayHeight]
});
frame.set({
dimensions: [frameWidth, frameHeight]
});
mapPivot.set({
start: [displayWidth - mapDimensionX, 0]
});
}
mainCell.updateHere();
mapCell.updateHere();
};Keyboard navigation
const moveFrame = (direction) => {
let [x, y] = frame.get('position');
switch (direction) {
case 'left' :
x -= 1;
break;
case 'up' :
y -= 1;
break;
case 'right' :
x += 1;
break;
case 'down' :
y += 1;
break;
}
if(checkPermittedFramePosition(x, y)) {
const newX = x * mainMapRatioX,
newY = y * mainMapRatioY;
frame.set({
startX: x,
startY: y,
});Adjust the position of the Picture wrt to the frame in the map
mainCellPicture.set({
copyStartX: newX,
copyStartY: newY
});Adjust the position of the large Cell wrt the base Cell
mainCell.set({
startX: -newX,
startY: -newY
});
}
};
const moveMinimap = (direction) => {
let [x, y] = mapPivot.get('position');
switch (direction) {
case 'left' :
x -= 2;
break;
case 'up' :
y -= 2;
break;
case 'right' :
x += 2;
break;
case 'down' :
y += 2;
break;
}
mapPivot.set({
startX: x,
startY: y,
});
};Show/hide minimap
let minimapIsShowing = true;
const displayMinimap = () => {
minimapIsShowing = !minimapIsShowing;
mapPivot.set({
visibility: minimapIsShowing,
});
mapCell.set({
shown: minimapIsShowing,
});
};Keyboard event listener functions
const keyboard = scrawl.makeKeyboardZone({
zone: canvas,
shiftAlt: {
Backspace: () => displayMinimap(),
ArrowLeft: () => moveMinimap('left'),
ArrowUp: () => moveMinimap('up'),
ArrowRight: () => moveMinimap('right'),
ArrowDown: () => moveMinimap('down'),
},
altOnly: {
Backspace: () => displayMinimap(),
ArrowLeft: () => moveFrame('left'),
ArrowUp: () => moveFrame('up'),
ArrowRight: () => moveFrame('right'),
ArrowDown: () => moveFrame('down'),
},
}); const killCanvasMinimap = () => {
pivotGroup.kill(true);
myFrameGroup.kill(true);
mainCell.get('group').kill(true);
mapCell.get('group').kill(true);
keyboard.kill();
frameDragZone();
mapDragZone();
};Return object
return {
mainCell,
checkForMinimapChanges,
killCanvasMinimap,
};
};export {
initializeMinimap,
};