vue图片之放大、缩小、1:1、刷新、左切换、全屏、右切换、左旋咋、右旋转、x轴翻转、y轴翻转

news2024/12/23 14:09:46

先上效果,代码在下面

 

 

 

<template>
  <!-- 图片列表 -->
  <div class="image-list">
    <img
      :src="imageSrc"
      v-for="(imageSrc, index) in images"
      :key="index"
      @click="openImage(index)"
      @error="handleImageError(index)"
      alt="Thumbnail Image"
    />
  </div>
  <!-- 点击的图片 -->
  <div
    v-if="selectedImage"
    class="enlarged-image-box"
    @wheel="onZoom"
    @click="closeImage"
  >
    <img
      :src="selectedImage"
      draggable="true"
      @dragstart="onDragStart"
      @dragend="onDragEnd"
      @load="onImageLoad"
      @click.stop
      ref="enlargedImageRef"
      :style="{
        left: `${imageLeft}px`,
        top: `${imageTop}px`,
        transform: `translate(-50%, -60%) scale(${scale}) ${
          isFlippedX ? 'scaleX(-1)' : 'scaleX(1)'
        } ${isFlippedY ? 'scaleY(-1)' : 'scaleY(1)'} rotate(${rotation}deg)`,
      }"
    />
  </div>
  <!-- 控制按钮 -->
  <div class="control-buttons" v-if="selectedImage" v-show="areControlsVisible">
    <img
      :src="buttonSrc"
      v-for="(buttonSrc, index) in controlButtons"
      :key="index"
      @click="onControlButtonClick(index)"
      @error="handleButtonError(index)"
      :class="{ active: activeControlIndex === index }"
      alt="Control Button"
    />
  </div>
  <!-- 中间显示的倍数 -->
  <div class="zoom-percentage" v-if="isZoomVisible">
    {{ zoomPercentage.toFixed(0) }}%
  </div>
  <!-- 全屏的背景图片 -->
  <div class="fullscreen-overlay" v-if="isFullscreen">
    <img :src="selectedImage" class="fullscreen-image" />
  </div>
  <!-- 点击叉号 -->
  <div v-if="selectedImage" @click="closeImage" class="close-button">
    <img src="/assets/叉号.svg" class="close-icon" />
  </div>
  <!-- 最下面显示的图片 -->
  <div class="thumbnail-container" v-if="selectedImage">
    <img
      :src="imageSrc"
      v-for="(imageSrc, index) in images"
      :key="index"
      :style="{ transform: `translateX(${-thumbnailOffsetLeft}px)` }"
      class="thumbnail"
      @click="onThumbnailClick(index)"
      :class="{ active: activeThumbnailIndex === index }"
    />
  </div>
</template>

<script setup>
import { ref, computed } from "vue";

// 状态变量
const selectedImage = ref(""); // 当前显示的大图
const scale = ref(1); // 缩放比例
const isZoomVisible = ref(false); // 控制倍数显示与隐藏
const zoomTimeout = ref(null); // 定时器 ID
const oneToOneScale = ref(1); // 1:1 缩放比例
const imageLeft = ref(window.innerWidth / 2); // 图片初始 left
const imageTop = ref(window.innerHeight / 2); // 图片初始 top
const startPosition = ref({ x: 0, y: 0 }); // 拖拽开始时的鼠标位置
const isAtOneToOne = ref(false); // 是否处于1:1状态
const initialScale = ref(1); // 初始缩放比例
const lastScale = ref(1); // 上一次的缩放比例
const activeControlIndex = ref(null); // 当前激活的控制按钮索引
const activeThumbnailIndex = ref(null); // 当前激活的缩略图索引
const rotation = ref(0); // 图片旋转角度
const isFullscreen = ref(false); // 全屏遮罩
const areControlsVisible = ref(true); // 控制按钮显示与隐藏
const isFlippedX = ref(false); // 是否水平翻转
const isFlippedY = ref(false); // 是否上下翻转
const thumbnailOffsetLeft = ref(0); // 缩略图左侧的偏移量
const zoomPercentage = computed(() => scale.value * initialScale.value * 100); // 计算属性:百分比显示
const enlargedImageRef = ref(null);

