mirror of
				https://github.com/imfing/hextra.git
				synced 2025-10-31 09:54:50 -04:00 
			
		
		
		
	feat(image-zoom): add minimal image zoom functionality
- Introduced CSS for image zoom overlay and image styling. - Implemented JavaScript for handling image zoom interactions, including overlay creation and close functionality. - Updated configuration to enable image zoom feature in site parameters. - Added partial for including image zoom assets in the layout.
This commit is contained in:
		
							
								
								
									
										42
									
								
								assets/css/components/image-zoom.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								assets/css/components/image-zoom.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| /* Minimal styles for Hextra image zoom overlay */ | ||||
| .hextra-zoom-image-overlay { | ||||
|   position: fixed; | ||||
|   inset: 0; | ||||
|   background: var(--hextra-image-zoom-backdrop, rgba(0, 0, 0, 0.9)); | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   z-index: 9999; | ||||
|   opacity: 0; | ||||
|   transition: opacity 0.25s ease-out; | ||||
|   cursor: zoom-out; | ||||
| } | ||||
|  | ||||
| .hextra-zoom-image-overlay.show { | ||||
|   opacity: 1; | ||||
| } | ||||
|  | ||||
| .hextra-zoom-image { | ||||
|   max-width: min(95vw, 1200px); | ||||
|   max-height: 95vh; | ||||
|   border-radius: 8px; | ||||
|   box-shadow: 0 8px 40px rgba(0, 0, 0, 0.35); | ||||
|   transition: transform 0.3s ease-out; | ||||
|   transform: scale(0.98); | ||||
| } | ||||
|  | ||||
| .hextra-zoom-image-overlay.show .hextra-zoom-image { | ||||
|   transform: scale(1); | ||||
| } | ||||
|  | ||||
| @media (prefers-reduced-motion: reduce) { | ||||
|   .hextra-zoom-image-overlay, | ||||
|   .hextra-zoom-image { | ||||
|     transition: none !important; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* Show magnifier cursor over zoomable images in content */ | ||||
| .content img:not([data-no-zoom]) { | ||||
|   cursor: zoom-in; | ||||
| } | ||||
							
								
								
									
										76
									
								
								assets/js/image-zoom.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								assets/js/image-zoom.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| // Minimal, dependency-free image zoom for Hextra | ||||
| // - Activates on images inside `.content` | ||||
| // - Close on overlay click or Escape | ||||
| // - Opt-out with `data-no-zoom` on <img> | ||||
|  | ||||
| (function () { | ||||
|   function ready(fn) { | ||||
|     if (document.readyState === "loading") { | ||||
|       document.addEventListener("DOMContentLoaded", fn, { once: true }); | ||||
|     } else { | ||||
|       fn(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function createOverlay(src, alt) { | ||||
|     const overlay = document.createElement("div"); | ||||
|     overlay.className = "hextra-zoom-image-overlay"; | ||||
|     overlay.setAttribute("role", "dialog"); | ||||
|     overlay.setAttribute("aria-modal", "true"); | ||||
|  | ||||
|     const img = document.createElement("img"); | ||||
|     img.className = "hextra-zoom-image"; | ||||
|     img.src = src; | ||||
|     if (alt) img.alt = alt; | ||||
|  | ||||
|     overlay.appendChild(img); | ||||
|  | ||||
|     function close() { | ||||
|       overlay.classList.remove("show"); | ||||
|       document.documentElement.style.removeProperty("overflow"); | ||||
|       window.removeEventListener("keydown", onKeyDown, true); | ||||
|       overlay.addEventListener( | ||||
|         "transitionend", | ||||
|         () => overlay.remove(), | ||||
|         { once: true } | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     function onKeyDown(e) { | ||||
|       if (e.key === "Escape") close(); | ||||
|     } | ||||
|  | ||||
|     overlay.addEventListener("click", close, { once: true }); | ||||
|     window.addEventListener("keydown", onKeyDown, true); | ||||
|  | ||||
|     document.body.appendChild(overlay); | ||||
|     // lock scroll | ||||
|     document.documentElement.style.overflow = "hidden"; | ||||
|  | ||||
|     // trigger fade-in | ||||
|     requestAnimationFrame(() => overlay.classList.add("show")); | ||||
|   } | ||||
|  | ||||
|   ready(function () { | ||||
|     const container = document.querySelector(".content"); | ||||
|     if (!container) return; | ||||
|  | ||||
|     container.addEventListener( | ||||
|       "click", | ||||
|       function (e) { | ||||
|         const target = e.target; | ||||
|         if (!(target instanceof HTMLImageElement)) return; | ||||
|         if (target.dataset.noZoom === "" || target.dataset.noZoom === "true") return; | ||||
|  | ||||
|         // avoid following parent links when zooming | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
|  | ||||
|         const src = target.currentSrc || target.src; | ||||
|         if (!src) return; | ||||
|         createOverlay(src, target.alt || ""); | ||||
|       }, | ||||
|       true | ||||
|     ); | ||||
|   }); | ||||
| })(); | ||||
| @@ -238,3 +238,6 @@ params: | ||||
|       # inputPosition: top | ||||
|       # lang: en | ||||
|       # theme: noborder_dark | ||||
|  | ||||
|   imageZoom: | ||||
|     enable: true | ||||
|   | ||||
| @@ -13,3 +13,10 @@ | ||||
| {{- if (.Store.Get "hasAsciinema") -}} | ||||
|   {{- partial "scripts/asciinema.html" . -}} | ||||
| {{- end -}} | ||||
|  | ||||
| {{/* Image zoom */}} | ||||
| {{- with site.Params.imageZoom }} | ||||
|   {{- if .enable }} | ||||
|     {{- partial "scripts/image-zoom.html" $ -}} | ||||
|   {{- end -}} | ||||
| {{- end -}} | ||||
|   | ||||
							
								
								
									
										13
									
								
								layouts/_partials/scripts/image-zoom.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								layouts/_partials/scripts/image-zoom.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| {{/* Optional minimal image zoom assets */}} | ||||
| {{- $js := resources.Get "js/image-zoom.js" -}} | ||||
| {{- $css := resources.Get "css/components/image-zoom.css" -}} | ||||
|  | ||||
| {{- if hugo.IsProduction -}} | ||||
|   {{- $js = $js | minify | fingerprint -}} | ||||
|   {{- $css = $css | minify | fingerprint -}} | ||||
| {{- end -}} | ||||
|  | ||||
|  | ||||
| <link rel="preload" href="{{ $css.RelPermalink }}" as="style" integrity="{{ $css.Data.Integrity }}" /> | ||||
| <link href="{{ $css.RelPermalink }}" rel="stylesheet" integrity="{{ $css.Data.Integrity }}" /> | ||||
| <script defer src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}"></script> | ||||
		Reference in New Issue
	
	Block a user
	 Xin
					Xin