Created
April 30, 2025 07:34
-
-
Save PrintNow/51a80f76ab4e7e0ebc011be8e91f732c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="zh-CN"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>图片查看器插件</title> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
margin: 0; | |
padding: 20px; | |
} | |
.post-content { | |
max-width: 800px; | |
margin: 0 auto; | |
border: 1px solid #ddd; | |
padding: 20px; | |
} | |
.post-content img { | |
max-width: 100%; | |
cursor: pointer; | |
} | |
/* 图片查看器样式 */ | |
.img-viewer-overlay { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background-color: rgba(0, 0, 0, 0.9); | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
z-index: 9999; | |
opacity: 0; | |
transition: opacity 0.3s; | |
pointer-events: none; | |
} | |
.img-viewer-overlay.active { | |
opacity: 1; | |
pointer-events: all; | |
} | |
.img-viewer-container { | |
position: relative; | |
width: 100%; | |
height: 100%; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
} | |
.img-viewer-img { | |
max-width: 90vw; | |
max-height: 80vh; | |
object-fit: contain; | |
transform-origin: center; | |
transition: transform 0.2s; | |
} | |
.img-viewer-close { | |
position: absolute; | |
top: 15px; | |
right: 15px; | |
color: white; | |
font-size: 30px; | |
cursor: pointer; | |
width: 40px; | |
height: 40px; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
background-color: rgba(0, 0, 0, 0.5); | |
border-radius: 50%; | |
z-index: 10000; | |
} | |
.img-viewer-controls { | |
position: fixed; | |
bottom: 20px; | |
left: 0; | |
width: 100%; | |
display: flex; | |
justify-content: center; | |
gap: 15px; | |
z-index: 10000; | |
} | |
.img-viewer-btn { | |
background-color: rgba(255, 255, 255, 0.2); | |
color: white; | |
border: none; | |
border-radius: 5px; | |
padding: 8px 15px; | |
cursor: pointer; | |
font-size: 16px; | |
transition: background-color 0.2s; | |
} | |
.img-viewer-btn:hover { | |
background-color: rgba(255, 255, 255, 0.3); | |
} | |
</style> | |
</head> | |
<body> | |
<h1>图片查看器插件演示</h1> | |
<div class="post-content" itemprop="articleBody"> | |
<h2>示例文章</h2> | |
<p>这是一个示例文章,包含多张图片。点击图片可以放大查看。</p> | |
<p><img src="https://picsum.photos/id/1015/600/400" alt="示例图片1"></p> | |
<p>这是第一张示例图片的说明文字。</p> | |
<p><img src="https://picsum.photos/id/1016/600/400" alt="示例图片2"></p> | |
<p>这是第二张示例图片的说明文字。</p> | |
<p><img src="https://picsum.photos/id/1018/600/400" alt="示例图片3"></p> | |
<p>这是第三张示例图片的说明文字。</p> | |
</div> | |
<script> | |
/** | |
* 图片查看器插件 | |
* 功能: | |
* 1. 点击图片放大显示 | |
* 2. 滚轮缩放 | |
* 3. 放大、缩小、上一张、下一张按钮 | |
* 4. 键盘方向键切换图片 | |
* 5. ESC键、点击关闭按钮或点击非图片区域关闭 | |
*/ | |
class ImageViewer { | |
constructor(selector) { | |
// 初始化变量 | |
this.containerSelector = selector; | |
this.container = document.querySelector(selector); | |
this.images = this.container.querySelectorAll('img'); | |
this.currentIndex = 0; | |
this.scale = 1; | |
this.minScale = 0.5; | |
this.maxScale = 3; | |
this.scaleStep = 0.2; | |
// 创建查看器DOM | |
this.createViewerDOM(); | |
// 绑定事件 | |
this.bindEvents(); | |
} | |
createViewerDOM() { | |
// 创建遮罩层 | |
this.overlay = document.createElement('div'); | |
this.overlay.className = 'img-viewer-overlay'; | |
// 创建图片容器 | |
this.imageContainer = document.createElement('div'); | |
this.imageContainer.className = 'img-viewer-container'; | |
// 创建图片元素 | |
this.viewerImage = document.createElement('img'); | |
this.viewerImage.className = 'img-viewer-img'; | |
// 创建关闭按钮 | |
this.closeBtn = document.createElement('div'); | |
this.closeBtn.className = 'img-viewer-close'; | |
this.closeBtn.innerHTML = '×'; | |
// 创建控制按钮 | |
this.controls = document.createElement('div'); | |
this.controls.className = 'img-viewer-controls'; | |
// 创建各个控制按钮 | |
this.prevBtn = this.createButton('上一张', 'prev'); | |
this.zoomOutBtn = this.createButton('缩小', 'zoom-out'); | |
this.zoomInBtn = this.createButton('放大', 'zoom-in'); | |
this.nextBtn = this.createButton('下一张', 'next'); | |
// 组装DOM | |
this.controls.appendChild(this.prevBtn); | |
this.controls.appendChild(this.zoomOutBtn); | |
this.controls.appendChild(this.zoomInBtn); | |
this.controls.appendChild(this.nextBtn); | |
this.imageContainer.appendChild(this.viewerImage); | |
this.overlay.appendChild(this.closeBtn); | |
this.overlay.appendChild(this.imageContainer); | |
this.overlay.appendChild(this.controls); | |
// 添加到body | |
document.body.appendChild(this.overlay); | |
} | |
createButton(text, action) { | |
const btn = document.createElement('button'); | |
btn.className = 'img-viewer-btn'; | |
btn.dataset.action = action; | |
btn.textContent = text; | |
return btn; | |
} | |
bindEvents() { | |
// 图片点击事件 | |
this.images.forEach((img, index) => { | |
img.addEventListener('click', () => { | |
this.open(index); | |
}); | |
}); | |
// 关闭按钮点击事件 | |
this.closeBtn.addEventListener('click', () => { | |
this.close(); | |
}); | |
// 遮罩层点击事件(点击非图片区域关闭) | |
this.overlay.addEventListener('click', (e) => { | |
if (e.target === this.overlay) { | |
this.close(); | |
} | |
}); | |
// 阻止图片点击冒泡到遮罩层 | |
this.viewerImage.addEventListener('click', (e) => { | |
e.stopPropagation(); | |
}); | |
// 控制按钮点击事件 - 使用事件委托优化性能 | |
this.controls.addEventListener('click', (e) => { | |
const button = e.target.closest('button'); | |
if (!button) return; | |
const action = button.dataset.action; | |
switch (action) { | |
case 'prev': | |
this.prev(); | |
break; | |
case 'next': | |
this.next(); | |
break; | |
case 'zoom-in': | |
this.zoomIn(); | |
break; | |
case 'zoom-out': | |
this.zoomOut(); | |
break; | |
} | |
}); | |
// 使用节流函数优化滚轮事件 | |
const throttledWheel = this.throttle((e) => { | |
e.preventDefault(); | |
if (e.deltaY < 0) { | |
this.zoomIn(); | |
} else { | |
this.zoomOut(); | |
} | |
}, 100); | |
this.overlay.addEventListener('wheel', throttledWheel, { passive: false }); | |
// 键盘事件 - 使用防抖函数优化 | |
const debouncedKeydown = this.debounce((e) => { | |
if (!this.overlay.classList.contains('active')) return; | |
switch (e.key) { | |
case 'Escape': | |
this.close(); | |
break; | |
case 'ArrowLeft': | |
this.prev(); | |
break; | |
case 'ArrowRight': | |
this.next(); | |
break; | |
case '+': | |
this.zoomIn(); | |
break; | |
case '-': | |
this.zoomOut(); | |
break; | |
} | |
}, 100); | |
document.addEventListener('keydown', debouncedKeydown); | |
// 窗口大小变化事件 - 使用防抖函数优化 | |
const debouncedResize = this.debounce(() => { | |
if (this.overlay.classList.contains('active')) { | |
this.updateZoom(); | |
} | |
}, 200); | |
window.addEventListener('resize', debouncedResize); | |
} | |
// 防抖函数 - 优化性能 | |
debounce(func, delay) { | |
let timeout; | |
return function(...args) { | |
clearTimeout(timeout); | |
timeout = setTimeout(() => func.apply(this, args), delay); | |
}; | |
} | |
// 节流函数 - 优化性能 | |
throttle(func, limit) { | |
let inThrottle; | |
return function(...args) { | |
if (!inThrottle) { | |
func.apply(this, args); | |
inThrottle = true; | |
setTimeout(() => inThrottle = false, limit); | |
} | |
}; | |
} | |
open(index) { | |
this.currentIndex = index; | |
this.scale = 1; | |
this.updateImage(); | |
this.overlay.classList.add('active'); | |
document.body.style.overflow = 'hidden'; // 防止页面滚动 | |
} | |
close() { | |
this.overlay.classList.remove('active'); | |
document.body.style.overflow = ''; // 恢复页面滚动 | |
} | |
prev() { | |
this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length; | |
this.scale = 1; | |
this.updateImage(); | |
} | |
next() { | |
this.currentIndex = (this.currentIndex + 1) % this.images.length; | |
this.scale = 1; | |
this.updateImage(); | |
} | |
zoomIn() { | |
if (this.scale < this.maxScale) { | |
this.scale += this.scaleStep; | |
this.updateZoom(); | |
} | |
} | |
zoomOut() { | |
if (this.scale > this.minScale) { | |
this.scale -= this.scaleStep; | |
this.updateZoom(); | |
} | |
} | |
updateImage() { | |
const img = this.images[this.currentIndex]; | |
this.viewerImage.src = img.src; | |
this.viewerImage.alt = img.alt; | |
// 等待图片加载完成 | |
this.viewerImage.onload = () => { | |
this.updateZoom(); | |
}; | |
} | |
updateZoom() { | |
this.viewerImage.style.transform = `scale(${this.scale})`; | |
} | |
} | |
// 初始化插件 | |
document.addEventListener('DOMContentLoaded', () => { | |
new ImageViewer('.post-content'); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment