Skip to content

Commit

Permalink
Better zoom (#525)
Browse files Browse the repository at this point in the history
* Restore +/- zoom and double click zoom

* Make zoom consistent across mouse wheels

* Fix zoom in/out buttons to scale 2x

* Fix centering

* Fix js errors breaking the page

* Fix slider for minimal/embed views

* Fix slider zoom step count
  • Loading branch information
dgarciabriseno authored Feb 7, 2024
1 parent 322f5e5 commit 8584556
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 42 deletions.
3 changes: 2 additions & 1 deletion index.php
Original file line number Diff line number Diff line change
Expand Up @@ -1845,6 +1845,8 @@ function attr($attr, $file) {
<script src="/resources/js/Tiling/Manager/TileLayerManager.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/Tiling/Manager/HelioviewerTileLayerManager.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/Image/JP2Image.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/UI/ZoomControls.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/Viewport/Helper/ScrollZoom.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/Viewport/Helper/PinchDetector.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/Viewport/Helper/HelioviewerZoomer.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/Viewport/Helper/TouchMover.js?v=<?=$debugTime?>" type="text/javascript"></script>
Expand All @@ -1854,7 +1856,6 @@ function attr($attr, $file) {
<script src="/resources/js/Viewport/Helper/ViewportMovementHelper.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/Viewport/HelioviewerViewport.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/HelioviewerClient.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/UI/ZoomControls.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/UI/ImageScale.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/UI/Timeline.js?v=<?=$debugTime?>" type="text/javascript"></script>
<script src="/resources/js/UI/TimelineEvents.js?v=<?=$debugTime?>" type="text/javascript"></script>
Expand Down
2 changes: 1 addition & 1 deletion resources/build/build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

<echo>concatenating JavaScript...</echo>
<concat destfile="resources/compressed/helioviewer.js" encoding="UTF-8" outputencoding="UTF-8" fixlastline="true" eol="lf">
<filelist dir="resources/js" files="UI/TileLayerData.js, Utility/Config.js, Utility/HelperFunctions.js, Tiling/Layer/Layer.js, Tiling/Layer/TileLoader.js, Tiling/Layer/TileLayer.js, Tiling/Layer/HelioviewerTileLayer.js, Utility/KeyboardManager.js, Tiling/Manager/LayerManager.js, Tiling/Manager/TileLayerManager.js, Tiling/Manager/HelioviewerTileLayerManager.js, Image/JP2Image.js, Viewport/Helper/MouseCoordinates.js, Viewport/Helper/HelioviewerMouseCoordinates.js, Viewport/Helper/SandboxHelper.js, Viewport/Helper/ViewportMovementHelper.js, Viewport/Helper/TouchMover.js, Viewport/Helper/PinchDetector.js, Viewport/Helper/ScrollZoom.js, Viewport/Helper/HelioviewerZoomer.js, Viewport/HelioviewerViewport.js, HelioviewerClient.js, UI/ImageScale.js, UI/Timeline.js, UI/TimelineEvents.js, Utility/InputValidator.js, Utility/SettingsLoader.js, Utility/UserSettings.js, Tiling/Manager/LayerManager.js, Events/SelectedEventsCache.js, Events/EventManager.js, Events/EventType.js, Events/EventTree.js, Events/EventFeatureRecognitionMethod.js, Events/EventLayerManager.js, Events/EventLayerManager.js, Events/HelioviewerEventLayer.js, Events/HelioviewerEventLayerManager.js, UI/TreeSelect.js, UI/ImageSelectTool.js, Media/MediaManager.js, Media/MovieManager.js, Media/ScreenshotManager.js, Media/ScreenshotManagerUI.js, UI/TileLayerAccordion.js, UI/TimeSelector.js, UI/EventLayerAccordion.js, UI/MessageConsole.js, UI/TimeControls.js, Utility/FullscreenControl.js, Utility/Tutorial.js, HelioviewerWebClient.js, UI/UserVideoGallery.js, UI/ImagePresets.js, UI/Glossary.js, UI/jquery.ui.dynaccordion.js, Viewport/CelestialBodiesSatellites.js Patches/broken_screenshots_493.js"/>
<filelist dir="resources/js" files="UI/TileLayerData.js, Utility/Config.js, Utility/HelperFunctions.js, Tiling/Layer/Layer.js, Tiling/Layer/TileLoader.js, Tiling/Layer/TileLayer.js, Tiling/Layer/HelioviewerTileLayer.js, Utility/KeyboardManager.js, Tiling/Manager/LayerManager.js, Tiling/Manager/TileLayerManager.js, Tiling/Manager/HelioviewerTileLayerManager.js, Image/JP2Image.js, Viewport/Helper/MouseCoordinates.js, Viewport/Helper/HelioviewerMouseCoordinates.js, Viewport/Helper/SandboxHelper.js, Viewport/Helper/ViewportMovementHelper.js, Viewport/Helper/TouchMover.js, Viewport/Helper/PinchDetector.js, UI/ZoomControls.js, Viewport/Helper/ScrollZoom.js, Viewport/Helper/HelioviewerZoomer.js, Viewport/HelioviewerViewport.js, HelioviewerClient.js, UI/ImageScale.js, UI/Timeline.js, UI/TimelineEvents.js, Utility/InputValidator.js, Utility/SettingsLoader.js, Utility/UserSettings.js, Tiling/Manager/LayerManager.js, Events/SelectedEventsCache.js, Events/EventManager.js, Events/EventType.js, Events/EventTree.js, Events/EventFeatureRecognitionMethod.js, Events/EventLayerManager.js, Events/EventLayerManager.js, Events/HelioviewerEventLayer.js, Events/HelioviewerEventLayerManager.js, UI/TreeSelect.js, UI/ImageSelectTool.js, Media/MediaManager.js, Media/MovieManager.js, Media/ScreenshotManager.js, Media/ScreenshotManagerUI.js, UI/TileLayerAccordion.js, UI/TimeSelector.js, UI/EventLayerAccordion.js, UI/MessageConsole.js, UI/TimeControls.js, Utility/FullscreenControl.js, Utility/Tutorial.js, HelioviewerWebClient.js, UI/UserVideoGallery.js, UI/ImagePresets.js, UI/Glossary.js, UI/jquery.ui.dynaccordion.js, Viewport/CelestialBodiesSatellites.js Patches/broken_screenshots_493.js"/>
</concat>

<echo>concatenating CSS...</echo>
Expand Down
2 changes: 1 addition & 1 deletion resources/js/UI/EventLayerAccordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ var EventLayerAccordion = Layer.extend(
_createK12VisibilityBtn: function(index, id, name, markersVisible, labelsVisible, startOpened, apiSource) {
var visibilityBtn, labelsBtn, availableBtn/*, removeBtn*/, markersHidden, labelsHidden, availableHidden, eventsDiv, self=this;

let treeid = 'tree-'+id;
let treeid = 'tree_'+name;

var visState = Helioviewer.userSettings.get("state.eventLayerAvailableVisible");
if ( typeof visState == 'undefined') {
Expand Down
4 changes: 4 additions & 0 deletions resources/js/UI/ImagePresets.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ var UserLayersPresets = Class.extend({
layers = layers.slice(1, -1);
settings['imageLayers'] = layers.split("],[");

if (typeof helioviewer.viewport._tileLayerManager == "undefined") {
return;
}

helioviewer.viewport._tileLayerManager.each(function(){
$(document).trigger("remove-tile-layer", [this.id]);
$("#" + this.id + " *[oldtitle]").qtip("destroy");
Expand Down
77 changes: 77 additions & 0 deletions resources/js/UI/ZoomControls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* @fileOverview Contains the class definition for an ZoomControls class.
* @author <a href="mailto:[email protected]">Jeff Stys</a>
* @author <a href="mailto:[email protected]">Keith Hughitt</a>
* @author <a href="mailto:[email protected]">Daniel Garcia Briseno</a>
*/
/*jslint browser: true, white: true, onevar: true, undef: true, nomen: false,
eqeqeq: true, plusplus: true, bitwise: true, regexp: false, strict: true,
newcap: true, immed: true, maxlen: 80, sub: true */
/*globals $, Class */
"use strict";

class ZoomControls {
/**
* @constructs
*
* Creates a new ZoomControl
* @param {number} maxImageScale Maximum image scale.
* @param {number} nLevels Number of zoom levels
* @param {() => void} slideStart
* @param {(percentage: number) => void} setZoomLevel
*/
constructor(maxImageScale, nLevels, slideStart, setZoomLevel) {
this._maxImageScale = maxImageScale;
this.nLevels = nLevels
this.slideStart = slideStart;
this.setZoomLevel = setZoomLevel;

this.zoomSlider = $('#zoomControlSlider');
$(document).bind("helioviewer-ready", this._initSlider.bind(this));
$(document).bind("update-viewport", this._updateSliderValue.bind(this));
}

get _currentLevel() {
return Math.log(helioviewer.getZoomedImageScale() / this._maxImageScale) / Math.log(0.5);
}

/**
* Update the slider value
*/
_updateSliderValue() {
let currentValue = this.zoomSlider.slider("value");
// Only change the value if it is significantly different.
// Otherwise the slider value is changed, which triggers a zoom update
// which triggers a slider update, and we have an infinite loop.
let dt = Math.abs(currentValue - this._currentLevel);
if (dt > 0.01) {
this.zoomSlider.slider("value", this._currentLevel);
}
}

/**
* @description Initializes zoom level slider
*/
_initSlider() {
// Initialize slider
this.zoomSlider.slider({
start: this.slideStart,
slide: this.onslide.bind(this),
min: 0,
max: this.nLevels,
step: 1,
orientation: 'vertical',
value: this._currentLevel
});

// Add tooltip
let description = "Drag this handle up and down to zoom in and out of " +
"the displayed image.";
$("#zoomControlSlider > .ui-slider-handle").attr('title', description)
.qtip();
}

onslide(event, slider) {
this.setZoomLevel(slider.value);
}
}
9 changes: 6 additions & 3 deletions resources/js/Viewport/HelioviewerViewport.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ var HelioviewerViewport = Class.extend(
$(this.domNode).bind("mousedown", $.proxy(this.onMouseMove, this));
this.domNode.dblclick($.proxy(this.doubleClick, this));

$('#center-button').click($.proxy(this.centerViewportOnBiggestLayer, this));
$('#center-button').click(() => {$(document).trigger('center-viewport');});
$(window).resize($.proxy(this.resize, this));
},

Expand Down Expand Up @@ -399,8 +399,11 @@ var HelioviewerViewport = Class.extend(
* @param {Event} e Event class
*/
doubleClick: function (event) {
this.movementHelper.doubleClick(event);

let anchor = {
left: event.pageX,
top: event.pageY
};
this.helioZoom.setAnchorForCenter(anchor);
if (event.shiftKey) {
$("#zoom-out-button").click();
} else {
Expand Down
89 changes: 74 additions & 15 deletions resources/js/Viewport/Helper/HelioviewerZoomer.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
const MIN_THRESHOLD = 0.5;
const MAX_THRESHOLD = 1.5;

/**
* This module helps with handling smooth zoom scaling via
* CSS transforms.
Expand All @@ -14,16 +17,44 @@
this._initZoomLevel();
this._initializePinchListeners();
this._zoomInBtn = document.getElementById('zoom-in-button');
this._zoomInBtn.addEventListener('click', this._smoothZoomIn.bind(this));
this._zoomOutBtn = document.getElementById('zoom-out-button');
this._zoomOutBtn.addEventListener('click', this._smoothZoomOut.bind(this));
this._mc = document.getElementById('moving-container');
this._sandbox = document.getElementById('sandbox');
this._scale = 1;
this._anchor = {left: 0, top: 0};
this._last_size = 0;
this._css_rules = [];
this._maxImageScale = zoomLevels[0];
this._minImageScale = zoomLevels[zoomLevels.length - 1];
this._slider = new ZoomControls(this._maxImageScale, zoomLevels.length - 1, this._targetCenter.bind(this), this.jumpToZoomLevel.bind(this));
Helioviewer.userSettings.set('mobileZoomScale', 1);

// Make sure the sun is centered when the user requests centering the viewport
$(document).bind("center-viewport", this._resetOrigin.bind(this));

// Register zoom in button click and zoom-in event
this._zoomInBtn.addEventListener('click', this._smoothZoomIn.bind(this));
$(document).bind("zoom-in", this._smoothZoomIn.bind(this));

// Register zoom out button click and zoom-out event.
this._zoomOutBtn.addEventListener('click', this._smoothZoomOut.bind(this));
$(document).bind("zoom-out", this._smoothZoomOut.bind(this));
}

/**
* Sets the anchor to the center of the viewport
*/
_targetCenter() {
let center = {left: window.innerWidth / 2, top: window.innerHeight / 2};
this.setAnchorForCenter(center);
}

/**
* Resets the transform origin property from the moving container
* so that the transform origin is at sun center.
*/
_resetOrigin() {
this.setAnchor({left: 0, top: 0});
}

/**
Expand Down Expand Up @@ -82,7 +113,6 @@
let new_top = apparent_y + (targetScale - 1) * new_anchor_y;

// Sandbox may shift, account for this by tracking its position.
// TODO: I'll have to deal with this later.
let initial_sandbox_position = $(this._sandbox).position();

let closure = () => {
Expand Down Expand Up @@ -111,10 +141,7 @@
*/
_zoomIn() {
let nextValue = this._zoomIndex + 1;
if (nextValue < this.zoomLevels.length) {
this._zoomIndex = nextValue;
this._setAppImageScale(this._zoomIndex);
}
this._setZoomLevel(nextValue);
}

/**
Expand All @@ -123,8 +150,18 @@
*/
_zoomOut() {
let nextValue = this._zoomIndex - 1;
if (nextValue >= 0) {
this._zoomIndex = nextValue;
this._setZoomLevel(nextValue);
}

/**
* Forces the application zoom resolution to the given level
* @param {number} level index into zoomLevels. Lower is lower res, higher is higher res.
*/
_setZoomLevel(level) {
// Enforce that the given value is an integer
level = Math.ceil(level);
if (0 <= level && level < this.zoomLevels.length) {
this._zoomIndex = level;
this._setAppImageScale(this._zoomIndex);
}
}
Expand Down Expand Up @@ -157,9 +194,9 @@
setScale(scale) {
// Limit scale to 2.5 and 0.25
if (0.25 <= scale && scale <= 2.5) {
if (scale >= 1.5) {
if (scale >= MAX_THRESHOLD) {
this._zoomHelioviewer(scale, true);
} else if (scale <= 0.5) {
} else if (scale <= MIN_THRESHOLD) {
this._zoomHelioviewer(scale, false);
} else {
Helioviewer.userSettings.set('mobileZoomScale', scale);
Expand Down Expand Up @@ -265,6 +302,7 @@
* @param {number} duration Length of animation in seconds
*/
_animateZoom(factor, duration) {
clearInterval(this._animate_interval);
// Compute animation frame details.
let fps = 120;
let frame_delay = 1/fps;
Expand All @@ -276,24 +314,45 @@
let frame_delta = delta_scale / num_frames;

let ticks = 0;
let interval = setInterval(() => {
this._animate_interval = setInterval(() => {
let lastScale = this._scale;
this.setScale(this._scale + frame_delta);
if ((factor > 1) && (this._scale < lastScale)) {
frame_delta /= 2;
}
if ((factor < 1) && (this._scale > lastScale)) {
frame_delta *= 2;
}
ticks += 1;
if (ticks == num_frames) { clearInterval(interval); }
if (ticks == num_frames) { clearInterval(this._animate_interval); }
}, frame_delay)
}

/**
* Executed when the zoom in button is clicked.
*/
_smoothZoomIn() {
this._animateZoom(2, 0.25);
this._animateZoom(2, 0.2);
}

/**
* Executed when the zoom out button is clicked.
*/
_smoothZoomOut() {
this._animateZoom(0.5, 0.25);
this._animateZoom(0.5, 0.2);
}

/**
* Sets the zoom to the given percentage without animation.
* @note This is used by the slider in mininal view for animating zoom.
* @param {number} level Continuous index into zoom levels (can be decimal)
*/
jumpToZoomLevel(level) {
while (level > this._zoomIndex) {
this._zoomHelioviewer(2, true);
}
while (level < this._zoomIndex) {
this._zoomHelioviewer(0.5, false);
}
}
};
6 changes: 3 additions & 3 deletions resources/js/Viewport/Helper/MouseCoordinates.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ var MouseCoordinates = Class.extend(

//scale
let zoom = (Helioviewer.userSettings.get('mobileZoomScale') || 1);
scale = this.imageScale / zoom;
let scale = this.imageScale / zoom;
// TODO: Apply scaling fix depending on the current layer
x = scale * mouse_pos.x;
y = scale * mouse_pos.y;
let x = scale * mouse_pos.x;
let y = scale * mouse_pos.y;
let correctedCoord = this.correctCoordinate(scale, mouse_pos.x, -mouse_pos.y)

// Return scaled coords
Expand Down
Loading

0 comments on commit 8584556

Please sign in to comment.