// 图片数据
const images = ref([
  "/assets/tibet-1.jpg",
  "/assets/tibet-2.jpg",
  "/assets/tibet-3.jpg",
  "/assets/tibet-4.jpg",
  "/assets/tibet-5.jpg",
  "/assets/tibet-6.jpg",
  "/assets/tibet-7.jpg",
  "/assets/tibet-8.jpg",
  "/assets/tibet-9.jpg",
]);
// 控制按钮数据
const controlButtons = ref([
  "/assets/加号.svg", // 加号
  "/assets/减号.svg", // 减号
  "/assets/1_1.svg", // 1:1
  "/assets/刷新.svg", // 刷新
  "/assets/左箭头.svg", // 左箭头
  "/assets/播放.svg", // 播放
  "/assets/右箭头.svg", // 右箭头
  "/assets/左旋转.svg", // 左旋转
  "/assets/右旋转.svg", // 右旋转
  "/assets/左右箭头.svg", // 左右箭头
  "/assets/上下箭头.svg", // 上下箭头
]);
// 点击关闭
const closeImage = () => {
  selectedImage.value = "";
};
// 重置所有相关状态的函数
const resetImageState = () => {
  scale.value = 1;
  imageLeft.value = window.innerWidth / 2;
  imageTop.value = window.innerHeight / 2;
  isAtOneToOne.value = false;
  rotation.value = 0;
  isFlippedX.value = false;
  isFlippedY.value = false;
  activeControlIndex.value = null;
};
// 图片点击事件
const openImage = (index) => {
  activeThumbnailIndex.value = index;
  selectedImage.value = images.value[index];
  resetImageState(); // 重置所有状态
};
// 图片加载完成事件,用于计算初始缩放比例
const onImageLoad = () => {
  if (enlargedImageRef.value) {
    const naturalWidth = enlargedImageRef.value.naturalWidth;
    const rect = enlargedImageRef.value.getBoundingClientRect();
    const displayedWidth = rect.width;
    // 计算初始缩放比例(显示尺寸与自然尺寸的比例)
    initialScale.value = displayedWidth / naturalWidth;
    // 初始化缩放比例为1
    scale.value = 1;
    // 设置 1:1 缩放比例
    oneToOneScale.value = 1 / initialScale.value;
  }
};
// 拖拽开始事件
const onDragStart = (event) => {
  startPosition.value = { x: event.clientX, y: event.clientY }; // 记录开始时的鼠标位置
};
// 拖拽结束事件
const onDragEnd = (event) => {
  imageLeft.value += event.clientX - startPosition.value.x; // 更新元素的左偏移量
  imageTop.value += event.clientY - startPosition.value.y; // 更新元素的上偏移量
};
// 图片缩放处理函数
const onZoom = (event) => {
  isZoomVisible.value = true;
  // 重置定时器
  clearTimeout(zoomTimeout.value);
  zoomTimeout.value = setTimeout(() => {
    isZoomVisible.value = false;
  }, 1000);

  // 判断滚动方向并设置新的缩放比例
  if (event.deltaY < 0) {
    // 向上滚动
    scale.value += 0.1;
  } else {
    // 向下滚动
    scale.value -= 0.1;
    scale.value = Math.max(scale.value, 0.3); // 确保最小缩放比例为0.3
  }
};

// 控制按钮点击事件
const onControlButtonClick = (index) => {
  switch (index) {
    case 0: // 加号
      if (scale.value < 3) {
        // 最大缩放比例为3
        scale.value += 0.1;
        isZoomVisible.value = true;
      }
      break;
    case 1: // 减号
      if (scale.value > 0.5) {
        // 最小缩放比例为0.5
        scale.value -= 0.1;
        scale.value = Math.max(scale.value, 0.1);
        isZoomVisible.value = true;
      }
      break;
    case 2: // 1:1
      if (!isAtOneToOne.value) {
        lastScale.value = scale.value;
        scale.value = oneToOneScale.value;
        isAtOneToOne.value = true;
      } else {
        scale.value = lastScale.value;
        isAtOneToOne.value = false;
      }
      isZoomVisible.value = true;
      break;
    case 3: // 刷新
      resetImageState(); // 重置所有状态
      isZoomVisible.value = true;
      break;
    case 4: // 左箭头
      navigateToPreviousImage();
      break;
    case 5: // 全屏
      toggleFullscreen();
      break;
    case 6: // 右箭头
      navigateToNextImage();
      break;
    case 7: // 左旋转
      rotation.value -= 90;
      break;
    case 8: // 右旋转
      rotation.value += 90;
      break;
    case 9: // 左右翻转
      isFlippedX.value = !isFlippedX.value; // 切换翻转状态
      break;
    case 10: // 上下翻转
      isFlippedY.value = !isFlippedY.value; // 切换翻转状态
      break;
    default:
      break;
  }

  // 设置当前激活的控制按钮索引
  activeControlIndex.value = index;

  // 重置定时器
  clearTimeout(zoomTimeout.value);
  zoomTimeout.value = setTimeout(() => {
    isZoomVisible.value = false;
  }, 1000);
};

