mpetroff / pannellum

Pannellum is a lightweight, free, and open source panorama viewer for the web.
https://pannellum.org/
MIT License
4.3k stars 728 forks source link

Zoom towards hot spot when changing scene #706

Open mpetroff opened 5 years ago

mpetroff commented 5 years ago

A possible enhancement to the current tour functionality would be an option for a zoom animation towards a hot spot before changing to the next scene.

DStillingfleet commented 5 years ago

Hi Matthew

This would be a great feature.

Within my tours I written a script to make three consecutive zooms. The first focus the view in the right general direction, the next zooms in to around an HFOV of say 55, the final zoom to a HFOV of say 15, which may also move the focus left or right, up or down. It's good for conveying the impression of moving along a path, for example.

HubertK42 commented 4 years ago

mhh, zoom in for transitions in walkthroughs would be nice. I've implemented some kind of workaround, to get a feeling of moving in....

The main idea is after clicking a hotspot:

But there is some work to be done in advance.

here a brief sketch of my way (for details, just ask):

function LookAndWait(dHfov, dTime) // Initiate LookAt / Zoom
{
    var pZ = dLastPitch; // Or other pan angle to Zoom in on.
    var yZ = dLastYaw ; // Or other yaw angle to Zoom in on.
    var vZ = dHfov ; // Or other Hfov angle to Zoom in on.
    var pS = dTime ; // Or other zoom speed
    viewer.lookAt(pZ, yZ, vZ, pS); 
    setTimeout(function(){wait_for_view(dLastPitch, dLastYaw, vZ);},1000);
    console.log("LookAndWait(): pZ: "+pZ +" yZ: " +yZ + " vZ:" +vZ + " pS: " + pS);
}

and

function wait_for_view(dPitch, dYaw, dHfov)
{
    var iCnt = 0;
    pI=Math.round(viewer.getPitch()); // get actual pitch and round
    yI=Math.round(viewer.getYaw()); // get actual yaw and round      
    vI=Math.round(viewer.getHfov()); // get actual Hfov and round

     if(yI>180){yI=yI-360;viewer.setYaw(yI,0);}  
     if(yI<-180){yI=yI+360;viewer.setYaw(yI,0);}

         if (pI===dPitch && yI===dYaw && vI===dHfov)  // Has LookAt finished
     {      
        return true;
     }
    else
     {  
        setTimeout(function(){wait_for_view (dPitch, dYaw, dHfov);},10);    
     }
 }
// this function is called after an image is loaded, aka if someone click on a hot spot:

function sceneLoadListener() 
{
    console.log("sceneLoadListener() load " + viewer.getScene() + 
                " done - looking at LP: " + viewer.getPitch() + " LY: " + viewer.getYaw() + " HF: " +
                " setting view to LP: " + dLastPitch + "LY:" + dLastYaw);

    //--- zoom a little bit into current viewing direction
    LookAndWait(110, dTimeZoomInAfterLoad);

};

and add a onmousedown handler in the displaying div: <div id = "panorama" onmousedown="inMouseDown(event)"> with panorama is the div you gave the pannellum.viewer

//--- stores viewing coordinates of last mouse click (calculates real coordinates to viewer coordinates)
//--- this funcion must be set in the html div
function inMouseDown(e)
{
    var coords = viewer.mouseEventToCoords(e);

    dLastPitch   = coords[0];
    dLastYaw     = coords[1];
    dLastHfov    = viewer.getHfov();

    console.log(logPOVstr("inMouseDown: "));
};

this worked for me. Here are my timing values used:

var dTimeLookAtHotSpot   =  400; // [ms] , after clicking a hotspot, time to focus on hotspot
var dTimeZoomInHotSpot   =  1000; // [ms] , after clicking a hotspot, time to zoom into the hotspot
var dTimeZoomInAfterLoad =  500; // [ms] , after clicking a hotspot and its loaded, time to zoom in a little bit
var dTimeToLoad         =   1000; // [ms] , setTimeout time to wait for finisheing loading new a scene
DStillingfleet commented 4 years ago

