自学内容网 自学内容网

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

先上效果,代码在下面

 

 

 

<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>


原文地址:https://blog.csdn.net/weixin_58462329/article/details/144383186

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!