mirror of
https://github.com/zulip/zulip.git
synced 2025-11-10 00:46:03 +00:00
lightbox: Replace lightbox_canvas with PanZoom library.
This PR changes how the Pan & Zoom feature of images displayed in the attachment lightbox are handled. The existing method of using a canvas element is replaced by the Panzoom library (timmywil/panzoom). This library is lightweight and has 0 transitive dependencies. This fixes #20759 where the issue is that the viewport of a zoomed image was not expanding to fill the available space on the page. Switching to this new library also solves several other UX issues: * Images are no longer blurred when in Pan & Zoom mode. * The zoom behavior itself uses focal point zooming: zooming occurs where the cursor is on the image instead of at the center of the image, reducing the need for extra panning. * CSS transitions are used for a more visually pleasing experience when switching images, toggling zoom off, etc. * The library has the potential to open other file types which leaves that option open for us in the future.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import panzoom from "@panzoom/panzoom";
|
||||
import $ from "jquery";
|
||||
|
||||
import render_lightbox_overlay from "../templates/lightbox_overlay.hbs";
|
||||
|
||||
import * as blueslip from "./blueslip";
|
||||
import {LightboxCanvas} from "./lightbox_canvas";
|
||||
import * as message_store from "./message_store";
|
||||
import * as overlays from "./overlays";
|
||||
import * as people from "./people";
|
||||
@@ -15,6 +15,64 @@ let is_open = false;
|
||||
// memoized instead of being looked up multiple times.
|
||||
const asset_map = new Map();
|
||||
|
||||
export class PanZoomControl {
|
||||
// Class for both initializing and controlling the
|
||||
// the pan/zoom functionality.
|
||||
constructor(container) {
|
||||
this.container = container;
|
||||
this.panzoom = panzoom(this.container, {
|
||||
disablePan: true,
|
||||
disableZoom: true,
|
||||
cursor: "auto",
|
||||
});
|
||||
|
||||
// keybinds
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (!overlays.lightbox_open()) {
|
||||
return;
|
||||
}
|
||||
switch (e.key) {
|
||||
case "Z":
|
||||
case "+":
|
||||
this.zoomIn();
|
||||
break;
|
||||
case "z":
|
||||
case "-":
|
||||
this.zoomOut();
|
||||
break;
|
||||
case "v":
|
||||
overlays.close_overlay("lightbox");
|
||||
break;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.panzoom.reset();
|
||||
}
|
||||
|
||||
disablePanZoom() {
|
||||
this.container.removeEventListener("wheel", this.panzoom.zoomWithWheel);
|
||||
this.panzoom.setOptions({disableZoom: true, disablePan: true, cursor: "auto"});
|
||||
this.reset();
|
||||
}
|
||||
|
||||
enablePanZoom() {
|
||||
this.panzoom.setOptions({disableZoom: false, disablePan: false, cursor: "move"});
|
||||
this.container.addEventListener("wheel", this.panzoom.zoomWithWheel);
|
||||
}
|
||||
|
||||
zoomIn() {
|
||||
this.panzoom.zoomIn();
|
||||
}
|
||||
|
||||
zoomOut() {
|
||||
this.panzoom.zoomOut();
|
||||
}
|
||||
}
|
||||
|
||||
export function clear_for_testing() {
|
||||
is_open = false;
|
||||
asset_map.clear();
|
||||
@@ -51,21 +109,10 @@ function display_image(payload) {
|
||||
$(".player-container").hide();
|
||||
$(".image-actions, .image-description, .download, .lightbox-canvas-trigger").show();
|
||||
|
||||
const lightbox_canvas = $(".lightbox-canvas-trigger").hasClass("enabled");
|
||||
|
||||
if (lightbox_canvas === true) {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.dataset.src = payload.source;
|
||||
|
||||
$("#lightbox_overlay .image-preview").html(canvas).show();
|
||||
const photo = new LightboxCanvas(canvas);
|
||||
photo.speed(2.3);
|
||||
} else {
|
||||
const img = new Image();
|
||||
img.src = payload.source;
|
||||
|
||||
$("#lightbox_overlay .image-preview").html(img).show();
|
||||
}
|
||||
const img_container = $("#lightbox_overlay .image-preview > .zoom-element");
|
||||
const img = new Image();
|
||||
img.src = payload.source;
|
||||
img_container.html(img).show();
|
||||
|
||||
$(".image-description .title").text(payload.title || "N/A");
|
||||
$(".image-description .user").text(payload.user);
|
||||
@@ -282,9 +329,15 @@ export function next() {
|
||||
|
||||
// this is a block of events that are required for the lightbox to work.
|
||||
export function initialize() {
|
||||
// Renders the DOM for the lightbox.
|
||||
const rendered_lightbox_overlay = render_lightbox_overlay();
|
||||
$("body").append(rendered_lightbox_overlay);
|
||||
|
||||
// Bind the pan/zoom control the newly created element.
|
||||
const pan_zoom_control = new PanZoomControl(
|
||||
$("#lightbox_overlay .image-preview > .zoom-element")[0],
|
||||
);
|
||||
|
||||
$("#main_div, #compose .preview_content").on("click", ".message_inline_image a", function (e) {
|
||||
// prevent the link from opening in a new page.
|
||||
e.preventDefault();
|
||||
@@ -308,6 +361,7 @@ export function initialize() {
|
||||
|
||||
$(".image-list .image.selected").removeClass("selected");
|
||||
$(this).addClass("selected");
|
||||
pan_zoom_control.reset();
|
||||
|
||||
const parentOffset = this.parentNode.clientWidth + this.parentNode.scrollLeft;
|
||||
// this is the left and right of the image compared to its parent.
|
||||
@@ -341,18 +395,15 @@ export function initialize() {
|
||||
});
|
||||
|
||||
$("#lightbox_overlay").on("click", ".lightbox-canvas-trigger", function () {
|
||||
let $img = $("#lightbox_overlay").find(".image-preview img");
|
||||
|
||||
if ($img.length) {
|
||||
$(this).addClass("enabled");
|
||||
// the `lightbox.open` function will see the enabled class and
|
||||
// enable the `LightboxCanvas` class.
|
||||
open($img);
|
||||
} else {
|
||||
$img = $($("#lightbox_overlay").find(".image-preview canvas")[0].image);
|
||||
const $img = $("#lightbox_overlay").find(".image-preview img");
|
||||
open($img);
|
||||
|
||||
if ($(this).hasClass("enabled")) {
|
||||
pan_zoom_control.disablePanZoom();
|
||||
$(this).removeClass("enabled");
|
||||
open($img);
|
||||
} else {
|
||||
pan_zoom_control.enablePanZoom();
|
||||
$(this).addClass("enabled");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user