JS源码分享 - 图片查看器
前言
一个不需要其他依赖的图片查看器插件,您仅需要引入单个js,然后初始化它,既可使用。
使用也仅需要给它一个src,然后使用show方法进行显示,效果图如下。

初始化
使用 const imageViewer = new ImageViewer();来进行初始化,就可以开始使用它了。
调用
您只需要拿到图片的src后,调用刚才定义的对象imageViewer,然后在通过imageViewer.changeImg(src);将图片的地址传给图片查看器,再通过imageViewer.show();show方法进行显示即可。在显示后,如果您需要隐藏可以通过右上角的关闭按钮进行关闭,或者调用imageViewer.hide();hide方法。
功能
- 图片打开的默认最大宽高是屏幕高度的95%,但用户如果挑了显示比例加浏览器放大可能会出现偏差。
 - 点击右上角X号图标可以关闭图片查看器。
 - 点击右下角的放大缩小可以进行放大图片或者缩小图片。
 - 图片查看器按住CTRL键滚轮滚动可以进行放大缩小,最大可放大至500%,或缩小至50%。
 - 按住CTRL+鼠标拖拽图片,可以在屏幕上拖动图片。
 - 按住CTRL+0进行复位。(定位复位+大小复位)
 
后续计划
- 目前仅在chrome上测试过,未在其他浏览器进行测试,以及未测试各种显示比例情况是否会显示错误,我会找时间进行一个较为完整的测试。
 - show方法与hide方法增加回调
 