// 导航到上一张图片
const navigateToPreviousImage = () => {
  if (activeThumbnailIndex.value > 0) {
    activeThumbnailIndex.value -= 1;
  } else {
    // 跳转到最后一张图片
    activeThumbnailIndex.value = images.value.length - 1;
  }
  selectImage(activeThumbnailIndex.value);
};

// 导航到下一张图片
const navigateToNextImage = () => {
  if (activeThumbnailIndex.value < images.value.length - 1) {
    activeThumbnailIndex.value += 1;
  } else {
    // 跳转到第一张图片
    activeThumbnailIndex.value = 0;
  }
  selectImage(activeThumbnailIndex.value);
};

// 选择图片并更新状态
const selectImage = (index) => {
  selectedImage.value = images.value[index];
  activeThumbnailIndex.value = index;
  resetImageState(); // 重置所有状态
};

// 全屏切换函数
const toggleFullscreen = () => {
  const element = document.documentElement; // 全屏元素可以是 `document.documentElement`,也可以是图片元素等
  if (
    !document.fullscreenElement &&
    !document.webkitFullscreenElement &&
    !document.mozFullScreenElement &&
    !document.msFullscreenElement
  ) {
    // 进入全屏
    if (element.requestFullscreen) {
      element.requestFullscreen();
    } else if (element.webkitRequestFullscreen) {
      element.webkitRequestFullscreen();
    } else if (element.mozRequestFullScreen) {
      element.mozRequestFullScreen();
    } else if (element.msRequestFullscreen) {
      element.msRequestFullscreen();
    }
    isFullscreen.value = true;
    areControlsVisible.value = false;
  } else {
    // 退出全屏
    if (document.exitFullscreen) {
      document.exitFullscreen();
    } else if (document.webkitExitFullscreen) {
      document.webkitExitFullscreen();
    } else if (document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
    } else if (document.msExitFullscreen) {
      document.msExitFullscreen();
    }
    isFullscreen.value = false;
    areControlsVisible.value = true;
  }
};

// 监听全屏变化以确保遮罩层正确隐藏
document.addEventListener("fullscreenchange", () => {
  if (!document.fullscreenElement) {
    isFullscreen.value = false; // 确保退出全屏时隐藏遮罩层
    areControlsVisible.value = true;
  }
});

// 点击缩略图事件
const onThumbnailClick = (index) => {
  activeThumbnailIndex.value = index;
  selectedImage.value = images.value[index];

  const thumbnailWidth = 32; // 图片宽度(30px)加上左右间距(2px)
  const centerIndex = 4; // 中间显示第5张图片(索引为4)

  if (index <= centerIndex) {
    thumbnailOffsetLeft.value = (centerIndex - index) * thumbnailWidth;
  } else {
    // 点击右边的图片,调整右边距,清零左边距
    thumbnailOffsetLeft.value = (centerIndex - index) * thumbnailWidth;
  }

  resetImageState(); // 重置所有状态
};

// 错误处理函数:图片加载失败
const handleImageError = (index) => {
  console.error(`图片加载失败: ${images.value[index]}`);
  // 可选:设置为占位图
  images.value[index] = "/assets/placeholder.png";
};

// 错误处理函数:控制按钮图片加载失败
const handleButtonError = (index) => {
  console.error(`按钮图片加载失败: ${controlButtons.value[index]}`);
  // 可选:设置为占位图
  controlButtons.value[index] = "/assets/button-placeholder.png";
};
</script>

<style scoped>
/* 图片列表 */
.image-list {
  width: 540px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.image-list img {
  width: 170px;
  height: 170px;
  cursor: pointer;
  object-fit: cover;
  border-radius: 4px;
  transition: transform 0.3s;
}

/* 放大的图片框架 */
.enlarged-image-box {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.5); /* 背景加深 */
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1003;
}

/* 放大的图片 */
.enlarged-image-box img {
  height: 70%;
  position: absolute;
  z-index: 9999;
  cursor: grab; /* 鼠标样式为抓取 */
  user-select: none; /* 禁止用户选择图片 */
  transition: transform 0.3s ease; /* 平滑缩放 */
}

/* 控制按钮图片 */
.control-buttons {
  position: fixed;
  top: 85%;
  left: 50%;
  transform: translate(-50%);
  display: flex;
  z-index: 1008;
}
.control-buttons img {
  width: 20px;
  height: 20px;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 5;
  padding: 3px;
  margin-right: 2px;
  border-radius: 50%;
  cursor: pointer;
  transition: background-color 0.3s, transform 0.3s;
}
.control-buttons img.active {
  background-color: rgba(0, 0, 0, 0.8);
}

