-
Notifications
You must be signed in to change notification settings - Fork 5
Add ImageEnlarger feature with modal viewer #924
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
f81babd
a13e964
35a2346
e6653cc
c1e5246
9b68c78
2b8330d
259cc7a
1d4a041
7f74188
5c6daab
b5945cf
279dada
6f310f3
8ced3d0
9dba403
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2121,6 +2121,8 @@ async function main() { | |||||||||||||||||||||||||||||||||||||||||||
| }, {"ID": "CompareSource", "Type": "A", "Name": "比较代码"}, { | ||||||||||||||||||||||||||||||||||||||||||||
| "ID": "BBSPopup", "Type": "A", "Name": "讨论提醒" | ||||||||||||||||||||||||||||||||||||||||||||
| }, {"ID": "MessagePopup", "Type": "A", "Name": "短消息提醒"}, { | ||||||||||||||||||||||||||||||||||||||||||||
| "ID": "ImageEnlarger", "Type": "A", "Name": "图片放大功能" | ||||||||||||||||||||||||||||||||||||||||||||
| }, { | ||||||||||||||||||||||||||||||||||||||||||||
| "ID": "DebugMode", "Type": "A", "Name": "调试模式(仅供开发者使用)" | ||||||||||||||||||||||||||||||||||||||||||||
| }, { | ||||||||||||||||||||||||||||||||||||||||||||
| "ID": "SuperDebug", "Type": "A", "Name": "本地调试模式(仅供开发者使用) (未经授权的擅自开启将导致大部分功能不可用!)" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -5572,6 +5574,275 @@ int main() | |||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Image Enlargement Feature | ||||||||||||||||||||||||||||||||||||||||||||
| if (UtilityEnabled("ImageEnlarger")) { | ||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||
| // Add CSS styles for the enlarger | ||||||||||||||||||||||||||||||||||||||||||||
| let EnlargerStyle = document.createElement("style"); | ||||||||||||||||||||||||||||||||||||||||||||
| EnlargerStyle.textContent = ` | ||||||||||||||||||||||||||||||||||||||||||||
| .xmoj-image-preview { | ||||||||||||||||||||||||||||||||||||||||||||
| cursor: pointer; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| .xmoj-image-preview:hover { | ||||||||||||||||||||||||||||||||||||||||||||
| opacity: 0.8; | ||||||||||||||||||||||||||||||||||||||||||||
| transition: opacity 0.2s ease; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| .xmoj-image-preview::after { | ||||||||||||||||||||||||||||||||||||||||||||
| content: "点击放大"; | ||||||||||||||||||||||||||||||||||||||||||||
| position: absolute; | ||||||||||||||||||||||||||||||||||||||||||||
| background-color: rgba(0, 0, 0, 0.7); | ||||||||||||||||||||||||||||||||||||||||||||
| color: white; | ||||||||||||||||||||||||||||||||||||||||||||
| padding: 5px 10px; | ||||||||||||||||||||||||||||||||||||||||||||
| border-radius: 3px; | ||||||||||||||||||||||||||||||||||||||||||||
| font-size: 12px; | ||||||||||||||||||||||||||||||||||||||||||||
| white-space: nowrap; | ||||||||||||||||||||||||||||||||||||||||||||
| pointer-events: none; | ||||||||||||||||||||||||||||||||||||||||||||
| opacity: 0; | ||||||||||||||||||||||||||||||||||||||||||||
| transition: opacity 0.2s ease; | ||||||||||||||||||||||||||||||||||||||||||||
| top: 50%; | ||||||||||||||||||||||||||||||||||||||||||||
| left: 50%; | ||||||||||||||||||||||||||||||||||||||||||||
| transform: translate(-50%, -50%); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| .xmoj-image-preview:hover::after { | ||||||||||||||||||||||||||||||||||||||||||||
| opacity: 1; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
| .xmoj-image-preview::after { | |
| content: "点击放大"; | |
| position: absolute; | |
| background-color: rgba(0, 0, 0, 0.7); | |
| color: white; | |
| padding: 5px 10px; | |
| border-radius: 3px; | |
| font-size: 12px; | |
| white-space: nowrap; | |
| pointer-events: none; | |
| opacity: 0; | |
| transition: opacity 0.2s ease; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| } | |
| .xmoj-image-preview:hover::after { | |
| opacity: 1; | |
| } | |
Copilot
AI
Mar 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The close control is a <span> with click handling only; it is not keyboard-focusable and has no accessible name/role. Use a <button type='button'> (or add role='button', tabindex='0', and an aria-label) so screen readers and keyboard users can operate it reliably.
| let CloseButton = document.createElement("span"); | |
| CloseButton.className = "xmoj-image-modal-close"; | |
| let CloseButton = document.createElement("button"); | |
| CloseButton.className = "xmoj-image-modal-close"; | |
| CloseButton.type = "button"; | |
| CloseButton.setAttribute("aria-label", "关闭图片"); | |
| CloseButton.title = "关闭图片"; |
Copilot
AI
Mar 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ApplyEnlargerToImages() will also match the modal’s own <img> (and any images inside the modal) because it only excludes images whose parent has xmoj-image-modal. The modal image’s parent is xmoj-image-modal-content, so it gets the preview class and a click handler, which can reset zoom/reopen the modal when the user clicks the enlarged image. Exclude any image with closest('.xmoj-image-modal') (or explicitly skip .xmoj-image-modal-image) instead of checking only parentElement.
| !img.parentElement.classList.contains("xmoj-image-modal") && | |
| !img.closest(".xmoj-image-modal") && |
Copilot
AI
Mar 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Click handling uses img.src to open the modal. For responsive images (srcset/sizes) or some lazy-loading setups, img.currentSrc is the actually-displayed resource and may differ from img.src. Using currentSrc || src will make enlargement/download match what the user sees.
Copilot
AI
Mar 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MutationObserver callback calls ApplyEnlargerToImages(), which scans all images on every DOM mutation. On pages with frequent updates this can become an O(n) hot loop and noticeably degrade performance. Consider iterating only over mutation.addedNodes (and their descendant imgs), or throttling/debouncing the full scan.
Uh oh!
There was an error while loading. Please reload this page.