从零开始:用JavaScript打造简洁高效的HTML图片查看器
简介
先上效果图:
可以看到整体布局简洁清爽,功能设计直观易用。界面支持左右翻页,底部显示页码,右上角展示缩略图,而中间则呈现大图。用户可以通过双击放大图片,长按鼠标则可进行拖动操作,这些简单的功能已经能够满足基本需求。
实现
首先,我们来写一下HTML界面。
HTML
<div id="preview-modal" class="modal">
<span class="close">×</span>
<button class="nav-btn prev-btn"><</button>
<button class="nav-btn next-btn">></button>
<div class="modal-content">
<img id="preview-image" src="" alt="Preview Image">
</div>
<div id="image-count" class="image-count">1 / {{ all_images }}</div>
</div>
短短几行足以。需要注意的是all_images表示总图片数,请自行定义。
CSS
.modal {
display: none; /* 默认隐藏 */
position: fixed;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
z-index: 1001;
left: 0;
top: 0;
width: 100%;
height: 100vh;
background-color: rgba(0, 0, 0, 0.9);
}
.modal-content {
display: flex;
justify-content: center;
align-items: center;
position: relative;
margin: auto;
max-width: 100%;
max-height: 100%;
}
#preview-image {
position: relative; /* 确保能够自由拖动 */
max-width: 100%;
max-height: 80vh;
transition: transform 0.25s ease;
}
.nav-btn {
position: absolute;
top: 50%;
background-color: rgba(0, 0, 0, 0.5);
color: white;
border: none;
/*padding: 10px;*/
cursor: pointer;
font-size: 24px;
}
.prev-btn {
left: 10px;
z-index: 1005;
}
.next-btn {
right: 10px;
z-index: 1005;
}
.close {
position: absolute;
top: 20px;
right: 35px;
color: white;
font-size: 40px;
font-weight: bold;
cursor: pointer;
z-index: 1002;
}
/* 样式化图片序号显示 */
.image-count {
position: absolute;
left: 50%; /* 水平居中 */
top: 95%;
transform: translateX(-50%);
color: white;
font-size: 18px;
font-weight: bold;
background-color: rgba(0, 0, 0, 0.5); /* 背景色透明 */
padding: 5px;
border-radius: 5px;
}
@media (max-width: 630px) {
.nav-btn{
top: 90%;
}
.image-count{
top: 90%;
}
.modal-content {
max-width: 100%;
max-height: 80%
}
.close {
top: 10px;
right: 10px;
}
}
基本的样式,根据需要自行调整。
JS
预览界面入口
这部分是进入预览界面以及实现平移缩放功能的关键。我们先实现预览界面的函数调用。
{% for url links in all_urls %}
<div class="grid-item">
<a onclick="openPreview({{ loop.index0 }})">
<img src="{{ url }}" alt="图片"/>
</a>
</div>
{% endfor %}
用<a>标签包裹img缩略图,并添加函数openPreview,这样我们就可以在点击缩略图时打开图片预览界面。openPreview函数需要传入当前图片的index用于获取图片url,这里我传了index。
接下来实现openPreview入口函数。
let currentImageIndex = 0;
let scale = 1;
const previewImage = document.getElementById('preview-image');
const modal = document.getElementById('preview-modal');
const imageCount = document.getElementById('image-count');
function openPreview(index0){
currentImageIndex = index0;
previewImage.src = links[currentImageIndex];
modal.style.display = 'flex'; // 显示控件
scale = 1;
previewImage.style.transform = `scale(${scale})`;
imageCount.textContent = `${currentImageIndex + 1} / ${links.length}`;
}
注意,变量links为大图地址的列表,请自行定义。
控件功能实现
const closeBtn = document.querySelector('.close');
const prevBtn = document.querySelector('.prev-btn');
const nextBtn = document.querySelector('.next-btn');
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
img_offsetX = 0;
img_offsetY = 0;
scale = 1;
});
// 上一张图片
prevBtn.addEventListener('click', () => {
currentImageIndex = (currentImageIndex - 1 + links.length) % links.length;
previewImage.src = links[currentImageIndex];
scale = 1;
previewImage.style.transform = `scale(${scale})`;
imageCount.textContent = `${currentImageIndex + 1} / ${links.length}`;
});
// 下一张图片
nextBtn.addEventListener('click', () => {
currentImageIndex = (currentImageIndex + 1) % links.length;
previewImage.src = links[currentImageIndex];
scale = 1;
previewImage.style.transform = `scale(${scale})`;
imageCount.textContent = `${currentImageIndex + 1} / ${links.length}`;
});
这里实现了关闭、上一页和下一页按钮的功能。其中img_offsetX、img_offsetY是后面平移功能使用到的变量,这里是已经完成好的代码。
平移功能实现
我们希望实现的平移效果是,用户点击previewImage并长按后,图片能够跟随鼠标滑动。
首先定义变量;
let scale = 1;
let isLongPress = false; // 长按标志位
let pressTimer; // 长按计时器
let startX, startY; // 鼠标的起始位置
let offsetX = 0, offsetY = 0; // 鼠标的偏移量
let img_offsetX = 0, img_offsetY = 0; // 图片的偏移量
let lastTouchTime = 0;
接下来实现具体逻辑;
previewImage.addEventListener('mousedown', (e) => {
// 防止图片的默认拖动行为
e.preventDefault();
startX = e.clientX; // 记录鼠标单击时的初始位置
startY = e.clientY;
// 设置一个定时器判断是否为长按
pressTimer = setTimeout(() => {
isLongPress = true;
}, 200); // 200毫秒判断为长按,可以调整这个值
});
// 鼠标移动时拖动图片
previewImage.addEventListener('mousemove', (e) => {
if (isLongPress) {
// 计算鼠标的相对移动
let deltaX = e.clientX - startX;
let deltaY = e.clientY - startY;
// 将鼠标移动量累加到图片的偏移量中
img_offsetX += deltaX / scale;
img_offsetY += deltaY / scale;
// 刷新鼠标初始位置
startX = e.clientX;
startY = e.clientY;
previewImage.style.transition = 'none'; // 禁用动画
previewImage.style.transform = `scale(${scale}) translate(${img_offsetX}px, ${img_offsetY}px)`; // 平移图片
}
});
// 鼠标点击结束时
previewImage.addEventListener('mouseup', () => {
// 清除定时器
clearTimeout(pressTimer);
isLongPress = false;
});
// 鼠标离开控件时
previewImage.addEventListener('mouseleave', () => {
// 清除定时器
clearTimeout(pressTimer);
isLongPress = false;
});
在适配移动端时,需要响应触摸事件;
previewImage.addEventListener('touchstart', function(e) {
// 记录触摸起始位置
if (e.touches.length === 1) {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
}
});
previewImage.addEventListener('touchmove', function(e) {
if (e.touches.length === 1) {
// 获取滑动的距离
const moveX = e.touches[0].clientX - startX;
const moveY = e.touches[0].clientY - startY;
// 更新偏移量
img_offsetX += moveX / scale;
img_offsetY += moveY / scale;
startX = e.touches[0].clientX
startY = e.touches[0].clientY
// 更新图片位置
previewImage.style.transition = 'none';
previewImage.style.transform = `scale(${scale}) translate(${img_offsetX}px, ${img_offsetY}px)`;
// 防止默认的触摸滚动行为
e.preventDefault();
}
});
移动端和PC端的处理有所不同。对于PC端,若不复位长按标志位,鼠标继续移动时,图片会继续跟随移动;而在移动端,触摸结束后,触摸点的位置不再变化,因此图片也不会继续移动。
缩放功能实现
首先来实现Ctrl+鼠标滚轮缩放;
// 监听鼠标滚轮缩放
previewImage.addEventListener('wheel', (e) => {
// 如果没有按住 Ctrl 键,则不进行缩放
if (!e.ctrlKey) {
return;
}
e.preventDefault(); // 防止页面滚动
if (e.deltaY < 0) {
// 向上滚动:放大
scale *= 1.1;
} else {
// 向下滚动:缩小
scale /= 1.1;
}
// 限制缩放范围
scale = Math.max(1, Math.min(scale, 5)); // 设定最小缩放为1,最大为3
// 更新图片的transform属性
previewImage.style.transition = 'transform 0.3s ease'; // 缩放加一点过渡效果
previewImage.style.transform = `scale(${scale})`;
});
接下来实现双击缩放,我们要实现的效果是:放大并居中用户双击的位置,一共可以放大2次,每次放大3倍,图片放缩至最大时再次双击复原。
previewImage.addEventListener('dblclick', (e) => {
// 获取屏幕中央位置
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
if (scale < 10) {
// 获取鼠标点击位置与屏幕中央的偏移
offsetX = centerX - e.clientX;
offsetY = centerY - e.clientY;
scale *= 3;
// 应用偏移
img_offsetX += offsetX / scale;
img_offsetY += offsetY / scale;
previewImage.style.transition = 'transform 0.3s ease';
previewImage.style.transform = `scale(${scale}) translate(${img_offsetX}px, ${img_offsetY}px)`;
} else {
// 复原
scale = 1
img_offsetX = 0
img_offsetY = 0
previewImage.style.transition = 'transform 0.3s ease';
previewImage.style.transform = `scale(${scale}) translate(${img_offsetX}px, ${img_offsetY}px)`;
}
});
最后适配移动端的缩放功能;
previewImage.addEventListener('touchend', function(e) {
const currentTime = new Date().getTime();
const timeDifference = currentTime - lastTouchTime; // 计算两次触摸的间隔时间
if (timeDifference < 500 && timeDifference > 0) {
if(e.touches && e.touches.length > 0){
// 双击事件发生,时间间隔小于500ms视为双击
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
if (scale < 10) {
offsetX = centerX - e.touches[0].clientX;
offsetY = centerY - e.touches[0].clientY;
scale *= 3;
img_offsetX += offsetX / scale;
img_offsetY += offsetY / scale;
previewImage.style.transition = 'transform 0.3s ease';
previewImage.style.transform = `scale(${scale}) translate(${img_offsetX}px, ${img_offsetY}px)`;
} else {
scale = 1
img_offsetX = 0
img_offsetY = 0
previewImage.style.transition = 'transform 0.3s ease';
previewImage.style.transform = `scale(${scale}) translate(${img_offsetX}px, ${img_offsetY}px)`;
}
}
}
lastTouchTime = currentTime; // 更新上次触摸时间
});
到这里我们的简易图片预览界面就做好啦!
附件
为方便使用,这里免费提供js和css代码给大家~
从零开始:用JavaScript打造简洁高效的HTML图片查看器
https://blog.nasxyz.top/archives/37b5f9c4-8e37-4132-a64d-b3bcbcd4252a