/* 激活缩略图 */
.thumbnail-container .thumbnail.active {
  filter: brightness(100%) !important;
}

/* 中间显示的倍数 */
.zoom-percentage {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  border-radius: 5px;
  padding: 8px 12px;
  font-size: 18px;
  z-index: 1111;
  opacity: 0.9;
  transition: opacity 0.3s;
}

/* 全屏遮罩 */
.fullscreen-overlay {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  background-color: black;
  z-index: 1010;
}
.fullscreen-image {
  height: 100%;
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 2000;
  transform: translate(-50%, -50%);
}

/* 点击叉号 */
.close-button {
  background-color: rgba(0, 0, 0, 0.7);
  position: fixed;
  right: 0;
  top: 0;
  border-bottom-left-radius: 50px;
  padding: 4px 4px 5px 8px;
  z-index: 1005;
}
.close-icon {
  height: 17px;
  width: 17px;
}

/* 最下面显示的缩略图 */
.thumbnail-container {
  background-color: rgba(0, 0, 0, 0.5);
  position: fixed;
  bottom: 0;
  left: 0;
  height: 50px;
  width: 100vw;
  display: flex;
  justify-content: center;
  z-index: 1008;
}
.thumbnail-container .thumbnail {
  height: 100%;
  width: 30px;
  margin-right: 2px;
  filter: brightness(70%);
  cursor: pointer;
  transition: filter 0.3s;
}
.thumbnail-container .thumbnail:hover {
  filter: brightness(50%);
}
.thumbnail-container {
  transition: transform 0.8s ease;
}
</style>

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

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

相关文章

Oracle EBS FA 如何打开关闭的资产会计期间?

