VUE 的彈出框實現圖片預覽和視頻預覽

這是一個基于Vue3封裝的媒體預覽組件,主要功能包括:

  1. 多格式支持:可同時預覽圖片和視頻
  2. 圖片操作功能
    • 縮放(支持滾輪縮放和按鈕控制)
    • 旋轉(90度增量旋轉)
    • 拖拽(僅在放大狀態下可用)
  3. 自適應顯示:圖片自動適應容器大小
  4. 響應式設計:使用Element UI的Dialog作為容器

組件特點:

  • 通過計算屬性動態計算圖片樣式
  • 使用requestAnimationFrame優化拖拽性能
  • 支持圖片加載后自動調整方向
  • 提供視頻播放控制功能

該組件封裝了完整的交互邏輯,可方便地集成到項目中實現媒體預覽功能。

下面是實現代碼:

<template><el-dialog v-model="visible" width="1184px" class="preview-dialog" close align-center><template v-if="!isVideoPreview" #footer><div class="preview-dialog-footer"><el-button type="text" @click="zoomOut" class="zoom-button"><svgxmlns="http://www.w3.org/2000/svg"width="20"height="20"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg></el-button><el-button type="text" @click="zoomIn" class="zoom-button"><svgxmlns="http://www.w3.org/2000/svg"width="20"height="20"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg></el-button><el-button type="text" @click="rotateImage(90)" class="rotate-button"><img :src="Rotate" /></el-button></div></template><div class="preview-content" @wheel="handleWheel"><imgv-if="!isVideoPreview":src="previewUrl"@load="onImageLoad":style="imageStyle"ref="previewImage"@mousedown="startDrag"@mousemove="onDrag"@mouseup="endDrag"@mouseleave="endDrag"/><video v-if="isVideoPreview" :src="previewUrl" class="media-video" controls autoplay></video></div></el-dialog>
</template><script setup>import { ref, computed, watch } from 'vue';import Rotate from '@/assets/home/icon/rotate.svg';const props = defineProps({modelValue: Boolean,previewUrl: {type: String,default: ''},isVideoPreview: Boolean});const emit = defineEmits(['update:modelValue']);const visible = ref(props.modelValue);watch(() => props.modelValue,(newVal) => {visible.value = newVal;});watch(visible, (val) => {emit('update:modelValue', val);});const imageRotation = ref(0);const previewImage = ref(null);const dialogWidth = 1184;const dialogHeight = 648;const zoomLevel = ref(1);// 縮放限制const minZoom = 0.1;const maxZoom = 5;// 拖拽相關變量const isDragging = ref(false);const dragStartX = ref(0);const dragStartY = ref(0);const imageStartLeft = ref(0);const imageStartTop = ref(0);const imageLeft = ref(0);const imageTop = ref(0);const rafId = ref(0);const zoomIn = () => {if (zoomLevel.value < maxZoom) {zoomLevel.value = Math.min(zoomLevel.value + 0.1, maxZoom);}};const zoomOut = () => {if (zoomLevel.value > minZoom) {zoomLevel.value = Math.max(zoomLevel.value - 0.1, minZoom);}};const handleWheel = (event) => {event.preventDefault();if (event.deltaY < 0) {zoomIn();} else {zoomOut();}};const startDrag = (event) => {if (zoomLevel.value <= 1) return; // 只有在放大時才能拖拽isDragging.value = true;dragStartX.value = event.clientX;dragStartY.value = event.clientY;imageStartLeft.value = imageLeft.value;imageStartTop.value = imageTop.value;if (previewImage.value) {previewImage.value.style.cursor = 'grabbing';}// 阻止默認行為,防止圖片被選中event.preventDefault();};const onDrag = (event) => {if (!isDragging.value || zoomLevel.value <= 1) return;// 使用 requestAnimationFrame 優化性能if (rafId.value) {cancelAnimationFrame(rafId.value);}rafId.value = requestAnimationFrame(() => {const deltaX = event.clientX - dragStartX.value;const deltaY = event.clientY - dragStartY.value;imageLeft.value = imageStartLeft.value + deltaX;imageTop.value = imageStartTop.value + deltaY;rafId.value = 0;});// 阻止默認行為event.preventDefault();};const endDrag = () => {isDragging.value = false;if (rafId.value) {cancelAnimationFrame(rafId.value);rafId.value = 0;}if (previewImage.value) {previewImage.value.style.cursor = 'grab';}};const rotateImage = (degree) => {console.log('翻轉', degree, (imageRotation.value + degree) % 360);imageRotation.value += degree;// zoomIn();// 旋轉時重置縮放級別以避免布局問題zoomLevel.value = 1;// 重置拖拽位置imageLeft.value = 0;imageTop.value = 0;};const imageDimensions = computed(() => {if (!previewImage.value) return { width: 0, height: 0 };const img = previewImage.value;const naturalWidth = img.naturalWidth;const naturalHeight = img.naturalHeight;const isRotated = imageRotation.value % 180 !== 0;const displayWidth = isRotated ? naturalHeight : naturalWidth;const displayHeight = isRotated ? naturalWidth : naturalHeight;return { width: displayWidth, height: displayHeight };});const imageStyle = computed(() => {if (!previewImage.value) return {};const { width: displayWidth, height: displayHeight } = imageDimensions.value;// 計算基礎縮放比例,確保圖片適應容器const baseScale = Math.min(dialogWidth / displayWidth, dialogHeight / displayHeight);// 應用用戶縮放級別const finalScale = baseScale * zoomLevel.value;// 計算縮放后的尺寸const scaledWidth = displayWidth * finalScale;const scaledHeight = displayHeight * finalScale;// 居中定位const left = (dialogWidth - scaledWidth) / 2 + imageLeft.value;const top = (dialogHeight - scaledHeight) / 2 + imageTop.value;return {position: 'absolute',left: `${left}px`,top: `${top}px`,width: `${scaledWidth}px`,height: `${scaledHeight}px`,transform: `rotate(${imageRotation.value}deg)`,transformOrigin: 'center center',cursor: zoomLevel.value > 1 ? 'grab' : 'default'};});const onImageLoad = () => {// 重置旋轉和縮放imageRotation.value = 0;zoomLevel.value = 1;imageLeft.value = 0;imageTop.value = 0;for (let index = 0; index < 4; index++) {console.log('執行第幾次', index + 1);rotateImage(90); //執行四次 可以讓圖片以合適的寬度呈現}// 可選:調試用// console.log('Image loaded:', previewImage.value.naturalWidth, previewImage.value.naturalHeight);};
</script><style lang="scss" scoped>.preview-dialog {:deep(.el-dialog) {height: 648px;display: flex;flex-direction: column;}:deep(.el-dialog__body) {flex: 1;overflow: hidden !important;text-align: center;padding: 0;position: relative;}.preview-dialog-footer {display: flex;justify-content: center;align-items: center;}.rotate-button {font-size: 20px;padding: 10px;}.preview-content {width: 1184px;height: 648px;display: flex;justify-content: center;align-items: center;overflow: hidden;position: relative;img {max-width: none;max-height: none;object-fit: contain;user-select: none;// 添加硬件加速transform: translateZ(0);backface-visibility: hidden;perspective: 1000px;}.media-video {max-width: 100%;max-height: 100%;object-fit: contain;}}}
</style>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920050.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920050.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920050.shtml

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

相關文章

【Linux基礎知識系列】第一百零九篇 - 使用shell的輸入與輸出重定向

在 Linux 系統中&#xff0c;Shell 是用戶與操作系統交互的界面&#xff0c;通過命令行輸入命令來執行各種操作。輸入與輸出重定向是 Shell 編程中非常重要的概念&#xff0c;它允許用戶將命令的輸出保存到文件中&#xff0c;或者從文件中讀取輸入&#xff0c;從而實現更靈活的…

Redis面試精講 Day 30:Redis面試真題解析與答題技巧

【Redis面試精講 Day 30】Redis面試真題解析與答題技巧 在“Redis面試精講”系列的第30天&#xff0c;我們迎來收官之作——Redis面試真題解析與答題技巧。這一天的核心目標是&#xff1a;幫助你系統化梳理前29天所學知識&#xff0c;掌握高頻面試題的解題思路&#xff0c;提升…

設計模式:單例模式(Singleton Pattern)

文章目錄一、單例模式的概念二、單例模式的結構三、常見實現方式3.1 餓漢式單例3.2 懶漢式單例一、單例模式的概念 單例模式&#xff08;Singleton Pattern&#xff09;是一種創建型設計模式&#xff0c;它的核心思想是&#xff1a;保證在一個進程中&#xff0c;某個類僅有一個…

Swift 解法詳解 LeetCode 362:敲擊計數器,讓數據統計更高效

文章目錄 摘要 描述 題解答案 題解代碼分析 代碼講解 示例測試及結果 時間復雜度 空間復雜度 總結 摘要 “敲擊計數器”這道題聽上去像個小游戲里的功能,但其實它背后對應的是一個常見的需求:在過去一段時間內統計事件發生的次數。比如網站的訪問量統計、API 調用次數限制、…

coze工作流200+源碼,涵蓋AI文案生成、圖像處理、視頻生成、自動化腳本等多個領域

AI 博主風哥在github分享了 200 實用生產力coze工作流&#xff0c;涵蓋AI文案生成、圖像處理、視頻生成、自動化腳本等多個領域&#xff0c;導入即用&#xff0c;項目地址https://github.com/Hammer1/cozeworkflows github下載慢也可前往該地址下載https://pan.baidu.com/s/1fC…

AI與SEO關鍵詞協同優化

內容概要 人工智能&#xff08;AI&#xff09;技術的迅猛發展正深刻變革著搜索引擎優化&#xff08;SEO&#xff09;的實踐方式&#xff0c;特別是在關鍵詞策略這一核心領域。兩者的深度融合&#xff0c;為企業在數字海洋中精準導航提供了前所未有的強大工具。通過AI驅動的智能…

【Unity開發】Unity核心學習(二)

二、動畫基礎 1、Animation動畫窗口 &#xff08;1&#xff09;介紹&#xff08;2&#xff09;Animation窗口功能2、創建編輯動畫 面板變化&#xff1a;動畫文件界面&#xff1a;3、Animator動畫狀態機 &#xff08;1&#xff09;有限狀態機概念&#xff08;2&#xff09;Anima…

NETSDK1045 當前 .NET SDK 不支持將 .NET 8.0 設置為目標。請將 .NET 5.0 或更低版本設置為目標,或使用支持

C# 項目中的目標框架無法修改并且顯示為空 嚴重性 代碼 說明 項目 文件 行 禁止顯示狀態 錯誤 NETSDK1045 當前 .NET SDK 不支持將 .NET 8.0 設置為目標。請將 .NET 5.0 或更低版本設置為目標&#xff0c;或使用支持 .NET 8.0 的 .NET SDK 版本。 Padim C:\Program …

MNIST 數據集mnist.npz詳解

MNIST 數據集是機器學習領域最著名的數據集之一&#xff0c;全稱為"Modified National Institute of Standards and Technology"數據庫。它包含了大量手寫數字的圖像&#xff0c;是入門機器學習和深度學習的經典數據集。1. MNIST 數據集概述 60,000 張訓練圖像 10,00…

深入理解HTTPS:從概念到實戰優化

深入理解HTTPS&#xff1a;從概念到實戰優化一&#xff1a;概述二&#xff1a;工作流程三&#xff1a;創建自簽名證書四&#xff1a;案例1&#xff09;案例一&#xff1a;HTTPS 搭建2&#xff09;案例二&#xff1a;HTTP/2 搭建3&#xff09;案例三&#xff1a;HTTP 重定向 HTT…

MySQL數據備份與恢復全攻略

一、數據備份與恢復按照備份方式分類&#xff1a;物理備份&#xff0c;直接復制數據庫的物理文件&#xff0c;可以直接拷貝和恢復&#xff1b;邏輯備份&#xff0c;通過SQL語句導出數據庫結構和數據&#xff0c;可用于不同版本和不同類型的MySQL數據庫之間的數據遷移。按照數據…

單機多卡間大張量傳輸迷惑行為?

老鐵們我最近真的好慘&#x1f62d;&#xff0c;一個大模型在單機多卡上運行就是出錯&#xff0c;debug看的老眼昏花&#xff0c;最后發現大張量在設備間直接傳輸會有很發癲的行為&#xff0c;還請大家幫我看看&#x1f647;?摒棄屎山一樣的代碼&#xff0c;簡單運行下列腳本i…

無法將“pnpm”項識別為 cmdlet、函數、腳本文件或可運行程序的名稱。請檢查名稱的拼寫,如果包括路徑,請確保路徑正確,然后再試一次。

1 問題描述今天使用pnpm安裝如下報錯&#xff1a;pnpm : 無法將“pnpm”項識別為 cmdlet、函數、腳本文件或可運行程序的名稱。請檢查名稱的拼寫&#xff0c;如果包括路徑&#xff0c;請確保路徑正確&#xff0c;然后再試一次。 所在位置 行:1 字符: 1pnpm install~~~~ Categor…

內核編譯 day61

二&#xff1a;內核啟動 一&#xff1a;流程 sudo vim /etc/network/interfaces sudo chmod 0666 /etc/default/tft...... //修改可讀uboot命令&#xff1a; help/&#xff1f; &#xff1a; 幫助手冊&#xff0c;列出uboot支持的所有命令 printenv/print 打印環境變量 sete…

【YOLOv5部署至RK3588】模型訓練→轉換RKNN→開發板部署

已在GitHub開源與本博客同步的YOLOv5_RK3588_object_detect項目&#xff0c;地址&#xff1a;https://github.com/A7bert777/YOLOv5_RK3588_object_detect/tree/main 詳細使用教程&#xff0c;可參考README.md或參考本博客第六章 模型部署 文章目錄一、項目回顧二、模型選擇介紹…

Telematics Control Unit(TCU)的系統化梳理

1、Telematics Control Unit (TCU)概述 TCU中文名為遠程信息處理控制單元&#xff0c;很多場合都稱為Telematics Box&#xff0c;又叫TBox&#xff0c;顧名思義&#xff0c;一般都為一個獨立的盒子&#xff08;如圖2、圖3所示&#xff09;&#xff0c;負責和云端的遠程信息交互…

Appium學習筆記

adb構成client端&#xff0c;在電腦上&#xff0c;負責發送adb命令daemon守護進程&#xff0c;在手機上&#xff0c;負責接收和執行adb命令server端&#xff0c;在電腦上&#xff0c;負責管理client和daemon之間的通信![[Pasted image 20250825201322.png]]包名&#xff0c;對應…

棧指針(Stack Pointer)是什么?

棧指針(Stack Pointer)是什么? 首先,用一個簡單易懂的方式解釋棧指針(Stack Pointer)。 核心比喻:摞起來的書 想象有一摞書整齊地堆在桌面上: 這摞書就是“棧”(Stack),它是一種后進先出(LIFO) 的數據結構。你只能從最頂部拿走一本書(“彈出”),或者把一本新…

數據結構:紅黑樹(Red-Black Tree)

目錄 從AVL樹的“煩惱”說起 如何用“顏色”來定義“大致平衡”&#xff1f;—— 紅黑樹的五個規則 五個規則如何保證“大致平衡”&#xff1f; 用 C/C 代碼定義紅黑樹的結構 定義顏色和節點結構 定義樹的結構和哨兵節點 從AVL樹的“煩惱”說起 我們從已經了解的 AVL 樹出…

Ubuntu22.04安裝VMware Tools

文章目錄前言安裝open-mv-tools前言 本教程使用的版本是Ubuntu22.04.5&#xff0c;由于虛擬機上面的重新安裝VMware Tools是灰的&#xff0c;于是自動下載安裝open-mv-tools&#xff0c; 安裝open-mv-tools 打開終端&#xff0c;更新一下 sudo apt update這一步可能需要先…