Panorama Viewer

Got bored waiting for FaceBook to add an easy “Panorama Picture Viewer” to their Application.
So to see the pictures myself, I made a small tool for it using MaxCanvas. F12 and dig around to see the code if you need it.

  • Click “Next” and “Prev” to scroll through the images
  • Hover Over the “Pan” buttons to …. well … “pan” 😎
  • Hover Over the image for a zoom box

N.B. as I do not want to re-distribute the images, this is not in the MC examples package and the below is minimally commented ;p

Code

// MaxCanvas Panorama Viewer
// Simple Panorama viewer, since facebook has not provided one
//
// Required to drag MC.js into Intellisense
// thanks to https://visualstudiomagazine.com/blogs/tool-tracker/2018/07/use-javascript-code.aspx (Dec 2018)
/// <reference path="MC.js" />

// Gamecode is wrapped in .. window.onload = (function () { -- ALL CODE HERE --}); .. so game will start AFTER everything else is loaded

window.onload = (function() {

// AQUIRE HTML DOM CANVAS REFERENCE
//  HTML has to have a HTML5 canvas element tagged as "canvas"
var c=document.getElementById("canvas");

//////////////////////////
// INITIALISE MC Engine //
//////////////////////////

MC.init(c);

///////////////////////////
// END OF Initialisation //
///////////////////////////
//-----------------------------------------------------------------------------
//////////////////////////
// VARIABLE DECLARATION //
//////////////////////////

var PV = {  // object to hold everything (and so avoid polluting global namespace)
    // Variables
    Pan1 : new MC.Picture(),
    T : new MC.Point(0, 0),
    B : new MC.Point(0.5, 1),
    set : false,
    mod : 0,
    maxMod: 0,
    datum: new MC.Point(MC.canvas.midX - 300, MC.canvas.midY - 300),
    delta : 0.001,
    current: 0,
    path: "../Assets/images/panoramas/",
    files: [
        ["Dio01.JPG", "Greek Style Diorama"],
        ["Dio02.JPG"," Prada Wallpaper"],
        ["P1.JPG", "1. Busy Pompeii Street 2019"],
        ["At1.JPG", "2. View from Athens Apartment"],
        ["Sa1.JPG", "3. Fira Harbour (Santorini)"],
        ["Sa2.JPG", "4. Santorini Caldera"],
        ["K1.JPG", "5. Kotor from half way up the walls"],
        ["M1.JPG", "6. Messina"],
        ["M2.JPG", "7. Messina"],
        ["M3.JPG", "8. Messina"],
        ["Sal1.JPG", "9. Salerno Harbour"],
        ["A1.JPG", "10. Appledore & Crow Point from Instow Beach"],
        ["A2.JPG", "11. Estuary from Crow Point"],
        ["A3.JPG", "12. Braunton Burrows"],
        ["A4.JPG", "13. Tapeley House Monument View"],
        ["A5.JPG", "14. Appledore from Instow"]
    ],
    // a "hit sprite" to see if zoom is required - will not be rendered
    win: new MC.Sprite({
        type: "rect",
        size1: 533,
        size2: 533
    }),

    // GUI setup
    GUI : new MC.GUI(),
    Title : new MC.ConBox({ width: 600,
        height: 60,
        text: "Title",
        pos: new MC.Point(MC.canvas.midX, 50)
    }),
    Prev : new MC.ConBox({
        width: 160,
        height: 60,
        text: "< Prev",
        pos: new MC.Point(90, 50),
        onClick: function () { PV.change(-1) }

    }),
    Next : new MC.ConBox({
        width: 160,
        height: 60,
        text: "Next >",
        pos: new MC.Point(MC.canvas.right - 90, 50),
        onClick: function () { PV.change(1) }
    }),
    Left : new MC.ConBox({
        width: 160,
        height: 60,
        text: "<< pan LEFT (a)",
        pos: new MC.Point(MC.canvas.midX - 90,MC.canvas.bottom - 50)
    }),
    Right : new MC.ConBox({
        width: 160,
        height: 60,
        text: "(d) pan RIGHT >>",
        pos: new MC.Point(MC.canvas.midX + 90, MC.canvas.bottom - 50)
    }),

    // Methods
    setUp : function () {
        PV.Pan1 = new MC.Picture(PV.path + PV.files[PV.current][0]);
        PV.Title.text = "Loading";
        PV.set = false;
        PV.mod = 0;
    },
    change : function (direction) { // only use 1 and -1 for these calls 
        if (direction === 1 || direction === -1) {
            PV.current += direction;
            if (PV.current >= PV.files.length) {
                PV.current = 0;
            }
            if (PV.current < 0) {
                PV.current = PV.files.length - 1;
            }
            PV.setUp();
        }
    }
}; 


PV.GUI.push(PV.Prev, PV.Next, PV.Left, PV.Right);  // Add the ConBoxes to the GUI
    
MC.mouse.onClickL = function () { // configure MC.game so it checks the GUI upon Left click down 
        PV.GUI.click();
    };    
    



//////////////////////////
// END OF declarations  //
//////////////////////////
//-----------------------------------------------------------------------------
//////////////////////////
//      GAME LOOP       //
//////////////////////////

    // Set up for the first time
    PV.setUp();

// first call, request subsequently made within
gameLoop();

function gameLoop() {
    // ESSENTIAL MAINTAINANCE
    requestAnimationFrame(gameLoop);    // Required so the PC will automatically call the gameLoop again when (1) the screen is ready and (2) the CPU has finished any previous frame calculations
    MC.game.update();                   // Essential to update MC.game to ensure MC.game.deltaTime is available
    
    // PHYSICS UPDATE LOOP
    // Check if Image freshly loaded
    if (!PV.set && PV.Pan1.loaded)
    {
        PV.B.set(600 / PV.Pan1.width, 1);
        PV.maxMod = 1 - PV.B.x;
        PV.Title.text = PV.files[PV.current][1];
        PV.set = true;
    }
    // Navigate as required
    if (MC.keys.a.down || MC.keys.leftArrow.down || PV.Left.hover) PV.mod = MC.maths.maxOf(0, PV.mod - PV.delta);
    else if (MC.keys.d.down || MC.keys.rightArrow.down || PV.Right.hover) PV.mod = MC.maths.minOf(PV.maxMod, PV.mod + PV.delta);

    // NON-PHYSICS UPDATES
    PV.GUI.update();    // calls update on all ConBoxes in the GUI.  Required so they can change colour if the mouse "hovers" over them

    // RENDER
    // Stage 1.  Clear the canvas
    MC.draw.clearCanvas("Black");
    
    // Stage 2.  Render
    PV.Pan1.render(new MC.Point(MC.canvas.midX, MC.canvas.midY), 0, 600, 600, PV.T.clone().add(PV.mod, 0), PV.B.clone().add(PV.mod, 0));

    // Stage 3.  Render GUI
    PV.Title.render(); // not updated, so no hoverover colour change
    PV.GUI.render();

    // Render zoom box (if required)
    if (PV.win.hit(MC.game.mouse)) {
        var centre = MC.mouse.pos.clone().minus(PV.datum).div(600); // 0-1 ratio of where mouse is in picture screen
        var picpos = new MC.Point(PV.mod + (centre.x * PV.B.x), centre.y); // 0-1 ratio of where on the ACTUAL picture it is
        var newmod = new MC.Point(0.05 * PV.B.x, 0.05);
        var NT = picpos.clone().minus(newmod);
        var NB = picpos.clone().add(newmod);
        MC.draw.rect(MC.mouse.pos, 202, 202, 0, "Orange");
        PV.Pan1.render(MC.game.mouse, 0, 200, 200, NT, NB);
    };
}

}); // EoF