Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions README-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
| 32 | timeline | 时间线 | |
| 33 | chat | 聊天 | |
| 34 | auto_number | 章节、表格、图片、代码块等自动编号 | |
| 35 | image_viewer | 图片查看器 | |
| 35 | image_see | 双击图片查看器 | |
| 36 | cjk_symbol_pairing | 中文符号配对 | |
| 37 | resize_table | 调整表格行高列宽 | |
| 38 | resize_image | 调整图片显示大小 | |
Expand All @@ -76,6 +76,7 @@
| 57 | hotkeys | 快捷键注册中心(高级) | |
| 58 | action_buttons | 于右下角添加功能按钮(高级) | |
| 59 | json_rpc | 外部操纵 Typora(高级) | × |
| 60 | image_viewer | 多图片查看器 | |

> 如果有需求或发现 BUG,欢迎 [提 issue](https://github.com/obgnail/typora_plugin/issues/new),欢迎 PR。如果觉得本项目对您有帮助,请不吝点亮一个 Star ⭐!

Expand Down Expand Up @@ -507,7 +508,24 @@ docker run -d --name plantuml-server -p 8080:8080 plantuml/plantuml-server:jetty



### image_viewer:图片查看器
### image_see:双击图片查看器

功能:鼠标左键双击查看图片,并支持缩放、拖动。

使用方式:

- 查看:鼠标左键双击已被渲染的img图片,即可弹出图片。
- 缩放:图片弹出后,可通过鼠标滚轮进行缩放。
- 拖动:图片弹出后,可按住鼠标左键然后拖动图片,即查看各处细节。
- 关闭:图片弹出后,可点击右上角的X或按Esc,即可关闭查看图片。

![image-see-default](./assets/image-see-default.png)
![image-see-scale](./assets/image-see-scale.png)
![image-see-drag](./assets/image-see-drag.png)



### image_viewer:多图片查看器

功能:一站式图片查看,并且提供简单图片编辑。

Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ English | [简体中文](https://github.com/obgnail/typora_plugin/blob/master/RE
| 32 | timeline | Timeline | |
| 33 | chat | Chat | |
| 34 | auto_number | Auto Numbering | |
| 35 | image_viewer | Image reviewer | |
| 35 | image_viewer | Multi-image reviewer | |
| 36 | cjk_symbol_pairing | Automatic completion of CJK symbols | |
| 37 | resize_table | Adjust table row height and column width | |
| 38 | resize_image | Adjust image display size | |
Expand All @@ -76,6 +76,7 @@ English | [简体中文](https://github.com/obgnail/typora_plugin/blob/master/RE
| 57 | hotkeys | Hotkey registration center (advanced) | |
| 58 | action_buttons | Add function buttons in the lower right corner (advanced) | |
| 59 | json_rpc | External control of Typora (advanced) | × |
| 60 | image_see | Double-click Image Viewer | |

> If you have other needs or find bugs, feel free to [open an issue](https://github.com/obgnail/typora_plugin/issues/new). PRs are also welcome. If you find this project helpful, please give me a star ⭐

Expand Down Expand Up @@ -468,6 +469,21 @@ Usage:

![image-reviewer](./assets/image-reviewer.png)

### image_see: Double-click Image Viewer

Function: Double-click with left mouse button to view images, supporting zoom and drag.

Usage:

- View: Double-click any rendered img image with the left mouse button to pop up the image.
- Zoom: After the image pops up, use the mouse scroll wheel to zoom in/out.
- Drag: After the image pops up, hold the left mouse button and drag to view details.
- Close: After the image pops up, click the X in the top right corner or press Esc to close.

![image-see-default](./assets/image-see-default.png)
![image-see-scale](./assets/image-see-scale.png)
![image-see-drag](./assets/image-see-drag.png)

### cjk_symbol_pairing

Function: Automatically pair symbols when typing `《 【 ( ‘ “ 「`.
Expand Down
Binary file added assets/image-see-default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/image-see-drag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/image-see-scale.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions plugin/global/settings/settings.default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4000,6 +4000,15 @@ DEFAULT_ICON = "\\f075"
TEMPLATE = "> [!NOTE]\n> Support Type: TIP、BUG、INFO、NOTE、QUOTE、EXAMPLE、CAUTION、FAILURE、WARNING、SUCCESS、QUESTION、ABSTRACT、IMPORTANT"


[image_see]
# ------------------- 基础 -------------------
# 启用插件
ENABLE = true

# 插件名称(为空则使用默认名)
NAME = ""


[test]
# =========== [ 基础 ] ===========
# 启用插件
Expand Down
250 changes: 250 additions & 0 deletions plugin/image_see.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
class ImageSeePlugin extends BasePlugin {
// 注册 CSS 样式
style = () => `
.image_see_overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.9);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
cursor: default;
}

.image_see_close {
position: absolute;
top: 20px;
right: 20px;
width: 24px;
height: 24px;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
border: 1px solid #050404ff;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 24px;
font-weight: bold;
color: #000;
z-index: 10000;
}

.image_see_close:hover {
background-color: rgba(255, 255, 255, 1);
}

.image_see_content {
position: relative;
overflow: visible;
transform-origin: center center;
}

.image_see_img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
cursor: grab;
}

.image_see_img:active {
cursor: grabbing;
}
`

// 初始化方法
init = () => {
// 初始化时可以做一些准备工作
}

// 处理插件逻辑,为所有图片注册双击事件
process = () => {
// 为所有已存在的 img 标签注册双击事件
this.registerDoubleClickEvents();

// 监听 DOM 变化,为新添加的图片也注册事件
this.observeDOMChanges();
}

// 为所有图片注册双击事件
registerDoubleClickEvents = () => {
// 获取所有非插件新增的 img 标签
const images = document.querySelectorAll('img:not(.image_see_img)');
images.forEach(img => {
// 移除可能存在的旧事件监听器
img.removeEventListener('dblclick', this.handleImageDoubleClick);
// 添加新的双击事件监听器
img.addEventListener('dblclick', this.handleImageDoubleClick);
});
}

// 监听 DOM 变化,为新添加的图片注册事件
observeDOMChanges = () => {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) { // 元素节点
// 检查是否是 img 标签
if (node.tagName === 'IMG' && !node.classList.contains('image_see_img')) {
node.addEventListener('dblclick', this.handleImageDoubleClick);
}
// 检查是否包含 img 标签
const images = node.querySelectorAll('img:not(.image_see_img)');
images.forEach(img => {
img.addEventListener('dblclick', this.handleImageDoubleClick);
});
}
});
});
});

