From 8b119d332b267cb84ca98017e5ed411a6c902118 Mon Sep 17 00:00:00 2001 From: Nicolas Ong Date: Sat, 1 Mar 2025 22:39:06 +0100 Subject: [PATCH] Adding an optional Javascript viewer --- css/portfolio.css | 51 ++++++++++++++++++++++ js/image_viewer.js | 103 +++++++++++++++++++++++++++++++++++++++++++++ lib/Image.php | 4 +- views/index.phtml | 3 +- 4 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 js/image_viewer.js diff --git a/css/portfolio.css b/css/portfolio.css index bc4b834..3069227 100644 --- a/css/portfolio.css +++ b/css/portfolio.css @@ -1,3 +1,54 @@ @charset "utf-8"; * { box-sizing: border-box; } + +/* Image viewer */ +.hidden { display: none; } +#viewer { + background-color: #0008; + height: 100%; + left: 0; + padding-top: 5%; + position: fixed; + top: 0; + text-align: center; + width: 100%; +} +.viewer_button { + cursor: pointer; + height: 2em; + position: fixed; + width: 2em; +} +#viewer > img { + border: solid 5px #eee; + box-shadow: #000a 0 5px 16px 0; + max-height: 90%; + max-width: 90%; +} +.viewer_button::before { + display: block; + color: #ddd; + font-size: 2em; + font-weight: bold; + height: 1em; + position: "fixed"; + width: 1em; +} +.viewer_button:hover::before { color: #fff; } +#close { + right: 2em; + top: 2em; +} +#close::before { + content: "x"; + right: 1em; + top: 1em; +} +#prev { + left: 2em; + top: calc(50% - 2em); +} +#prev::before { + content: "<<"; + left: 1em; diff --git a/js/image_viewer.js b/js/image_viewer.js new file mode 100644 index 0000000..c5a4275 --- /dev/null +++ b/js/image_viewer.js @@ -0,0 +1,103 @@ +function pop(event) { + var target = event.target.parentNode; + if (target.nodeName != "FIGURE") { + target = event.target; + } + var href = target.parentNode.dataset.href; + var alt = target.firstElementChild.alt; + var viewer = document.getElementById('viewer'); + var image = document.createElement('img'); + image.src = href; + image.alt = alt; + image.id = 'img' + target.parentNode.parentNode.id; + viewer.appendChild(image); + viewer.classList.remove('hidden'); +} + +function unpop(event) { + var viewer = document.getElementById('viewer'); + viewer.classList.add('hidden'); + if (viewer.lastChild.nodeName == 'IMG') { + viewer.removeChild(viewer.lastChild); + } +} + +function displayNeighbour(which) { + var viewer = document.getElementById('viewer'); + var current = viewer.lastChild; + if (current.nodeName != 'IMG') { + return; + } + var ref_image_container = document.getElementById(current.id.slice(3)) + var new_image_container = which == 'next' ? + ref_image_container.nextElementSibling : + ref_image_container.previousElementSibling; + if (new_image_container == null) { + return; + } + var new_image = new_image_container.firstElementChild; + current.src = new_image.dataset.href; + current.alt = new_image_container.firstElementChild.firstElementChild.firstElementChild.alt; + current.id = 'img' + new_image_container.id; +} + +function displayPrev(event) { + displayNeighbour('prev'); +} + +function displayNext(event) { + displayNeighbour('next'); +} + +/* Adds new HTML elements for interactive viewing */ +var viewer = document.createElement('div'); +viewer.id = 'viewer'; +viewer.classList.add('hidden'); + +var close = document.createElement('div'); +close.id = 'close'; +close.classList.add('viewer_button'); +close.addEventListener('click', unpop); +viewer.appendChild(close); + +/* not implemented yet */ +var prev = document.createElement('div'); +prev.id = 'prev'; +prev.classList.add('viewer_button'); +prev.addEventListener('click', displayPrev); +viewer.appendChild(prev); + +var next = document.createElement('div'); +next.id = 'next'; +next.classList.add('viewer_button'); +next.addEventListener('click', displayNext); +viewer.appendChild(next); +/* not implemented yet */ + +document.body.appendChild(viewer); +document.body.addEventListener( + 'keydown', + (event) => { + if (event.defaultPrevented) { + return; + } + switch (event.key) { + case "Escape": + unpop(); + break; + case "ArrowLeft": + displayPrev(); + break; + case "ArrowRight": + displayNext(); + break; + } + } +); + +/* Makes thumbnails interactive */ +for (var thumb of document.getElementsByClassName('thumbnail')) { + thumb.dataset.href = thumb.href; + thumb.href = 'javascript: void(0);'; + thumb.addEventListener('click', pop); +} diff --git a/lib/Image.php b/lib/Image.php index bb16a80..913bedc 100644 --- a/lib/Image.php +++ b/lib/Image.php @@ -2,6 +2,7 @@ final class Image { + public string $id; public string $full; public string $thumbnail; public string $title; @@ -9,8 +10,9 @@ final class Image public int $width; public int $height; - public function __construct(string $full, string $thumbnail, string $title, string $alt, int $width, int $height) + public function __construct(string $id, string $full, string $thumbnail, string $title, string $alt, int $width, int $height) { + $this->id = $id; $this->full = $full; $this->thumbnail = $thumbnail; $this->title = $title; diff --git a/views/index.phtml b/views/index.phtml index 0d54ebe..a379855 100644 --- a/views/index.phtml +++ b/views/index.phtml @@ -19,7 +19,7 @@

label?>