更新说明
| 日期 | 更新类型 | 更新说明 | 
|---|---|---|
| 2021-07-22 | 新增功能 | 新增一个当前倍率的显示框 | 
源码
/**
* 图片查看器 - 纯js版
*
* 所谓js版就是不需要依赖vue.js来进行组件化,以js类或者其他方式来进行展示等
* 之所以要专门开发一个纯js版本就是因为被太多需要依赖jquery的插件给坑惨了,在require.js里都是坑,纯js不吃环境,开箱即用
* 无需引入css
*
* 使用步骤
* 1. 引入js
* 2. 创建对象 window.imageViewer = new ImageViewer();
* 3. 指定图片地址 window.imageViewer.changeImg("图片地址");
* 4. 显示 window.imageViewer.show();
*
*
* @author wwq
* @date 20210624
* */
const imageViewerCss = `
<style id="imageViewerCss">
.imgViewer *{
box-sizing: border-box;
}
.imgViewer {
display: -webkit-box;
overflow: auto;
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 99999999;
background: rgba(0, 0, 0, 0.75);
-webkit-box-align: center;
-webkit-box-pack: center;
-moz-box-align: center;
-moz-box-pack: center;
-o-box-align: center;
-o-box-pack: center;
-ms-box-align: center;
-ms-box-pack: center;
box-align: center;
box-pack: center;
}
/*
如果能够实现一个定位框,那么我就把滚动条去掉
*/
.imgViewer::-webkit-scrollbar {
width: 0;
height: 0;
}
.imgViewer img {
transform: scale(1);
user-select: none;
transition: transform 0.2s ease-in-out;
position: relative;
left: 0;
top: 0;
image-rendering:-moz-crisp-edges;
image-rendering:-o-crisp-edges;
image-rendering: crisp-edges;
-ms-interpolation-mode:nearest-neighbor;
}
.imgViewer .closeBtn {
border: 4px solid white;
width: 48px;
height: 48px;
border-radius: 50%;
position: fixed;
right: 40px;
top: 40px;
cursor: pointer;
opacity: .65;
transition: transform 0.4s ease-in-out 0s;
background:rgba(0,0,0,0.75);
}
.imgViewer .closeBtn:hover {
opacity: .95;
transform: rotateZ(180deg);
}
.imgViewer .closeBtn::before,
.imgViewer .closeBtn::after {
content: " ";
display: block;
width: 30px;
position: absolute;
left: 5px;
top: 18px;
height: 4px;
background: white;
transform: rotateZ(135deg);
border-radius: 2PX;
}
.imgViewer .closeBtn::after {
transform: rotateZ(45deg);
}
.imgViewer .imgBtn {
position: fixed;
border: 2px solid white;
border-radius: 12px;
width: 64px;
height: 64px;
right: 30px;
bottom: 0;
cursor: pointer;
opacity: .65;
background:rgba(0,0,0,0.75);
}
.imgViewer .imgBtn:hover {
opacity: .95;
}
.imgViewer .imgBtn.zoomOutBtn {
bottom: 30px;
}
.imgViewer .imgBtn.zoomInBtn {
bottom: 120px;
}
.imgViewer .imgBtn::after,
.imgViewer .imgBtn::before {
content: " ";
display: block;
width: 40px;
position: absolute;
left: 10px;
top: 26px;
height: 8px;
background: white;
border-radius: 2PX;
}
.imgViewer .imgBtn.zoomInBtn::before {
transform: rotateZ(90deg);
}
.imgViewer .imgBtn.zoomInBtn::after {
transform: rotateZ(0deg);
}
.imgViewer .zoomRateBox{
position: fixed;
width: 64px;
text-align: center;
right: 30px;
bottom: 195px;
background: rgba(0,0,0,0.55);
padding: 5px;
border-radius: 10px;
}
.imgViewer .zoomRateBox .zoomText{
color: white;
text-shadow: 1px 1px 5px black;
font-size: 16px;
}
</style>
`;
const imageViewerHtml = `
<div tabindex="0" id="imageViewer" class="imgViewer" style="display: none;">
<div>
<img id="imageViewerImg" ondragstart="return false" src="">
<div class="closeBtn" id="imageViewerCloseBtn">
</div>
<div class="imgBtn zoomInBtn" id="imageViewerZoomInBtn">
</div>
<div class="imgBtn zoomOutBtn" id="imageViewerZoomOutBtn">
</div>
<div class="zoomRateBox">
<span class="zoomText">
<span id="magnification">100</span>%
</span>
</div>
</div>
</div>
`;
const accAdd = function (num1, num2) {
let r1, r2, m;
try {
r1 = num1.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = num2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
// return (num1*m+num2*m)/m;
return Math.round(num1 * m + num2 * m) / m;
}
// 两个浮点数相减
const accSub = function (num1, num2) {
let r1, r2, m;
try {
r1 = num1.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = num2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
n = (r1 >= r2) ? r1 : r2;
return (Math.round(num1 * m - num2 * m) / m).toFixed(n);
}
// 两数相除
const accDiv = function (num1, num2) {
let t1, t2, r1, r2;
try {
t1 = num1.toString().split('.')[1].length;
} catch (e) {
t1 = 0;
}
try {
t2 = num2.toString().split(".")[1].length;
} catch (e) {
t2 = 0;
}
r1 = Number(num1.toString().replace(".", ""));
r2 = Number(num2.toString().replace(".", ""));
//保留三维小数点
return (r1 / r2) * Math.pow(10, t2 - t1);
}
//多个数相除 accDiv2(10000,2,4,5) === 250
const accDiv2 = function () {
let nums = Array.prototype.slice.call(arguments, 0);
let num1 = nums.shift();
while (nums.length > 0) {
let num2 = nums.shift();
num1 = accDiv(num1, num2);
}
return num1
}
const accMul = function (num1, num2) {
num1 = num1 || 0;
num2 = num2 || 0;
let m = 0,
s1 = num1.toString(),
s2 = num2.toString();
// console.log(s1.split("."),s2.split("."));
try {
m += s1.split(".")[1].length
} catch (e) {
}
try {
m += s2.split(".")[1].length
} catch (e) {
}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
class ImageViewer {
zoom = 1;
zoomInUpperLimit = 5;
zoomOutUpperLimit = 0.4;
mouseDown = false;
ctrlDown = false;
point = {
x: 0,
y: 0
}
imageViewer = {};
imageViewerImg = {};
constructor({zoomInUpperLimit, zoomOutUpperLimit} = {}) {
if (typeof zoomInUpperLimit != "undefined") {
this.zoomInUpperLimit = zoomInUpperLimit;
}
if (typeof zoomOutUpperLimit != "undefined") {
this.zoomOutUpperLimit = zoomOutUpperLimit;
}
this.init();
}
init() {
if (typeof window.imageViewer != "undefined") {
this.imageViewer = document.querySelector("#imageViewer");
this.imageViewerImg = document.querySelector("#imageViewerImg");
console.warn("已存在一个imageViewer了,无法再次进行创建")
return;
}
document.head.innerHTML += imageViewerCss;
document.body.innerHTML += imageViewerHtml;
this.imageViewer = document.querySelector("#imageViewer");
this.imageViewerImg = document.querySelector("#imageViewerImg");
this.imageViewerImg.style.maxWidth = `${(window.screen.availWidth * 0.95)}px`;
this.imageViewerImg.style.maxHeight = `${(window.screen.availHeight * 0.95)}px`;
this.imageViewer.addEventListener("wheel", this.wheelHandler);
this.imageViewerImg.addEventListener("mousedown", this.mousedownHandler);
this.closeBtn = document.querySelector("#imageViewerCloseBtn");
this.closeBtn.addEventListener("click", this.hide);
this.zoomInBtn = document.querySelector("#imageViewerZoomInBtn");
this.zoomInBtn.addEventListener("click", this.zoomIn);
this.zoomOutBtn = document.querySelector("#imageViewerZoomOutBtn");
this.zoomOutBtn.addEventListener("click", this.zoomOut);
this.initData();
window.addEventListener("keydown", (e) => {
switch (e.key) {
case 'Control': {
window.imageViewer.ctrlDown = true;
if (!window.imageViewer.mouseDown) {
window.imageViewer.imageViewer.style.cursor = "grab";
}
break;
}
case '0': {
if (e.ctrlKey) {
window.imageViewer.reset();
}
}
}
});
window.addEventListener("keyup", (e) => {
switch (e.key) {
case 'Control': {
window.imageViewer.ctrlDown = false;
window.imageViewer.imageViewer.style.cursor = "default";
break;
}
}
});
window.addEventListener("mousemove", this.mousemoveHandler)
window.addEventListener("mouseup", this.mouseupHandler)
}
changeZoom() {
let magnificationEl = document.querySelector("#magnification");
magnificationEl.innerHTML = `${accMul(window.imageViewer.zoom, 100)}`;
}
initData() {
let imageViewerImg = document.querySelector("#imageViewerImg");
imageViewerImg.style.left = 0;
imageViewerImg.style.top = 0;
imageViewerImg.style.transform = `scale(1)`;
window.imageViewer.zoom = 1;
window.imageViewer.mouseDown = false;
}
reset() {
window.imageViewer.initData();
window.imageViewer.changeZoom();
}
zoomIn() {
let imageViewerImg = document.querySelector("#imageViewerImg");
if (window.imageViewer.zoom < window.imageViewer.zoomInUpperLimit) {
let zoom = accAdd(window.imageViewer.zoom, 0.2)
window.imageViewer.zoom = zoom;
imageViewerImg.style.transform = `scale(${window.imageViewer.zoom})`;
window.imageViewer.changeZoom();
}
}
zoomOut() {
let imageViewerImg = document.querySelector("#imageViewerImg");
if (window.imageViewer.zoom > window.imageViewer.zoomOutUpperLimit) {
let zoom = accSub(window.imageViewer.zoom, 0.2)
window.imageViewer.zoom = zoom;
imageViewerImg.style.transform = `scale(${window.imageViewer.zoom})`;
window.imageViewer.changeZoom();
}
}
changeImg(src) {
let imageViewerImg = document.querySelector("#imageViewerImg");
imageViewerImg.src = src;
console.log(imageViewerImg.naturalWidth)
console.log(imageViewerImg.naturalHeight)
}
show() {
window.imageViewer.reset();
let imageViewer = document.querySelector("#imageViewer");
imageViewer.style.display = "-webkit-box";
}
hide() {
window.imageViewer.reset();
let imageViewer = document.querySelector("#imageViewer");
imageViewer.style.display = "none";
}
mousedownHandler(e) {
window.imageViewer.mouseDown = true;
window.imageViewer.point = {
x: e.screenX,
y: e.screenY,
}
if (e.ctrlKey) {
window.imageViewer.imageViewer.style.cursor = "grabbing";
}
}
mousemoveHandler(e) {
let imageViewerImg = document.querySelector("#imageViewerImg");
if (window.imageViewer.ctrlDown && window.imageViewer.mouseDown) {
let left = Number((imageViewerImg.style.left || "0").replace("px", ""));
let top = Number((imageViewerImg.style.top || "0").replace("px", ""));
imageViewerImg.style.left =
`${left + (e.screenX - window.imageViewer.point.x)}px`
imageViewerImg.style.top =
`${top + (e.screenY - window.imageViewer.point.y)}px`
window.imageViewer.point = {
x: e.screenX,
y: e.screenY,
}
}
}
mouseupHandler(e) {
window.imageViewer.mouseDown = false;
}
wheelHandler(e) {
if (e.ctrlKey) {
if (e.deltaY > 0) {
window.imageViewer.zoomOut()
} else {
window.imageViewer.zoomIn()
}
}
e.preventDefault();
}
}
                    
                
                
            
        
浙公网安备 33010602011771号