Hi HubertK42

An interesting idea. I had tried something similar (in loading the next scene at 120 degrees and zooming to 100 hfov) but took it out of my js due to problems with older partial panoramas not being able to zoom out that far. I’ll have to look at reinstating it.

Best wishes

Derek

Sent from my phone.

On 13 Jan 2020, at 19:30, HubertK42 notifications@github.com wrote:

 mhh, zoom in for transitions in walkthroughs would be nice. I've implemented some kind of workaround, to get a feeling of moving in....

The main idea is after clicking a hotspot:

Focus on the Hotspot Zoom in the Hotspt Load new scene (with zoom out parameters, Hfov set to 120) Zoom back (this means zoom a little bit in) to normal view (Hfov set to 110) But there is some work to be done in advance.

here a brief sketch of my way (for details, just ask):

set all scenes initally to Hfov: 110, we zoom in with sceneLoadListener () later

all HotSpots must not contain "sceneId," instead add "clickHandlerFunc": onHotSpotClicked, "clickHandlerArgs" : {target:sSceneID} to the hotspot --- to prevent autoload, sSceneID is the id where the hotspot leads to

add hotspot listener function:

var onHotSpotClicked = function(e, clickHandlerArgs) { setTimeout(function(){LookAndWait(viewer.getHfov(), dTimeLookAtHotSpot);},10); //--- first look directly at the hotspot LookAndWait(50, dTimeZoomInHotSpot); //--- then zoom in setTimeout(function(){viewer.loadScene(clickHandlerArgs.target, dLastPitch, dLastYaw, 120);}, dTimeToLoad); // then load the new scene };

you need a need some function to wait for finished animations: function LookAndWait(dHfov, dTime) // Initiate LookAt / Zoom { var pZ = dLastPitch; // Or other pan angle to Zoom in on. var yZ = dLastYaw ; // Or other yaw angle to Zoom in on. var vZ = dHfov ; // Or other Hfov angle to Zoom in on. var pS = dTime ; // Or other zoom speed viewer.lookAt(pZ, yZ, vZ, pS); setTimeout(function(){wait_for_view(dLastPitch, dLastYaw, vZ);},1000); console.log("LookAndWait(): pZ: "+pZ +" yZ: " +yZ + " vZ:" +vZ + " pS: " + pS); } and

function wait_for_view(dPitch, dYaw, dHfov) { var iCnt = 0; pI=Math.round(viewer.getPitch()); // get actual pitch and round yI=Math.round(viewer.getYaw()); // get actual yaw and round vI=Math.round(viewer.getHfov()); // get actual Hfov and round

if(yI>180){yI=yI-360;viewer.setYaw(yI,0);}
if(yI<-180){yI=yI+360;viewer.setYaw(yI,0);}

 if (pI===dPitch && yI===dYaw && vI===dHfov)  // Has LookAt finished

{
return true; } else {
setTimeout(function(){wait_for_view (dPitch, dYaw, dHfov);},10);
} }

add the sceneLoadListener : viewer.on('load', sceneLoadListener); // this function is called after an image is loaded, aka if someone clickt on a hot spot:

function sceneLoadListener() { console.log("sceneLoadListener() load " + viewer.getScene() + " done - looking at LP: " + viewer.getPitch() + " LY: " + viewer.getYaw() + " HF: " + " setting view to LP: " + dLastPitch + "LY:" + dLastYaw);

//--- zoom a little bit into current viewing direction LookAndWait(110, dTimeZoomInAfterLoad); };

further more remeber last clicked mousecoordinates var dLastPitch; var dLastYaw; var dLastHfov; and add a onmousedown handler in the displaying div:

with panorama is the div you gave the pannellum.viewer //--- stores viewing coordinates of last mouse click (calculates real coordinates to viewer coordinates) //--- this funcion must be set in the html div function inMouseDown(e) { var coords = viewer.mouseEventToCoords(e);

dLastPitch = coords[0]; dLastYaw = coords[1]; dLastHfov = viewer.getHfov();

console.log(logPOVstr("inMouseDown: ")); };

this worked for me. Here are my timing values used: var dTimeLookAtHotSpot = 400; // [ms] , after clicking a hotspot, time to focus on hotspot var dTimeZoomInHotSpot = 1000; // [ms] , after clicking a hotspot, time to zoom into the hotspot var dTimeZoomInAfterLoad = 500; // [ms] , after clicking a hotspot and its loaded, time to zoom in a little bit var dTimeToLoad = 1000; // [ms] , setTimeout time to wait for finisheing loading new a scene

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

RoEVDiesel commented 1 year ago

Hello my experts programmers. I'm testing my limits here, but after a few days of work, I could not get this "zoom" effect to work. It seems that the onHotSpotClicked is never activated. Here is my full code, for a simple example, to switch between two panoramas, while zooming the transition. The first panorama loads just fine, but not the second. Your help is much appreciated!

<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Tour</title>

        <link rel="stylesheet" href="src/css/pannellum.css"/>
        <link rel="stylesheet" href="src/standalone/standalone.css"/>

        <script type="text/javascript" src="src/js/libpannellum.js"></script>
        <script type="text/javascript" src="src/js/pannellum.js"></script>
        <script type="text/javascript" src="src/standalone/standalone.js"></script>

        <style>
            #panorama {
                width: 1200px;
                height: 600px;
            }
        </style>

        <script>
            var dLastPitch;
            var dLastYaw;
            var dLastHfov;

            var dTimeLookAtHotSpot   =  400; // [ms] , after clicking a hotspot, time to focus on hotspot
            var dTimeZoomInHotSpot   =  1000; // [ms] , after clicking a hotspot, time to zoom into the hotspot
            var dTimeZoomInAfterLoad =  500; // [ms] , after clicking a hotspot and its loaded, time to zoom in a little bit
            var dTimeToLoad         =   1000; // [ms] , setTimeout time to wait for finisheing loading new a sce
        </script>

    </head>
        <body>

            <!-- <div id="panorama"></div> -->
            <div id = "panorama" onmousedown="inMouseDown(event)">
                <script>

                    //--- stores viewing coordinates of last mouse click (calculates real coordinates to viewer coordinates)
                    //--- this funcion must be set in the html div
                    function inMouseDown(e)
                    {
                        var coords = viewer.mouseEventToCoords(e);

                        dLastPitch   = coords[0];
                        dLastYaw     = coords[1];
                        dLastHfov    = viewer.getHfov();

                        console.log(logPOVstr("inMouseDown: "));
                    };

                </script>
            </div>

            <script>

                pannellum.viewer('panorama', {   
                    "default": {
                        "firstScene": "scene_one",
                        "sceneFadeDuration": 1000
                    },

                    "scenes": {
                        "scene_one": {
                            "title": "FIRST",
                            "hfov": 120,
                            "pitch": 0,
                            "yaw": 120,
                            "type": "equirectangular",
                            "panorama": "./1.jpg",
                            "hotSpots": [
                                {
                                    "pitch": 0,
                                    "yaw": 130,
                                    "type": "scene",
                                    "text": "Toward 2",
                                    "clickHandlerFunc": onHotSpotClicked,
                                    "clickHandlerArgs": {target:"scene_two"}
                                }
                            ]
                        },

                        "scene_two": {
                            "title": "SECOND",
                            "hfov": 120,
                            "yaw": 0,
                            "type": "equirectangular",
                            "panorama": "./2.jpg",
                            "hotSpots": [
                                {
                                    "pitch": 0,
                                    "yaw": 00,
                                    "type": "scene",
                                    "text": "Toward 1",
                                    "clickHandlerFunc": onHotSpotClicked,
                                    "clickHandlerArgs": {target:"scene_one"},
                                    "targetYaw": 120,
                                    "targetPitch": 0
                                }
                            ]
                        }
                    }
                });

                var onHotSpotClicked = function(e, clickHandlerArgs)
                {
                   console.log("Hotspot clicked!");
                   setTimeout(function(){LookAndWait(viewer.getHfov(), dTimeLookAtHotSpot);},10); //--- first look directly at the hotspot
                   LookAndWait(50, dTimeZoomInHotSpot); //--- then zoom in
                   setTimeout(function(){viewer.loadScene(clickHandlerArgs.target, dLastPitch, dLastYaw, 120);}, dTimeToLoad); // then load the new scene
                };

                function LookAndWait(dHfov, dTime) // Initiate LookAt / Zoom
                {
                    var pZ = dLastPitch; // Or other pan angle to Zoom in on.
                    var yZ = dLastYaw ; // Or other yaw angle to Zoom in on.
                    var vZ = dHfov ; // Or other Hfov angle to Zoom in on.
                    var pS = dTime ; // Or other zoom speed
                    viewer.lookAt(pZ, yZ, vZ, pS); 
                    setTimeout(function(){wait_for_view(dLastPitch, dLastYaw, vZ);},1000);
                    console.log("LookAndWait(): pZ: "+pZ +" yZ: " +yZ + " vZ:" +vZ + " pS: " + pS);
                };

                function wait_for_view(dPitch, dYaw, dHfov)
                {
                    var iCnt = 0;
                    pI=Math.round(viewer.getPitch()); // get actual pitch and round
                    yI=Math.round(viewer.getYaw()); // get actual yaw and round      
                    vI=Math.round(viewer.getHfov()); // get actual Hfov and round

                     if(yI>180){yI=yI-360;viewer.setYaw(yI,0);}  
                     if(yI<-180){yI=yI+360;viewer.setYaw(yI,0);}

                         if (pI===dPitch && yI===dYaw && vI===dHfov)  // Has LookAt finished
                     {      
                        return true;
                     }
                    else
                     {  
                        setTimeout(function(){wait_for_view (dPitch, dYaw, dHfov);},10);    
                     }
                 };

                // this function is called after an image is loaded, aka if someone click on a hot spot:
                function sceneLoadListener() 
                {
                    console.log("sceneLoadListener() load " + viewer.getScene() + 
                                " done - looking at LP: " + viewer.getPitch() + " LY: " + viewer.getYaw() + " HF: " +
                                " setting view to LP: " + dLastPitch + "LY:" + dLastYaw);

                    //--- zoom a little bit into current viewing direction
                    LookAndWait(110, dTimeZoomInAfterLoad);
                };

            </script>
        </body>
</html>
mpetroff commented 1 year ago

@RoEVDiesel There are a couple changes you need to make:

RoEVDiesel commented 1 year ago

Thank you. It is working fine now. I was wondering if the already present function: onDocumentDoubleClick(event) can be used as it performs some kind of zoom-ing ...

 * Event handler for double clicks. Zooms in at clicked location
 * @private
 * @param {MouseEvent} event - Document mouse down event.
function onDocumentDoubleClick(event) {
  if (config.minHfov === config.hfov) {
    _this.setHfov(origHfov, 1000);
} else {
    var coords = mouseEventToCoords(event);
    _this.lookAt(coords[0], coords[1], config.minHfov, 1000);
    }
}

Just my 2cents. By the way, thank you for your work Matthew! Really appreciate it!

mpetroff commented 1 year ago

I was wondering if the already present function: onDocumentDoubleClick(event) can be used as it performs some kind of zoom-ing

It wouldn't make much sense to use that function directly, since it's only a mouse event wrapper around the lookAt function. With an appropriate callback to finish with the scene change, lookAt could be used, though.