// 观察整个文档的变化
observer.observe(document.body, {
childList: true,
subtree: true
});
}

// 处理图片双击事件
handleImageDoubleClick = (e) => {
// 确保点击的是图片元素
if (e.target && e.target.tagName === 'IMG' && !e.target.classList.contains('image_see_img')) {
this.showEnlargedImage(e.target);
}
}

// 显示放大图片
showEnlargedImage = (imgElement) => {
// 创建覆盖层
const overlay = document.createElement('div');
overlay.className = 'image_see_overlay';

// 创建关闭按钮
const closeButton = document.createElement('div');
closeButton.className = 'image_see_close';
closeButton.textContent = '×';

// 创建内容容器
const contentContainer = document.createElement('div');
contentContainer.className = 'image_see_content';

// 创建图片
const enlargedImg = document.createElement('img');
enlargedImg.className = 'image_see_img';
enlargedImg.src = imgElement.src;
enlargedImg.alt = imgElement.alt;

// 组装结构
contentContainer.appendChild(enlargedImg);
overlay.appendChild(closeButton);
overlay.appendChild(contentContainer);

// 初始化变量
let scale = 1;
let translateX = 0;
let translateY = 0;
let isDragging = false;
let startX = 0;
let startY = 0;
let startTranslateX = 0;
let startTranslateY = 0;

// 更新图片变换
const updateTransform = () => {
contentContainer.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;
};

// 计算初始缩放比例,确保图片在可视范围内最大尺寸展示
const calculateInitialScale = () => {
const viewportWidth = window.innerWidth; //窗口可视宽度
const viewportHeight = window.innerHeight; //窗口可视高度
const imgWidth = enlargedImg.naturalWidth; //图片原始宽度
const imgHeight = enlargedImg.naturalHeight; //图片原始高度

// 如果图片小于等于视口,按原图大小展示
if (imgWidth <= viewportWidth && imgHeight <= viewportHeight) {
scale = 1;
} else {
// 如果图片超过视口,计算缩放比例
const scaleX = viewportWidth / imgWidth;
const scaleY = viewportHeight / imgHeight;
scale = Math.min(scaleX, scaleY);
}

updateTransform();
};

// 关闭事件
closeButton.addEventListener('click', () => {
document.body.removeChild(overlay);
document.removeEventListener('keydown', handleEscKey);
});

// 按 ESC 键关闭
const handleEscKey = (e) => {
if (e.key === 'Escape') {
document.body.removeChild(overlay);
document.removeEventListener('keydown', handleEscKey);
}
};

document.addEventListener('keydown', handleEscKey);

// 鼠标滚轮缩放
contentContainer.addEventListener('wheel', (e) => {
e.preventDefault();

// 计算缩放比例
const scaleFactor = e.deltaY > 0 ? 0.9 : 1.1;
const newScale = Math.max(0.1, Math.min(5, scale * scaleFactor));

// 以图片中心为原点进行缩放,不需要调整位移
scale = newScale;
updateTransform();
});

// 鼠标拖动
enlargedImg.addEventListener('mousedown', (e) => {
e.preventDefault();
isDragging = true;
startX = e.clientX;
startY = e.clientY;
startTranslateX = translateX;
startTranslateY = translateY;
});

document.addEventListener('mousemove', (e) => {
if (!isDragging) return;

const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;

translateX = startTranslateX + deltaX;
translateY = startTranslateY + deltaY;

updateTransform();
});

document.addEventListener('mouseup', () => {
isDragging = false;
});

// 添加到文档
document.body.appendChild(overlay);

// 等待图片加载完成后计算初始缩放比例
if (enlargedImg.complete) {
calculateInitialScale();
} else {
enlargedImg.addEventListener('load', calculateInitialScale);
}
}
}

module.exports = {
plugin: ImageSeePlugin
}