Skip to content

Commit 603cb4e

Browse files
committed
add zoom lock/unlock in image viewer
1 parent 5245fc4 commit 603cb4e

3 files changed

Lines changed: 118 additions & 55 deletions

File tree

assets/js/components/image_viewer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function initOSDViewer(el) {
4848
maxZoomPixelRatio: 4,
4949
minZoomLevel: 0.5,
5050
defaultZoomLevel: 0,
51-
gestureSettingsMouse: { scrollToZoom: true },
51+
gestureSettingsMouse: { scrollToZoom: false },
5252
tileSources: [iiifUrl],
5353
drawer: "canvas",
5454
});
Lines changed: 77 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,87 @@
1-
const buttons = [
1+
const zoomButtons = [
22
{ icon: "fa-plus", title: "Zoom in", action: (v) => v.viewport.zoomBy(1.5) },
3-
{ icon: "fa-minus", title: "Zoom out", action: (v) => v.viewport.zoomBy(0.667) },
3+
{
4+
icon: "fa-minus",
5+
title: "Zoom out",
6+
action: (v) => v.viewport.zoomBy(0.667),
7+
},
48
{ icon: "fa-home", title: "Reset view", action: (v) => v.viewport.goHome() },
5-
{ icon: "fa-expand", title: "Full screen", action: (v) => v.setFullScreen(!v.isFullPage()) },
9+
{
10+
icon: "fa-expand",
11+
title: "Full screen",
12+
action: (v) => v.setFullScreen(!v.isFullPage()),
13+
},
614
];
715

16+
function makeButton({ icon, title, onClick }) {
17+
const btn = document.createElement("button");
18+
btn.type = "button";
19+
btn.className = "osd-btn";
20+
btn.title = title;
21+
btn.innerHTML = `<i class="fas ${icon}"></i>`;
22+
btn.addEventListener("click", onClick);
23+
return btn;
24+
}
25+
26+
function setViewerInteractive(viewer, enabled) {
27+
const settings = {
28+
scrollToZoom: enabled,
29+
clickToZoom: enabled,
30+
dblClickToZoom: enabled,
31+
pinchToZoom: enabled,
32+
flickEnabled: enabled,
33+
};
34+
Object.assign(viewer.gestureSettingsMouse, settings);
35+
Object.assign(viewer.gestureSettingsTouch, settings);
36+
viewer.panHorizontal = enabled;
37+
viewer.panVertical = enabled;
38+
}
39+
840
export function addViewerButtons(viewer, container) {
9-
const group = document.createElement("div");
10-
Object.assign(group.style, {
11-
position: "absolute",
12-
top: "10px",
13-
right: "10px",
14-
zIndex: "10",
15-
borderRadius: "0.375rem",
16-
border: "1px solid rgba(0, 0, 0, 0.125)",
17-
overflow: "hidden",
18-
display: "flex",
19-
flexDirection: "column",
20-
});
41+
container.style.position = "relative";
2142

22-
for (const { icon, title, action } of buttons) {
23-
const btn = document.createElement("button");
24-
btn.type = "button";
25-
btn.title = title;
26-
btn.innerHTML = `<i class="fas ${icon}"></i>`;
27-
Object.assign(btn.style, {
28-
backgroundColor: "#fff",
29-
border: "none",
30-
borderBottom: "1px solid rgba(0, 0, 0, 0.125)",
31-
color: "#495057",
32-
fontSize: "0.875rem",
33-
width: "32px",
34-
height: "32px",
35-
display: "flex",
36-
alignItems: "center",
37-
justifyContent: "center",
38-
cursor: "pointer",
39-
transition: "all 0.15s ease-in-out",
40-
});
41-
btn.addEventListener("mouseenter", () => {
42-
btn.style.backgroundColor = "#f8f9fa";
43-
btn.style.color = "#212529";
44-
});
45-
btn.addEventListener("mouseleave", () => {
46-
btn.style.backgroundColor = "#fff";
47-
btn.style.color = "#495057";
48-
});
49-
btn.addEventListener("mousedown", () => {
50-
btn.style.backgroundColor = "#e9ecef";
51-
});
52-
btn.addEventListener("mouseup", () => {
53-
btn.style.backgroundColor = "#f8f9fa";
54-
});
55-
btn.addEventListener("click", () => action(viewer));
56-
group.appendChild(btn);
43+
// Start locked
44+
setViewerInteractive(viewer, false);
45+
46+
// --- Locked state: single unlock button ---
47+
const lockedGroup = document.createElement("div");
48+
lockedGroup.className = "osd-btn-group";
49+
lockedGroup.appendChild(
50+
makeButton({
51+
icon: "fa-search-plus",
52+
title: "Enable zoom",
53+
onClick: () => {
54+
setViewerInteractive(viewer, true);
55+
lockedGroup.style.display = "none";
56+
unlockedGroup.style.display = "flex";
57+
},
58+
}),
59+
);
60+
container.appendChild(lockedGroup);
61+
62+
// --- Unlocked state: zoom controls + lock button ---
63+
const unlockedGroup = document.createElement("div");
64+
unlockedGroup.className = "osd-btn-group";
65+
unlockedGroup.style.display = "none";
66+
67+
for (const { icon, title, action } of zoomButtons) {
68+
unlockedGroup.appendChild(
69+
makeButton({ icon, title, onClick: () => action(viewer) }),
70+
);
5771
}
5872

59-
// Remove border-bottom from last button
60-
group.lastElementChild.style.borderBottom = "none";
73+
unlockedGroup.appendChild(
74+
makeButton({
75+
icon: "fa-lock",
76+
title: "Lock view",
77+
onClick: () => {
78+
viewer.viewport.goHome();
79+
setViewerInteractive(viewer, false);
80+
unlockedGroup.style.display = "none";
81+
lockedGroup.style.display = "flex";
82+
},
83+
}),
84+
);
6185

62-
container.style.position = "relative";
63-
container.appendChild(group);
86+
container.appendChild(unlockedGroup);
6487
}

assets/styles/components/image-viewer.css

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,43 @@
1313
#osd-viewer[style*="aspect-ratio"] {
1414
height: auto;
1515
}
16+
17+
.osd-btn-group {
18+
position: absolute;
19+
top: 10px;
20+
right: 10px;
21+
z-index: 10;
22+
border-radius: 0.375rem;
23+
border: 1px solid rgba(0, 0, 0, 0.125);
24+
overflow: hidden;
25+
display: flex;
26+
flex-direction: column;
27+
}
28+
29+
.osd-btn {
30+
background-color: #fff;
31+
border: none;
32+
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
33+
color: #495057;
34+
font-size: 0.875rem;
35+
width: 32px;
36+
height: 32px;
37+
display: flex;
38+
align-items: center;
39+
justify-content: center;
40+
cursor: pointer;
41+
transition: all 0.15s ease-in-out;
42+
}
43+
44+
.osd-btn:last-child {
45+
border-bottom: none;
46+
}
47+
48+
.osd-btn:hover {
49+
background-color: #f8f9fa;
50+
color: #212529;
51+
}
52+
53+
.osd-btn:active {
54+
background-color: #e9ecef;
55+
}

0 commit comments

Comments
 (0)