Browse Source

Add hot spot dragging and a few more minor changes (#881).

* Hotspot dragging (hot spot attribute draggable + dragHandler function)
* Allow Image and ImageData and ImageBitmap as parameter panorama for equirectangular scenes
pull/918/head
NiHoel 4 years ago
committed by GitHub
parent
commit
6758d060f7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 115 additions and 9 deletions
  1. +16
    -3
      doc/json-config-parameters.md
  2. +72
    -5
      src/js/pannellum.js
  3. +16
    -0
      tests/run_tests.py
  4. +11
    -1
      tests/tests.html

+ 16
- 3
doc/json-config-parameters.md View File

@@ -323,6 +323,18 @@ If `clickHandlerFunc` is specified, this function is added as an event handler
for the hot spot's `click` event. The event object and the contents of for the hot spot's `click` event. The event object and the contents of
`clickHandlerArgs` are passed to the function as arguments. `clickHandlerArgs` are passed to the function as arguments.


#### `draggable`

If specified, the hotspot can moved using the mouse or by touch.

#### `dragHandlerFunc` (function) and `dragHandlerArgs` (object)

If `dragHandlerFunc` is specified, this function is added as an event handler
when dragging of the hotspot starts and ends. The event object and the contents of
`dragHandlerArgs` are passed to the function as arguments. Possible types of the
event object are: `mousedown`, `pointerdown`, `touchend`, `pointerup`, `pointerleave`,
`mouseup`, and `mouseleave`.

#### `scale` (boolean) #### `scale` (boolean)


When `true`, the hot spot is scaled to match changes in the field of view, When `true`, the hot spot is scaled to match changes in the field of view,
@@ -367,11 +379,12 @@ if this option is enabled.


## `equirectangular` specific options ## `equirectangular` specific options


### `panorama` (string)
### `panorama` (string or HTMLImageElement or ImageData or ImageBitmap)


Sets the URL to the equirectangular panorama image. This is relative to
`basePath` if it is set, else it is relative to the location of
If a string is passed, it sets the URL to the equirectangular panorama image.
This is relative to `basePath` if it is set, else it is relative to the location of
`pannellum.htm`. An absolute URL can also be used. `pannellum.htm`. An absolute URL can also be used.
Alternatively, an already loaded image can be passed.


### `haov` (number) ### `haov` (number)




+ 72
- 5
src/js/pannellum.js View File

@@ -40,6 +40,7 @@ var _this = this;
var config, var config,
renderer, renderer,
preview, preview,
draggingHotSpot,
isUserInteracting = false, isUserInteracting = false,
latestInteraction = Date.now(), latestInteraction = Date.now(),
onPointerDownPointerX = 0, onPointerDownPointerX = 0,
@@ -378,8 +379,15 @@ function init() {
if (config.dynamic !== true) { if (config.dynamic !== true) {
// Still image // Still image
if (config.panorama instanceof Image || config.panorama instanceof ImageData ||
config.panorama instanceof ImageBitmap) {
panoImage = config.panorama;
onImageLoad();
return;
}

p = absoluteURL(config.panorama) ? config.panorama : p + config.panorama; p = absoluteURL(config.panorama) ? config.panorama : p + config.panorama;
panoImage.onload = function() { panoImage.onload = function() {
window.URL.revokeObjectURL(this.src); // Clean up window.URL.revokeObjectURL(this.src); // Clean up
onImageLoad(); onImageLoad();
@@ -712,7 +720,7 @@ function onDocumentMouseDown(event) {
container.focus(); container.focus();
// Only do something if the panorama is loaded // Only do something if the panorama is loaded
if (!loaded || !config.draggable) {
if (!loaded || !config.draggable || config.draggingHotSpot) {
return; return;
} }
@@ -797,7 +805,9 @@ function mouseEventToCoords(event) {
* @param {MouseEvent} event - Document mouse move event. * @param {MouseEvent} event - Document mouse move event.
*/ */
function onDocumentMouseMove(event) { function onDocumentMouseMove(event) {
if (isUserInteracting && loaded) {
if (draggingHotSpot) {
moveHotSpot(draggingHotSpot, event);
} else if (isUserInteracting && loaded) {
latestInteraction = Date.now(); latestInteraction = Date.now();
var canvas = renderer.getCanvas(); var canvas = renderer.getCanvas();
var canvasWidth = canvas.clientWidth, var canvasWidth = canvas.clientWidth,
@@ -821,6 +831,10 @@ function onDocumentMouseMove(event) {
* @private * @private
*/ */
function onDocumentMouseUp(event) { function onDocumentMouseUp(event) {
if (draggingHotSpot && draggingHotSpot.dragHandlerFunc)
draggingHotSpot.dragHandlerFunc(event, draggingHotSpot.dragHandlerArgs);
draggingHotSpot = null;

if (!isUserInteracting) { if (!isUserInteracting) {
return; return;
} }
@@ -845,7 +859,7 @@ function onDocumentMouseUp(event) {
*/ */
function onDocumentTouchStart(event) { function onDocumentTouchStart(event) {
// Only do something if the panorama is loaded // Only do something if the panorama is loaded
if (!loaded || !config.draggable) {
if (!loaded || !config.draggable || draggingHotSpot) {
return; return;
} }


@@ -936,6 +950,8 @@ function onDocumentTouchMove(event) {
* @private * @private
*/ */
function onDocumentTouchEnd() { function onDocumentTouchEnd() {
draggingHotSpot = null;

isUserInteracting = false; isUserInteracting = false;
if (Date.now() - latestInteraction > 150) { if (Date.now() - latestInteraction > 150) {
speed.pitch = speed.yaw = 0; speed.pitch = speed.yaw = 0;
@@ -973,6 +989,11 @@ function onDocumentPointerDown(event) {
*/ */
function onDocumentPointerMove(event) { function onDocumentPointerMove(event) {
if (event.pointerType == 'touch') { if (event.pointerType == 'touch') {
if (draggingHotSpot) {
moveHotSpot(draggingHotSpot, event);
return;
}

if (!config.draggable) if (!config.draggable)
return; return;
for (var i = 0; i < pointerIDs.length; i++) { for (var i = 0; i < pointerIDs.length; i++) {
@@ -994,6 +1015,10 @@ function onDocumentPointerMove(event) {
* @param {PointerEvent} event - Document pointer up event. * @param {PointerEvent} event - Document pointer up event.
*/ */
function onDocumentPointerUp(event) { function onDocumentPointerUp(event) {
if (draggingHotSpot && draggingHotSpot.dragHandlerFunc)
draggingHotSpot.dragHandlerFunc(event, draggingHotSpot.dragHandlerArgs);
draggingHotSpot = null;

if (event.pointerType == 'touch') { if (event.pointerType == 'touch') {
var defined = false; var defined = false;
for (var i = 0; i < pointerIDs.length; i++) { for (var i = 0; i < pointerIDs.length; i++) {
@@ -1412,7 +1437,8 @@ function animate() {
} else if (renderer && (renderer.isLoading() || (config.dynamic === true && update))) { } else if (renderer && (renderer.isLoading() || (config.dynamic === true && update))) {
requestAnimationFrame(animate); requestAnimationFrame(animate);
} else { } else {
fireEvent('animatefinished', {pitch: _this.getPitch(), yaw: _this.getYaw(), hfov: _this.getHfov()});
if (_this.getPitch && _this.getYaw && _this.getHfov)
fireEvent('animatefinished', {pitch: _this.getPitch(), yaw: _this.getYaw(), hfov: _this.getHfov()});
animating = false; animating = false;
prevTime = undefined; prevTime = undefined;
var autoRotateStartTime = config.autoRotateInactivityDelay - var autoRotateStartTime = config.autoRotateInactivityDelay -
@@ -1813,10 +1839,51 @@ function createHotSpot(hs) {
div.className += ' pnlm-pointer'; div.className += ' pnlm-pointer';
span.className += ' pnlm-pointer'; span.className += ' pnlm-pointer';
} }
if (hs.draggable) {
// Handle mouse by container event listeners
div.addEventListener('mousedown', function (e) {
if (hs.dragHandlerFunc)
hs.dragHandlerFunc(e, hs.dragHandlerArgs);
draggingHotSpot = hs;
});

if (document.documentElement.style.pointerAction === '' &&
document.documentElement.style.touchAction === '') {
div.addEventListener('pointerdown', function (e) {
if (hs.dragHandlerFunc)
hs.dragHandlerFunc(e, hs.dragHandlerArgs);
draggingHotSpot = hs;
});
}

// Handle touch events by hotspot event listener
div.addEventListener('touchmove', function(e) {
moveHotSpot(hs, e.targetTouches[0]);
});
div.addEventListener('touchend', function (e) {
if (hs.dragHandlerFunc)
hs.dragHandlerFunc(e, hs.dragHandlerArgs);
draggingHotSpot = null;
})
}
hs.div = div; hs.div = div;
} }


/** /**
* Moves a curently displayed hot spot.
* @private
* @param {Object} hs - Hot spot to move.
* @param {MouseEvent} event - Mouse event to get coordinates from.
*/
function moveHotSpot(hs, event){
var coords = mouseEventToCoords(event);
hs.pitch = coords[0];
hs.yaw = coords[1];
renderHotSpot(hs);
};

/**
* Creates hot spot elements for the current scene. * Creates hot spot elements for the current scene.
* @private * @private
*/ */


+ 16
- 0
tests/run_tests.py View File

@@ -27,6 +27,7 @@ import numpy as np
from PIL import Image, ImageChops from PIL import Image, ImageChops
from selenium.common.exceptions import TimeoutException from selenium.common.exceptions import TimeoutException
from selenium import webdriver from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains




class PannellumServer(SimpleHTTPRequestHandler): class PannellumServer(SimpleHTTPRequestHandler):
@@ -170,6 +171,21 @@ class PannellumTester(object):
comparator = self.take_screenshot("panorama") comparator = self.take_screenshot("panorama")
self.equal_images(reference, comparator, "multires") self.equal_images(reference, comparator, "multires")


# Check hotspot dragging - move from (20, 20) to (0, 0)
action = ActionChains(self.browser)
action.drag_and_drop(
self.browser.find_element_by_class_name("pnlm-hotspot"),
self.browser.find_element_by_class_name(
"pnlm-render-container"
), # drops in the middle of the element
)
action.perform()
time.sleep(1)
assert self.browser.execute_script(
"var hs = viewer.getConfig().hotSpots[0]; return Math.abs(hs.yaw) < 0.001 && Math.abs(hs.pitch) < 0.001"
)
print("PASS: hot spot dragging")

self.httpd.server_close() self.httpd.server_close()


def get_browser(self, name=None): def get_browser(self, name=None):


+ 11
- 1
tests/tests.html View File

@@ -78,7 +78,17 @@ viewer = pannellum.viewer('panorama', {
"tileResolution": 256, "tileResolution": 256,
"maxLevel": 4, "maxLevel": 4,
"cubeResolution": 2048 "cubeResolution": 2048
}
},
"hotSpots": [
{
"pitch": 20,
"yaw": 20,
"type": "info",
"text": "drag test",
"draggable": true,
"dragHandlerFunc": console.log
}
]
} }
} }
}); });


Loading…
Cancel
Save