原生js实现图片预览控件,支持丝滑拖拽,滚轮放缩,放缩聚焦

手撸源代码如下:注释应该很详细了,拿去直用

可以放到在线编辑器测试,记得修改图片路径

菜鸟教程在线编辑器

<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#img_box {position: relative;width: 800px;height: 800px;border: 1px solid #000000;overflow: hidden;cursor: grab;}#img_c {width: 100%;height: 100%;position: absolute;object-fit: contain;user-select: none;-webkit-user-drag: none;user-select: none;-moz-user-select: none;-webkit-user-select: none;-ms-user-select: none;/* transition: all 0.1s ease-in-out; */}</style>
</head><body><div id="img_box"><img id="img_c" title="没有" src="./1719194464803.jpg"></div><script>initImageScaleElement();function initImageScaleElement() {//定义参数const max = 20;//放缩原图倍数const scale = 0.05;//滚轮一下缩放的变化比例 越大放缩越快//获取对象const box = document.getElementById("img_box");const img = document.getElementById("img_c");/*** 图片已加载完毕后,对图片居中* 图片一开始其实默认是居中的,只不过是img的object-fit: contain属性进行了居中,并且产生了一些边缘填充空白* 这些填充的空白会影响后续图片拖拽的计算,因此需要将img修改成图片展示的真实大小,但对用户视觉不发生改变。*/img.onload = function () {const imgRatio = img.naturalWidth / img.naturalHeight; //图片横纵比const boxRatio = box.clientWidth / box.clientHeight;   //容器横纵比/*** 因为拿到的图片真实大小,不是显示大小,需要手动放缩,适配到容器中* 当图片横纵比大于容器横纵比,图片保持比例的情况下填充容器,此时只能对图片的横向放缩填充全部容器 * 当图片横纵比小于容器横纵比,图片保持比例的情况下填充容器,此时只能对图片的纵向放缩填充全部容器 * 这个规律可以自己画图试验一下*/if (imgRatio > boxRatio) { const scale = box.clientWidth / img.naturalWidth;img.style.width = box.clientWidth; //长度填充img.style.height = img.naturalHeight * scale; //高度自适应img.style.top = Math.ceil((box.clientHeight - img.clientHeight) / 2) + "px" ;//位置居中} else {const scale = box.clientHeight / img.naturalHeight;img.style.height = box.clientHeight;//高度填充img.style.width = img.naturalWidth * scale;//长度自适应img.style.left = Math.ceil((box.clientWidth - img.clientWidth) / 2) + "px";//位置居中}};//用于元素拖拽时边界的参数限制,入参:{ 实际值,左区间,右区间 }const getRange = (actual, limita, limitb) => {if (actual < -limita) {return -limita;} else if (actual > limitb) {return limitb;}return actual;}/*** 缩放操作* */box.onwheel = (event) => {event.preventDefault(); //关闭默认事件const center_x = event.clientX;const center_y = event.clientY;const wheelDelta = event.deltaY;const top_d = center_y - img.offsetTop;const left_d = center_x - img.offsetLeft;if (wheelDelta > 0) {//缩小 往后推滚轮  【滚轮值和缩放关系不是固定的,你也可以反过来】let modifyHeight = img.clientHeight * (1 - scale);let modifyWidth = img.clientWidth * (1 - scale);if (modifyHeight * max > img.naturalHeight) { //只在比例范围内,放缩img.style.height = modifyHeight;img.style.width = modifyWidth;img.style.top = center_y - top_d * (1 - scale);img.style.left = center_x - left_d * (1 - scale);} else {console.log("缩小超出" + max * 100 + "%比例无法缩小");}} else {//放大  往前推滚轮let modifyHeight = img.clientHeight * (1 + scale);let modifyWidth = img.clientWidth * (1 + scale);if (modifyHeight < img.naturalHeight * max) { //只在比例范围内,放缩img.style.height = modifyHeight;img.style.width = modifyWidth;img.style.top = center_y - top_d * (1 + scale);img.style.left = center_x - left_d * (1 + scale);} else {console.log("放大超出" + max * 100 + "%比例无法放大");}}}/*** 拖拽操作*///拖拽需要的全局变量const drag = {status: false,lastX: null,lastY: null}box.onmousedown = (event) => {if (event.button === 0) {//鼠标会移出元素,甚至移出到浏览器外部,使用document监听移动就可以不受影响。document.onmousemove = (event) => {if (drag.status) {let mx = event.clientX - drag.lastXlet my = event.clientY - drag.lastYdrag.lastX = event.clientXdrag.lastY = event.clientYlet top = img.offsetTop + mylet left = img.offsetLeft + mx//拖拽超出容器外部无意义,可能找不回,因此图片位置不能任意拖拽至少有一个角或一个边在容器内部,这里减10就是预留出的宽度img.style.left = getRange(left, img.clientWidth - 10, box.clientWidth - 10) + 'px';img.style.top = getRange(top, img.clientHeight - 10, box.clientHeight - 10) + "px";//如果你不想让图片跑出容器,那就调整这里就好了,控制两个相邻边就能控制整个图片,如// img.style.left = getRange(left,0, box.clientWidth - img.clientWidth) + 'px';// img.style.top = getRange(top,0, box.clientHeight - img.clientHeight) + "px";}}//鼠标松开,初始化操作,如果鼠标在元素外部松开,元素的松开监听是获取不到的,因此需要用document监听document.onmouseup = (event) => {if (event.button === 0) {drag.status = falsedocument.onmousemove = nulldocument.onmouseup = null}}drag.status = truedrag.lastX = event.clientXdrag.lastY = event.clientY}}}</script></body></html>

 进阶版

这样使用还是很不方便,于是浓缩成一个函数,传入url便可直接预览

/**
* 图片预览函数,调用后自动预览图片
* @param {图片地址} imgurl 
*/
function openImagePreview(imgurl) {if (!imgurl) return;//定义参数const max = 20;//放缩原图倍数const scale = 0.05;//滚轮一下缩放的变化比例 越大放缩越快//定义对象const box = document.createElement("div");box.style.cssText = "position: fixed;top:10px;left:10px;z-index: 10000;background-color: rgba(0,0, 0, 0.3);width:" + (document.documentElement.clientWidth - 20) + "px;height:" + (document.documentElement.clientHeight - 20) + "px;border: 1px solid #000000;overflow: hidden;cursor: grab;";const img = document.createElement("img");img.style.cssText = "position: absolute;object-fit: contain;user-select: none;-webkit-user-drag: none;user-select: none;-moz-user-select: none;-webkit-user-select: none;-ms-user-select: none;";img.src = imgurl;img.title = "预览";const close = document.createElement("button");close.innerText = "X";close.style.cssText = "position: absolute;font-size: 30px;color: rgba(255,255, 255, 0.6);top:20px;right:20px;font-weight: bold;background: none;outline: none;border: none;";close.onclick = () => {document.body.removeChild(box);}box.append(img);box.append(close);document.body.append(box);/*** 图片已加载完毕后,对图片居中* 图片一开始其实默认是居中的,只不过是img的object-fit: contain属性进行了居中,并且产生了一些边缘填充空白* 这些填充的空白会影响后续图片拖拽的计算,因此需要将img修改成图片展示的真实大小,但对用户视觉不发生改变。*/img.onload = function () {const imgRatio = img.naturalWidth / img.naturalHeight; //图片横纵比const boxRatio = box.clientWidth / box.clientHeight;   //容器横纵比/*** 因为拿到的图片真实大小,不是显示大小,需要手动放缩,适配到容器中* 当图片横纵比大于容器横纵比,图片保持比例的情况下填充容器,此时只能对图片的横向放缩填充全部容器 * 当图片横纵比小于容器横纵比,图片保持比例的情况下填充容器,此时只能对图片的纵向放缩填充全部容器 * 这个规律可以自己画图试验一下*/if (imgRatio > boxRatio) {const scale = box.clientWidth / img.naturalWidth;img.style.width = box.clientWidth; //长度填充img.style.height = img.naturalHeight * scale; //高度自适应img.style.top = Math.ceil((box.clientHeight - img.clientHeight) / 2) + "px";//位置居中} else {const scale = box.clientHeight / img.naturalHeight;img.style.height = box.clientHeight;//高度填充img.style.width = img.naturalWidth * scale;//长度自适应img.style.left = Math.ceil((box.clientWidth - img.clientWidth) / 2) + "px";//位置居中}};//用于元素拖拽时边界的参数限制,入参:{ 实际值,左区间,右区间 }const getRange = (actual, limita, limitb) => {if (actual < -limita) {return -limita;} else if (actual > limitb) {return limitb;}return actual;}/*** 缩放操作* */box.onwheel = (event) => {event.preventDefault(); //关闭默认事件const center_x = event.clientX;const center_y = event.clientY;const wheelDelta = event.deltaY;const top_d = center_y - img.offsetTop;const left_d = center_x - img.offsetLeft;if (wheelDelta > 0) {//缩小 往后推滚轮  【滚轮值和缩放关系不是固定的,你也可以反过来】let modifyHeight = img.clientHeight * (1 - scale);let modifyWidth = img.clientWidth * (1 - scale);if (modifyHeight * max > img.naturalHeight) { //只在比例范围内,放缩img.style.height = modifyHeight;img.style.width = modifyWidth;img.style.top = center_y - top_d * (1 - scale);img.style.left = center_x - left_d * (1 - scale);} else {console.log("缩小超出" + max * 100 + "%比例无法缩小");}} else {//放大  往前推滚轮let modifyHeight = img.clientHeight * (1 + scale);let modifyWidth = img.clientWidth * (1 + scale);if (modifyHeight < img.naturalHeight * max) { //只在比例范围内,放缩img.style.height = modifyHeight;img.style.width = modifyWidth;img.style.top = center_y - top_d * (1 + scale);img.style.left = center_x - left_d * (1 + scale);} else {console.log("放大超出" + max * 100 + "%比例无法放大");}}}/*** 拖拽操作*///拖拽需要的全局变量const drag = {status: false,lastX: null,lastY: null}box.onmousedown = (event) => {if (event.button === 0) {//鼠标会移出元素,甚至移出到浏览器外部,使用document监听移动就可以不受影响。document.onmousemove = (event) => {if (drag.status) {let mx = event.clientX - drag.lastXlet my = event.clientY - drag.lastYdrag.lastX = event.clientXdrag.lastY = event.clientYlet top = img.offsetTop + mylet left = img.offsetLeft + mx//拖拽超出容器外部无意义,可能找不回,因此图片位置不能任意拖拽至少有一个角或一个边在容器内部,这里减10就是预留出的宽度img.style.left = getRange(left, img.clientWidth - 10, box.clientWidth - 10) + 'px';img.style.top = getRange(top, img.clientHeight - 10, box.clientHeight - 10) + "px";//如果你不想让图片跑出容器,那就调整这里就好了,控制两个相邻边就能控制整个图片,如// img.style.left = getRange(left,0, box.clientWidth - img.clientWidth) + 'px';// img.style.top = getRange(top,0, box.clientHeight - img.clientHeight) + "px";}}//鼠标松开,初始化操作,如果鼠标在元素外部松开,元素的松开监听是获取不到的,因此需要用document监听document.onmouseup = (event) => {if (event.button === 0) {drag.status = falsedocument.onmousemove = nulldocument.onmouseup = null}}drag.status = truedrag.lastX = event.clientXdrag.lastY = event.clientY}}
}

使用方式

openImagePreview("./1719194464803.jpg");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/860595.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习--KNN算法

目录 一、实验目的 二、实验的软、硬件平台 三、实验内容概述 四、实验设计方案 五、实验内容 一、实验目的 通过本实验掌握kNN算法的原理&#xff0c;熟悉kNN算法。 二、实验的软、硬件平台 python 3.6.5 CourseGrading在线实验环境 三、实验内容概述 1.kNN算法简介…

Android开发系列(九)Jetpack Compose之ConstraintLayout

ConstraintLayout是一个用于构建复杂布局的组件。它通过将子视图限制在给定的约束条件下来定位和排列视图。 使用ConstraintLayout&#xff0c;您可以通过定义视图之间的约束关系来指定它们的位置。这些约束可以是水平和垂直的对齐、边距、宽度和高度等。这允许您创建灵活而响…

⭐最新版!SpringBoot正确集成PageHelper姿势,不再被误导!

GGBond&#x1f508; CSDN的朋友们大家好哇&#xff0c;我是新来的Java练习生 CodeCodeBond&#xff01; 什么是PageHelper&#xff1f; 这里给不知道的人儿说明一下~~ 知道的xdm可以跳过了&#xff01; PageHelper顾名思义是一个 页面 帮手。也就是分页查询的一个好用的工具…

进阶篇08——MySQL管理

系统数据库 常用工具 mysql 客户端工具 mysqladmin 执行管理操作 mysqlbinlog 数据库二进制日志转成文本 mysqlshow 数据库查找 mysqldump 数据库备份 mysqlimport/source 数据库导入

vue3前后端开发:响应式对象不能直接成为前后端数据传输的对象

如图所示&#xff1a;前端控制台打印显示数据是没问题的&#xff0c;后端却显示没有接收到相应数据&#xff0c;但是后端的确接收到了一组空数据 直接说原因&#xff1a;这种情况唯一的原因是没有按正确格式传递参数。每个人写错的格式各有不同&#xff0c;我只是说明一下我在…

[深度学习]长短期记忆网络LSTM

1. 理解序列建模和RNN 长短期记忆网络是一种递归神经网络&#xff08;RNN&#xff09;的变体&#xff0c;专门用于处理和预测时间序列数据。首先&#xff0c;理解标准RNN的基本工作原理是非常重要的&#xff0c;因为LSTM是其在解决长期依赖问题上的改进。 传统的循环神经网络…

STM32定时器入门篇——(基本定时器的使用)

一、基本定时器的功能介绍&#xff1a; STM32F103的基本定时器有&#xff1a;TIM6、TIM7。基本定时器TIM6和TIM7各包含一个16位递增自动装载计数器&#xff0c;最大计数到2^16也就是65536&#xff0c;计数值为0~65535&#xff0c;其拥有的功能有&#xff1a;定时中断、主模式触…

Java网络编程之UDP通信与TCP通信交互代码实现

​import java.net.InetAddress; import java.io.IOException; class Main {public static void main(String[] args) {try { InetAddress localAddress InetAddress.getLocalHost(); //获得本地主机 InetAddress remoteAddress InetAddress.getByName("www.itcast.cn&qu…

电机故障检测系统的通用性限制分析

电机故障检测系统因应用环境、功能需求、经济性等多方面差异而难以实现通用。工厂与实验室在环境条件、使用频率、功能需求、成本、维护及数据处理方面有显著不同&#xff0c;此外&#xff0c;LabVIEW软件在两者中的应用和数据处理也存在差异&#xff0c;这进一步限制了系统的通…

一个实例配置多个服务名

更改参数实现配置多个服务名 需求背景 在做案例模拟的时候发现博主的环境配置的是3个服务名&#xff0c;通常都是一个服务名&#xff0c;服务名就是数据库名&#xff0c;出于好奇进行了以下实验。 环境&#xff1a;Oracle 11.2.0.4 单点 配置多个服务名的意义 可以通过服务…

YOLOv8/v10项目使用教程

根据改好的YOLOv8.yaml改yolov10.yaml教程 打开ultralytics/cfg/models/v8路径&#xff0c;找到需要移植的yaml文件&#xff0c;从其中复制相关的模块。打开一个YOLOv10的yaml文件。 注释掉之前相应位置的模块&#xff0c;并粘贴上面复制的模块&#xff0c;完成。 其余使用步骤…

基于SpringBoot+Vue的美容美发在线预约系统的设计与实现【附源码】

毕业设计(论文) 题目&#xff1a;基于SpringBootVue的美容美发在线预约系统的设计与实现 二级学院&#xff1a; 专业(方向)&#xff1a; 班 级&#xff1a; 学 生&#xff1a; 指导教师&#xff…

Go 语言学习笔记之通道 Channel

Go 语言学习笔记之通道 Channel 大家好&#xff0c;我是码农先森。 概念 Go 语言中的通道&#xff08;channel&#xff09;是用来在 Go 协程之间传递数据的一种通信机制。 通道可以避免多个协程直接共享内存&#xff0c;避免数据竞争和锁的使用&#xff0c;从而简化了并发程…

《Mybatis-Plus》系列文章目录

什么是 MyBatis-Plus&#xff1f; Mybatis-Plus是一个在MyBatis基础上进行增强和扩展的开源Java持久层框架。 Mybatis-Plus&#xff08;简称MP&#xff09;旨在简化开发、提高效率&#xff0c;通过提供一系列便捷的功能和工具&#xff0c;大幅度减少开发人员编写重复代码的时…

如何在web页面下做自动化测试?

自动化测试是在软件开发中非常重要的一环&#xff0c;它可以提高测试效率并减少错误率。在web页面下进行自动化测试&#xff0c;可以帮助我们验证网页的功能和交互&#xff0c;并确保它们在不同浏览器和平台上的一致性。本文将从零开始&#xff0c;详细介绍如何在web页面下进行…

10--7层负载均衡集群

前言&#xff1a;动静分离&#xff0c;资源分离都是在7层负载均衡完成的&#xff0c;此处常被与四层负载均衡比较&#xff0c;本章这里使用haproxy与nginx进行负载均衡总结演示。 1、基础概念详解 1.1、负载均衡 4层负载均衡和7层负载均衡是两种常见的负载均衡技术&#xff…

vite+vue3+ts项目搭建流程 (pnpm, eslint, prettier, stylint, husky,commitlint )

vitevue3ts项目搭建 项目搭建项目目录结构 项目配置自动打开项目eslint①vue3环境代码校验插件②修改.eslintrc.cjs配置文件③.eslintignore忽略文件④运行脚本 prettier①安装依赖包②.prettierrc添加规则③.prettierignore忽略文件④运行脚本 stylint①.stylelintrc.cjs配置文…

前端自动化

前端自动化的内容 自动化代码检查自动化测试自动化构建自动化部署自动化文档 前端自动化的最佳实践

基于改进YOLOv5的安全帽检测算法 | 引入Ghost卷积 + 添加CA注意力机制 + 更换Neck网络之BiFPN + 更换损失函数之WIoU

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。为了解决建筑工地、隧道、煤矿等施工场景中现有安全帽检测算法对于小目标、密集目标以及复杂环境下的检测精度低的问题&#xff0c;设计实现了一种基于YOLOv5的改进目标检测算法&#xff0c;记为YOLOv5-GBCW。首先使用Ghos…

C语言 | Leetcode C语言题解之第166题分数到小数

题目&#xff1a; 题解&#xff1a; struct HashMapNode {int key;int val;UT_hash_handle hh; };struct HashMapNode* hashMap NULL;int hashMapAdd(int key, int val) {struct HashMapNode* node;HASH_FIND_INT(hashMap, &key, node);if(node ! NULL){return node->…