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><!-- 點擊的圖片 --><divv-if="selectedImage"class="enlarged-image-box"@wheel="onZoom"@click="closeImage"><img:src="selectedImage"draggable="true"@dragstart="onDragStart"@dragend="onDragEnd"@load="onImageLoad"@click.stopref="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;// 初始化縮放比例為1scale.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) {// 最大縮放比例為3scale.value += 0.1;isZoomVisible.value = true;}break;case 1: // 減號if (scale.value > 0.5) {// 最小縮放比例為0.5scale.value -= 0.1;scale.value = Math.max(scale.value, 0.1);isZoomVisible.value = true;}break;case 2: // 1:1if (!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.pswp.cn/web/62562.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62562.shtml
英文地址,請注明出處:http://en.pswp.cn/web/62562.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【計算機網絡】實驗12:網際控制報文協議ICMP的應用

實驗12 網際控制報文協議ICMP的應用 一、實驗目的 驗證ping命令和tracert命令的工作原理。 二、實驗環境 Cisco Packet Tracer模擬器 三、實驗過程 1.構建網絡拓撲并進行信息標注&#xff0c;將所需要配置的IP地址寫在對應的主機或者路由器旁邊&#xff0c;如圖1所示。 圖…

迭代器模式的理解和實踐

引言 在軟件開發中&#xff0c;我們經常需要遍歷容器對象&#xff08;如數組、列表、集合等&#xff09;中的元素。如果每個容器對象都實現自己的遍歷算法&#xff0c;那么代碼將會變得冗余且難以維護。為了解決這個問題&#xff0c;迭代器模式應運而生。迭代器模式是一種行為型…

TS2339: Property ‘value‘ does not exist on type ‘MessageBoxData‘.

1、源代碼 <template><el-dialog:visible"visible":before-close"handleClose":close-on-click-modal"false"title"邀請碼"width"1200px"append-to-bodydestroy-on-close><div class"invite-code-wrap…

ubuntu防火墻(三)——firewalld使用與講解

本文是Linux下&#xff0c;用ufw實現端口關閉、流量控制(二) firewalld使用方式 firewalld 是一個動態管理防火墻的工具&#xff0c;主要用于 Linux 系統&#xff08;包括 Ubuntu 和 CentOS 等&#xff09;。它提供了一個基于區域&#xff08;zones&#xff09;和服務&#x…

Windows 安裝配置 RabbitMQ 詳解

博主介紹&#xff1a; 計算機科班人&#xff0c;全棧工程師&#xff0c;掌握C、C#、Java、Python、Android等主流編程語言&#xff0c;同時也熟練掌握mysql、oracle、sqlserver等主流數據庫&#xff0c;能夠為大家提供全方位的技術支持和交流。 工作五年&#xff0c;具有豐富的…

R語言的數據結構--矩陣

【圖書推薦】《R語言醫學數據分析實踐》-CSDN博客 《R語言醫學數據分析實踐 李丹 宋立桓 蔡偉祺 清華大學出版社9787302673484》【摘要 書評 試讀】- 京東圖書 (jd.com) R語言醫學數據分析實踐-R語言的數據結構-CSDN博客 矩陣是一個二維數組&#xff0c;矩陣中的元素都具有相…

JAVA基礎學習筆記_反射+動態代理

文章目錄 反射獲取class對象的三種方式獲取構造方法獲取成員變量獲取成員方法反射的作用 動態代理 反射 允許對成員變量\成員方法\構造方法的信息進行編程訪問 把類內的信息扒的干干凈凈,獲取解剖 獲取從class字節碼文件中獲取 獲取class對象的三種方式 public static void …

微信小程序一鍵復制功能

wx.setClipboardData(Object object) 設置系統剪貼板的內容。調用成功后&#xff0c;會彈出 toast 提示"內容已復制"&#xff0c;持續 1.5s wx.setClipboardData({data: 你需要復制的內容,success (res) {wx.getClipboardData({success (res) {console.log(res.dat…

【Python網絡爬蟲 常見問題匯總】

目錄 1. 爬取圖片出現403解決辦法&#xff1a;設置請求頭中的Referer字段 2.關于干壞事的問題后續不定期更新 歡迎共同探討學習進步 1. 爬取圖片出現403 問題出自案例9&#xff0c;已解決。 【Python網絡爬蟲筆記】9- 抓取優美圖庫高清壁紙 當在爬取圖庫圖片時遇到 403 錯誤…

Linux: docker: 怎么修改 proc下的文件內容?

