From 2016bd4c6f966f2ac80e958656829371419d3bba Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Thu, 11 Jan 2024 19:30:44 -0400 Subject: content, thumbs, css, client: general reveal interaction refactor --- src/content/dependencies/image.js | 97 ++++++++++++++++++++++++++++----------- src/gen-thumbs.js | 6 +++ src/static/client3.js | 40 ++++++++++++---- src/static/site6.css | 49 ++++++++++++++++---- 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; -- cgit 1.3.0-6-gf8a5