« get me outta code hell

content, thumbs, css, client: general reveal interaction refactor - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2024-01-11 19:30:44 -0400
committer(quasar) nebula <qznebula@protonmail.com>2024-01-11 19:30:44 -0400
commit2016bd4c6f966f2ac80e958656829371419d3bba (patch)
treee967e8ad8d7e4c8963c0f8d6f0b4f63cdd403daf /src
parentcb379596c5cbaeb98480cc727ea6639d938e28d9 (diff)
content, thumbs, css, client: general reveal interaction refactor
Diffstat (limited to 'src')
-rw-r--r--src/content/dependencies/image.js97
-rw-r--r--src/gen-thumbs.js6
-rw-r--r--src/static/client3.js40
-rw-r--r--src/static/site6.css49
4 files changed, 146 insertions, 46 deletions
diff --git a/src/content/dependencies/image.js b/src/content/dependencies/image.js
index 7ef50e5b..9de58dce 100644
--- a/src/content/dependencies/image.js
+++ b/src/content/dependencies/image.js
@@ -221,6 +221,10 @@ export default {
 
     let displaySrc = originalSrc;
 
+    // This is only distinguished from displaySrc by being a thumbnail,
+    // so it won't be set if thumbnails aren't available.
+    let revealSrc = null;
+
     // If thumbnails are available *and* being used, calculate thumbSrc,
     // and provide some attributes relevant to the large image overlay.
     if (hasThumbnails && slots.thumb) {
@@ -233,6 +237,17 @@ export default {
       displaySrc =
         to('thumb.path', mediaSrcJpeg);
 
+      if (willReveal) {
+        const miniSize =
+          getThumbnailEqualOrSmaller('mini', mediaSrc);
+
+        const mediaSrcJpeg =
+          mediaSrc.replace(/\.(png|jpg)$/, `.${miniSize}.jpg`);
+
+        revealSrc =
+          to('thumb.path', mediaSrcJpeg);
+      }
+
       const dimensions = getDimensionsOfImagePath(mediaSrc);
       const availableThumbs = getThumbnailsAvailableForDimensions(dimensions);
 
@@ -261,40 +276,66 @@ export default {
 
     if (!displaySrc) {
       return (
-        prepareVisible(
-          html.tag('img', imgAttributes)));
+        prepare(
+          html.tag('img', imgAttributes),
+          'visible'));
     }
 
-    const nonlazyHTML =
-      prepareVisible(
+    const images = {
+      displayStatic:
         html.tag('img',
           imgAttributes,
-          {src: displaySrc}));
+          {src: displaySrc}),
+
+      displayLazy:
+        slots.lazy &&
+          html.tag('img',
+            imgAttributes,
+            {class: 'lazy', 'data-original': displaySrc}),
+
+      revealStatic:
+        revealSrc &&
+          html.tag('img', {class: 'reveal-thumbnail'},
+            imgAttributes,
+            {src: revealSrc}),
+
+      revealLazy:
+        slots.lazy &&
+        revealSrc &&
+          html.tag('img', {class: 'reveal-thumbnail'},
+            imgAttributes,
+            {class: 'lazy', 'data-original': revealSrc}),
+    };
+
+    const staticImageContent =
+      html.tags([images.displayStatic, images.revealStatic]);
 
     if (slots.lazy) {
+      const lazyImageContent =
+        html.tags([images.displayLazy, images.revealLazy]);
+
       return html.tags([
         html.tag('noscript',
-          nonlazyHTML),
+          prepare(staticImageContent, 'visible')),
 
-        prepareHidden(
-          html.tag('img', {class: 'lazy'},
-            imgAttributes,
-            {'data-original': displaySrc})),
+        prepare(lazyImageContent, 'hidden'),
       ]);
     } else {
-      return nonlazyHTML;
+      return prepare(staticImageContent, 'visible');
     }
 
-    function prepareVisible(content) {
-      return prepare(content, false);
-    }
+    function prepare(imageContent, visibility) {
+      let wrapped = imageContent;
 
-    function prepareHidden(content) {
-      return prepare(content, true);
-    }
-
-    function prepare(content, hide) {
-      let wrapped = content;
+      if (willReveal) {
+        wrapped =
+          html.tags([
+            wrapped,
+            html.tag('span', {class: 'reveal-text-container'},
+              html.tag('span', {class: 'reveal-text'},
+                reveal)),
+          ]);
+      }
 
       wrapped =
         html.tag('div', {class: 'image-container'},
@@ -308,18 +349,18 @@ export default {
 
       if (willReveal) {
         wrapped =
-          html.tag('div', {class: 'reveal'}, [
-            wrapped,
-            html.tag('span', {class: 'reveal-text-container'},
-              html.tag('span', {class: 'reveal-text'},
-                reveal)),
-          ]);
+          html.tag('div', {class: 'reveal'},
+            images.revealStatic &&
+              {class: 'has-reveal-thumbnail'},
+
+            wrapped);
       }
 
       if (willSquare) {
         wrapped =
           html.tag('div', {class: 'square'},
-            hide && !willLink &&
+            visibility === 'hidden' &&
+            !willLink &&
               {class: 'js-hide'},
 
             html.tag('div', {class: 'square-content'},
@@ -331,7 +372,7 @@ export default {
           html.tag('a', {class: ['box', 'image-link']},
             linkAttributes,
 
-            hide &&
+            visibility === 'hidden' &&
               {class: 'js-hide'},
 
             wrapped);
diff --git a/src/gen-thumbs.js b/src/gen-thumbs.js
index 30e79b64..f0eb72a0 100644
--- a/src/gen-thumbs.js
+++ b/src/gen-thumbs.js
@@ -131,6 +131,12 @@ const thumbnailSpec = {
     size: 250,
     quality: 85,
   },
+
+  'mini': {
+    tackbust: 2,
+    size: 8,
+    quality: 95,
+  },
 };
 
 import {spawn} from 'node:child_process';
diff --git a/src/static/client3.js b/src/static/client3.js
index deeac6aa..1c813472 100644
--- a/src/static/client3.js
+++ b/src/static/client3.js
@@ -186,6 +186,7 @@ clientSteps.mutatePageContent.push(mutateCSSCompatibilityContent);
 const scriptedLinkInfo = initInfo('scriptedLinkInfo', {
   randomLinks: null,
   revealLinks: null,
+  revealContainers: null,
 
   nextNavLink: null,
   previousNavLink: null,
@@ -204,7 +205,11 @@ function getScriptedLinkReferences() {
     document.querySelectorAll('[data-random]');
 
   scriptedLinkInfo.revealLinks =
-    document.getElementsByClassName('reveal');
+    document.querySelectorAll('.reveal .image-inner-area');
+
+  scriptedLinkInfo.revealContainers =
+    Array.from(scriptedLinkInfo.revealLinks)
+      .map(link => link.closest('.reveal'));
 
   scriptedLinkInfo.nextNavLink =
     document.getElementById('next-button');
@@ -372,18 +377,29 @@ function addNavigationKeyPressListeners() {
 }
 
 function addRevealLinkClickListeners() {
-  for (const reveal of scriptedLinkInfo.revealLinks ?? []) {
-    reveal.addEventListener('click', (event) => {
-      if (!reveal.classList.contains('revealed')) {
-        reveal.classList.add('revealed');
-        event.preventDefault();
-        event.stopPropagation();
-        reveal.dispatchEvent(new CustomEvent('hsmusic-reveal'));
-      }
+  const info = scriptedLinkInfo;
+
+  for (const {revealLink, revealContainer} of stitchArrays({
+    revealLink: Array.from(info.revealLinks ?? []),
+    revealContainer: Array.from(info.revealContainers ?? []),
+  })) {
+    revealLink.addEventListener('click', (event) => {
+      handleRevealLinkClicked(event, revealLink, revealContainer);
     });
   }
 }
 
+function handleRevealLinkClicked(domEvent, _revealLink, revealContainer) {
+  if (revealContainer.classList.contains('revealed')) {
+    return;
+  }
+
+  revealContainer.classList.add('revealed');
+  domEvent.preventDefault();
+  domEvent.stopPropagation();
+  revealContainer.dispatchEvent(new CustomEvent('hsmusic-reveal'));
+}
+
 clientSteps.getPageReferences.push(getScriptedLinkReferences);
 clientSteps.addPageListeners.push(addRandomLinkListeners);
 clientSteps.addPageListeners.push(addNavigationKeyPressListeners);
@@ -1698,8 +1714,14 @@ function handleImageLinkClicked(evt) {
   if (evt.metaKey || evt.shiftKey || evt.altKey) {
     return;
   }
+
   evt.preventDefault();
 
+  // Don't show the overlay if the image still needs to be revealed.
+  if (evt.target.closest('a').querySelector('.reveal:not(.revealed)')) {
+    return;
+  }
+
   const container = document.getElementById('image-overlay-container');
   container.classList.add('visible');
   container.classList.remove('loaded');
diff --git a/src/static/site6.css b/src/static/site6.css
index a932dfe3..9996da68 100644
--- a/src/static/site6.css
+++ b/src/static/site6.css
@@ -1128,11 +1128,6 @@ img.pixelate {
   height: 100%;
 }
 
-.reveal .image {
-  filter: blur(20px);
-  opacity: 0.4;
-}
-
 .reveal-text-container {
   position: absolute;
   top: 15px;
@@ -1148,10 +1143,6 @@ img.pixelate {
   font-size: 0.9em;
 }
 
-.reveal:not(.revealed) .image-inner-area {
-  background: var(--deep-color);
-}
-
 .reveal-text {
   color: white;
   text-align: center;
@@ -1172,6 +1163,25 @@ img.pixelate {
 
 .reveal-interaction {
   opacity: 0.8;
+  text-decoration: underline;
+  text-decoration-style: dotted;
+}
+
+.reveal .image {
+  opacity: 0.7;
+  filter: blur(20px) brightness(0.7);
+}
+
+.reveal .image.reveal-thumbnail {
+  image-rendering: pixelated;
+}
+
+.reveal.has-reveal-thumbnail:not(.revealed) .image:not(.reveal-thumbnail) {
+  display: none !important;
+}
+
+.reveal.revealed.has-reveal-thumbnail .image.reveal-thumbnail {
+  display: none !important;
 }
 
 .reveal.revealed .image {
@@ -1183,6 +1193,27 @@ img.pixelate {
   display: none;
 }
 
+.reveal:not(.revealed) .image-inner-area {
+  background: var(--deep-color);
+
+  box-sizing: border-box;
+  border: 1px dotted var(--primary-color);
+  border-radius: 6px;
+}
+
+.reveal .image-inner-area:hover .reveal-interaction {
+  text-decoration-style: solid;
+}
+
+.reveal:not(.revealed) .image-inner-area:hover {
+  border-style: solid;
+}
+
+.reveal:not(.revealed) .image-inner-area:hover .image {
+  filter: blur(20px) brightness(0.6);
+  opacity: 0.6;
+}
+
 .image-link:not(.no-image-preview) .image-container {
   background: var(--deep-color);
   box-shadow: none;