用户“运行折旧”,误勾选为“关闭期间”,还有一部分资产还需要操作报废和调整,希望后台打开关闭的资产会计期 系统环境 RDBMS : 12.1.0.2.0 Oracle Applications : 12.2.9 解决方案 由官方提供SQL脚本代码如下: /*rollback120.sql - for Release 12.X only(based on r…

Hash、HASHTABLE底层原理【Redis对象篇】

&#x1f3c6; 作者简介&#xff1a;席万里 ⚡ 个人网站&#xff1a;https://dahua.bloggo.chat/ ✍️ 一名后端开发小趴菜&#xff0c;同时略懂Vue与React前端技术&#xff0c;也了解一点微信小程序开发。 &#x1f37b; 对计算机充满兴趣&#xff0c;愿意并且希望学习更多的技…

CentOS 二进制安装部署MongoDB 4.0

一、安装MongoDB 1. 下载 MongoDB 二进制文件 前往 MongoDB 官方下载页面(https://www.mongodb.com/try/download/community) 选择对应版本的 tar 包。 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.28.tgz 2. 解压并移动至目标目录 解压文件&#xff…

Redis篇-5--原理篇4--Lua脚本

1、概述 Redis 支持使用 Lua 脚本来执行复杂的操作&#xff0c;这为 Redis 提供了更强的灵活性和性能优化能力。通过 Lua 脚本&#xff0c;你可以在服务器端执行一系列命令&#xff0c;而不需要多次往返客户端与服务器之间&#xff0c;从而减少了网络延迟并提高了效率。此外&a…

新手上路,学Go还是Python

对于新手来说&#xff0c;Go和Python都是很好的编程语言&#xff0c;它们各有特点&#xff0c;以下是详细的对比来帮助你决定先学哪一个&#xff1a; 一、语法和学习难度 Python 语法简洁易懂&#xff1a;Python以其简洁、优雅的语法而闻名&#xff0c;代码的可读性很高。例如…

OceanBase 社区版 4.0 离线方式升级bp1至bp2 指南(含避坑总结)

注&#xff1a;目前社区版对 4.0 升级 bp1至 bp2也未有完善的文档&#xff0c;本次升级中也是遇到不少坑&#xff0c;写本文也希望对OB感兴趣的可以尝试少些遇坑。 也希望对升级有更好方式建议方式的朋友一起切磋交流&#xff0c;以便再进一步完善升级方案。 第一次做OB的升级&…

python学opencv|读取图像(六)读取图像像素RGB值

【1】引言 前序已经掌握了如何获取灰度图像的像素&#xff0c;文章链接为&#xff1a; python学opencv|读取图像&#xff08;五&#xff09;读取灰度图像像素-CSDN博客 实际上像素就像一个坐标轴&#xff0c;约束了图像的大小。 但实际上我们在学习过程中&#xff0c;对于同…

Linux kill、killall、pkill 命令区别

注&#xff1a;本文为 “Linux kill、killall、pkill” 相关几篇文章合辑。 未整理去重。 kill、killall、pkill、kill -9 区别 区别 进程 ID 唯一&#xff0c;所以 kill 一次只能杀死 1 个进程&#xff0c;其他相同名称的进程仍然存在&#xff0c;而 pkill 和 killall&#…

1139: Coin-row problem

解法&#xff1a; #include <bits/stdc.h> using namespace std; const int N 1e53; int dp[N]; int main() {int n;cin>>n;for (int i1;i<n;i) cin>>dp[i];for (int i2;i<n;i) {dp[i]max(dp[i-1],dp[i-2]dp[i]);}cout<<dp[n]<<endl;retur…

Ubuntu压缩打包解压

ubuntu压缩打包 上图&#xff0c;压缩当前目录svn 为svn.tar.gaz&#xff0c;解压后再当前解压目录生成svn文件 在Ubuntu中&#xff0c;你可以使用tar命令来创建一个压缩包&#xff0c;或者使用zip命令来创建一个.zip压缩文件。以下是两种常见的压缩方法&#xff1a; 下图&am…

Excel 合并工具 将文件复制到目标工作表中与操作日志记录

指定文件夹中读取符合条件的 Excel 文件&#xff0c;将其中的数据按照一定规则复制到目标工作表中&#xff0c;并进行相关的日志记录和工作簿保存操作。 先看下 excel 的结构 合并的结果 log 记录 vba 代码 Sub DeltaCheck()作者和创建时间的注释 定义工作表变量Dim ws As Wor…

Github----提交人不是自己

账号用户名都设置对的,但是提交人不是自己 解决 发现是用户名和账号都夹了"号导致 git config --global user.name "Your Name" git config --global user.email "your.emailexample.com"不用引号 git config --global user.name Your Name git …

ZZCMS2023存在跨站脚本漏洞(CNVD-2024-44822、CVE-2024-44818)

ZZCMS是一款用于搭建招商网站的CMS系统&#xff0c;由PHP语言开发&#xff0c;可快速搭建&#xff1a;医药招商、保健品招商、化妆品招商、农资招商、孕婴童招商、酒类副食类等招商网站。 国家信息安全漏洞共享平台于2024-11-14公布其存在跨站脚本漏洞。 漏洞编号&#xff1a…

[免费]SpringBoot+Vue企业OA自动化办公管理系统【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue企业OA自动化办公管理系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue企业OA自动化办公管理系统 Java毕业设计_哔哩哔哩_bilibili 项目介绍 随着信息技术在管理上越来越深入…

【MySQL】表的基本查询(下)

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

目前Java后端就业前景到底怎么样?

很多人都说今年对于IT行业根本没有所谓的“金三银四”“金九银十”。在各大招聘网站或者软件上不管是大厂还是中小公司大多都是挂个招聘需求&#xff0c;实际并不招人&#xff1b;在行业内的程序员基本都已经感受到了任老前段时间口中所谓的“寒气”。 虽然事实确实是如此&…

机器学习--张量

机器学习–张量 机器学习的数据结构–张量 张量是机器学习程序中的数字容器&#xff0c;本质上就是各种不同维度的数组&#xff0c;如下图所示。 张量的维度称为轴&#xff08;axis&#xff09;&#xff0c;轴的个数称为阶&#xff08;rank&#xff09; 标量–0D张量 impor…

3D 视觉定位技术:汽车零部件制造的智能变革引擎

在汽车零部件制造领域&#xff0c;传统工艺正面临着前所未有的挑战。市场对于零部件精度与生产效率近乎苛刻的要求&#xff0c;促使企业寻求突破之道。而 3D 视觉定位技术&#xff0c;为汽车零部件制造开启了精准定位与智能化生产的新纪元。 3D 视觉定位系统的核心技术原理 3…

uni-app之web-view组件 postMessage 通信【跨端开发系列】

&#x1f517; uniapp 跨端开发系列文章&#xff1a;&#x1f380;&#x1f380;&#x1f380; uni-app 组成和跨端原理 【跨端开发系列】 uni-app 各端差异注意事项 【跨端开发系列】uni-app 离线本地存储方案 【跨端开发系列】uni-app UI库、框架、组件选型指南 【跨端开…

数据结构 (37)外排序的基本方法

前言 外排序&#xff08;External Sorting&#xff09;是指处理那些无法完全加载到内存中的数据集时所使用的排序方法。由于数据量巨大&#xff0c;无法一次性全部放入内存&#xff0c;因此需要使用外部存储设备&#xff08;如磁盘&#xff09;来辅助排序过程。外排序的基本方法…