文章目錄 參考問題方法 1:在宿主機上修改參數方法 2:啟動容器時掛載 /proc 為可寫方法 3:通過 Kubernetes 調整配置方法 4:構建特權容器參考 https://docs.docker.com/security/for-admins/hardened-desktop/enhanced-container-isolation/features-benefits/#procfs–sys…

分布式 分布式事務 總結

前言 相關系列 《分布式 & 目錄》《分布式 & 分布式事務 & 總結》《分布式 & 分布式事務 & 問題》 分布式事務 所謂分布式事務是指操作范圍籠罩多個不同節點的事務。例如對于訂單節點&庫存節點而言&#xff0c;一次完整的交易需要同時調動兩個節…

STM32+模擬或硬件IIC+SHT20驅動問題:接上拉電阻、BUSY死鎖?

主要問題&#xff1a; 1&#xff0c;使用STM32F103C8T6&#xff0c;模擬IIC&#xff0c;SCL和SDA口配置為推挽輸出上拉&#xff0c;主要是SDA腳&#xff0c;每次都要輸出輸入模式重新配置&#xff0c;雖然也能通信&#xff0c;但不穩定&#xff0c;出錯率大&#xff1b; 2&…

【工業機器視覺】基于深度學習的水表盤讀數識別(3-數據標注與轉換)

【工業機器視覺】基于深度學習的儀表盤識讀&#xff08;2&#xff09;-CSDN博客 數據標注 標注擴展 Labelme 和 LabelImg 都是用于創建機器學習和計算機視覺項目所需標注數據的工具。它們都允許用戶通過圖形界面手動標注圖像&#xff0c;但各自有其特點和適用場景。 Labelme…

靜態路由與交換機配置實驗

1.建立網絡拓撲 添加2臺計算機&#xff0c;標簽名為PC0、PC1&#xff1b;添加2臺二層交換機2960&#xff0c;標簽名為S0、S1&#xff1b;添加2臺路由器2811&#xff0c;標簽名為R0、R1&#xff1b;交換機劃分的VLAN及端口根據如下拓撲圖&#xff0c;使用直通線、DCE串口線連接…

【Spark】Spark Join類型及Join實現方式

Spark Join類型 1. Inner Join (內連接) 示例&#xff1a;val result df1.join(df2, df1("id") df2("id"), "inner")執行邏輯&#xff1a;只返回那些在兩個表中都有匹配的行。 2. Left Join (左外連接) 示例&#xff1a;val result df1.jo…

socket UDP 環路回顯的服務端

基于socket通訊的方式&#xff0c;無論用http或者udp或者自定義的協議&#xff0c;程序結構都是類似的。這個以UDP協議為例簡要說明。 #include <stdio.h> // 標準輸入輸出庫 #include <sys/types.h> // 提供了一些數據類型&#xff0c;如ssize_t #include <sy…

Linux:network:添加ip的時候自動添加一個本地路由

文章目錄 問題問題 最近在看一個路由的問題,順便看內核代碼,發現在添加IP的時候,內核會自動添加一個local route。 net/ipv4/devinet.c inet_rtm_newaddr->__inet_insert_ifa /* Send message first, then call notifier.Notifier will trigger FIB update, so thatlis…

Magnet Player:一款基于Web的磁力鏈媒體播放器

Magnet Player&#xff1a;一款基于Web的磁力鏈媒體播放器 項目地址:https://gitcode.com/gh_mirrors/ma/magnet-player 是一個創新的開源項目&#xff0c;它允許用戶直接在瀏覽器中播放磁力鏈&#xff08;Magnet URI&#xff09;內容&#xff0c;無需下載或安裝任何桌面應用…

php:完整部署Grid++Report到php項目,并實現模板打印

一、下載Grid++Report軟件 路徑:開發者安裝包下載 - 銳浪報表工具 二、 安裝軟件 1、對下載的壓縮包運行內部的exe文件 2、選擇語言 3、 完成安裝引導 下一步即可 4、接收許可協議 點擊“我接受” 5、選擇安裝路徑 “瀏覽”選擇安裝路徑,點擊"安裝" 6、完成…

web安全攻防入門教程

Web安全攻防入門教程 Web安全攻防是指在Web應用程序的開發、部署和運行過程中&#xff0c;保護Web應用免受攻擊和惡意行為的技術與策略。這個領域不僅涉及防御措施的實現&#xff0c;還包括通過滲透測試、漏洞挖掘和模擬攻擊來識別潛在的安全問題。 本教程將帶你入門Web安全攻…