本文介绍了一个基于 JavaScript 的图片预览功能,用户点击图片时可实现放大预览,同时支持滚轮缩放和居中显示效果。通过简单的配置,可以将此功能灵活集成到不同的项目中。
功能概述
该图片预览功能具备以下特性:
- 图片点击放大:点击图片后,自动放大至屏幕中央,遮罩背景,提升用户沉浸感。
- 滚轮缩放:支持鼠标滚轮对图片进行放大或缩小。
- 居中显示:自动计算图片位置和缩放比例,使图片适配窗口并居中。
- 关闭预览:点击遮罩层即可退出预览,恢复初始状态。
- 链接优先支持:若图片嵌套在超链接中,则点击优先跳转,而不是触发预览功能。
功能实现
以下是功能的具体实现及逐步解析。
1. 初始化图片预览功能
通过 initialize 函数,将事件监听器绑定到目标图片上。当用户点击图片时,触发 handleImageClick 进行后续处理。
核心代码
function initialize(selector) {
const targetDom = document.querySelectorAll(selector);
if (targetDom.length > 0) {
targetDom.forEach(item => item.addEventListener("click", handleImageClick));
}
}
功能点
- 参数
selector接收一个 CSS 选择器,匹配所有目标图片。 - 遍历目标图片,逐一绑定点击事件监听器。
2. 处理点击事件
点击图片时,检查图片是否嵌套在超链接中。如果存在超链接,则优先跳转,否则触发图片预览。
核心代码
function handleImageClick(e) {
e.preventDefault();
if (e.target.tagName === "IMG" && closest(e.target, "a")) {
const link = closest(e.target, "a");
const target = link.getAttribute("target") || "_self";
const rel = link.getAttribute("rel") || "noopener";
window.open(link.href, target, rel);
return;
}
if (e.target.tagName === "IMG") {
originalEl = e.target;
cloneEl = originalEl.cloneNode(true);
openPreview();
}
}
功能点
closest函数递归寻找图片的最近父级超链接节点。- 如果存在超链接,则根据其
href和相关属性打开链接。 - 否则,克隆图片并调用
openPreview显示预览。
3. 显示图片预览
通过 openPreview 函数实现以下功能:
- 计算图片缩放比例和居中偏移。
- 创建遮罩层。
- 将图片居中并放大显示。
核心代码
function openPreview() {
scale = 1;
const { offsetWidth, offsetHeight } = originalEl;
const { top, left } = originalEl.getBoundingClientRect();
const mask = createMask();
document.body.appendChild(mask);
mask.addEventListener("click", () => closePreview(mask));
mask.addEventListener("wheel", zoom, { passive: false });
applyStyles(cloneEl, {
position: "absolute",
left: `${left}px`,
top: `${top}px`,
width: `${offsetWidth}px`,
transform: "translateZ(0)",
});
mask.appendChild(cloneEl);
moveToCenter(offsetWidth, offsetHeight, left, top);
}
功能点
- 通过
getBoundingClientRect获取图片位置和尺寸。 - 动态创建遮罩层,添加关闭和缩放事件监听。
- 调用
moveToCenter方法,将图片移动到屏幕中央。
4. 创建遮罩层
遮罩层使图片预览更加突出,并阻止用户与页面其他内容交互。
核心代码
function createMask() {
const mask = document.createElement("div");
mask.classList.add(config.maskClass);
applyStyles(mask, {
position: "fixed",
top: "0",
left: "0",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0, 0, 0, 0.75)",
zIndex: 10000,
userSelect: "none",
touchAction: "none",
});
return mask;
}
功能点
- 通过
applyStyles设置遮罩层样式,使其全屏覆盖。 - 提高层级显示,确保遮罩层在最顶层。
5. 图片居中显示
计算图片居中的偏移量,并通过动画效果将图片移动到屏幕中央。
核心代码
function moveToCenter(width, height, left, top) {
const scaleToFit = adaptScale(width, height);
const offsetCenter = calculateCenterOffset(width, height, left, top, scaleToFit);
applyStyles(cloneEl, {
transition: `all ${config.transitionDuration}ms`,
width: `${width * scaleToFit}px`,
transform: `translate(${offsetCenter.left}px, ${offsetCenter.top}px)`,
});
offset = offsetCenter;
recordInitialData();
}
功能点
adaptScale动态计算缩放比例,确保图片适配屏幕。- 调用
calculateCenterOffset获取居中偏移量。 - 使用
transition属性实现平滑动画效果。
6. 鼠标滚轮缩放
通过鼠标滚轮调整图片大小,并确保缩放效果以鼠标为中心。
核心代码
function zoom(event) {
event.preventDefault();
scale = Math.min(Math.max(scale + (event.deltaY < 0 ? config.zoomStep : -config.zoomStep), config.minScale), 3);
const offsetCorrection = getOffsetCorrection(event.offsetX, event.offsetY);
applyStyles(cloneEl, {
transform: `translate(${offsetCorrection.left}px, ${offsetCorrection.top}px) scale(${scale})`,
transformOrigin: `${event.offsetX}px ${event.offsetY}px`,
});
}
功能点
- 动态更新
scale值,限制缩放比例范围。 - 调用
getOffsetCorrection确保缩放以鼠标位置为中心。
7. 关闭预览
点击遮罩层时,调用 closePreview 将图片平滑恢复到原始位置,并移除遮罩层。
核心代码
function closePreview(mask) {
const { offsetWidth, offsetHeight } = originalEl;
const { top, left } = originalEl.getBoundingClientRect();
applyStyles(cloneEl, {
transform: "translate(0,0)",
left: `${left}px`,
top: `${top}px`,
width: `${offsetWidth}px`,
transition: `all ${config.transitionDuration}ms`,
});
setTimeout(() => {
mask.remove();
cloneEl.remove();
}, config.transitionDuration);
}
功能点
- 通过动画平滑恢复图片位置。
- 清理 DOM 节点,释放内存。
8. 初始化调用入口
通过以下方法初始化图片预览功能:
window.previewImg = function (selector = ".image-container img") {
initialize(selector);
};
使用示例
// 调用图片预览功能
previewImg(".image-container img");
总结
本文介绍了一种简单高效的图片预览功能,可轻松集成到各种项目中。希望本文对您有所帮助!