vue3實現輪播渲染多張圖每張進行放大縮小拖拽功能互不影響
1.以vue3中el-carousel輪播插件為例
<div class="pic_view"><el-carousel height="100vh" :autoplay="false" ref="carouselRef" @change="handleCarouselChange"><el-carousel-item v-for="item in picLists" :key="item.id" :data-id="item.id"><div :style="{ backgroundColor: item.backgroundColor }" @wheel.prevent="rollImg" class="pic_set":data-id="item.id" ref="picSetRefs"><img :src="getUrl(`${item.url}`)" class="img" @mousedown="startMove($event, item.id)" :style="{left: imgStates[item.id]?.position.x || '25%',top: imgStates[item.id]?.position.y || '0',transform: `scale(${imgStates[item.id]?.zoom || 1})`}" /></div></el-carousel-item></el-carousel></div>
注意:getUrl方法是vite處理靜態資源圖片的,由于是本地為了測試,你們拿例子的時候注意點可以去掉這個方法放你們本地路徑渲染
2.事件
<script setup>
import { ref, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { getImageUrl } from '@/utils/pub-use'const getUrl = getImageUrlconst picLists = reactive([{ id: 1, url: 'yu1.webp', backgroundColor: '#6f6865' },{ id: 2, url: 'yu2.webp', backgroundColor: '#726A67' },{ id: 3, url: 'yu3.webp', backgroundColor: '#705947' },
])// 存儲每個圖片的狀態
const imgStates = reactive({})
const picSetRefs = ref([])
let currentImgId = ref(null)
let isDragging = false
let startX = 0
let startY = 0
let currentImgElement = null
let currentImgRect = null
let initialOffsetX = 0
let initialOffsetY = 0
// yzm提示:由于一般方法只適用單張,特此整合渲染多張圖操作
// 初始化圖片狀態
const initImgState = (id) => {if (!imgStates[id]) {imgStates[id] = {zoom: 0.8,position: { x: '25%', y: '0' },positionConverted: false}}
}// 將百分比位置轉換為像素值
const convertPositionToPixels = (id, element) => {const state = imgStates[id]if (!state || state.positionConverted) returnconst parent = element.parentElementif (!parent) return// 轉換水平位置if (typeof state.position.x === 'string' && state.position.x.includes('%')) {const percent = parseFloat(state.position.x)state.position.x = `${(percent / 100) * parent.clientWidth}px`}// 轉換垂直位置if (typeof state.position.y === 'string' && state.position.y.includes('%')) {const percent = parseFloat(state.position.y)state.position.y = `${(percent / 100) * parent.clientHeight}px`}// 更新元素樣式element.style.left = state.position.xelement.style.top = state.position.ystate.positionConverted = true
}// 輪播切換時更新當前圖片ID
const handleCarouselChange = (currentIndex) => {currentImgId.value = picLists[currentIndex].id// 確保新激活的圖片位置已轉換nextTick(() => {const activeIndex = currentIndexif (picSetRefs.value[activeIndex]) {const img = picSetRefs.value[activeIndex].querySelector('.img')if (img) {convertPositionToPixels(picLists[activeIndex].id, img)}}})
}// 拖動圖片相關函數
const startMove = (e, id) => {currentImgId.value = idinitImgState(id)currentImgElement = e.currentTarget// 確保位置已轉換為像素值if (!imgStates[id].positionConverted) {convertPositionToPixels(id, currentImgElement)}e.preventDefault()isDragging = true// 獲取當前圖片的位置和尺寸currentImgRect = currentImgElement.getBoundingClientRect()// 計算初始位置(直接使用狀態中的像素值)initialOffsetX = parseFloat(imgStates[id].position.x || '0')initialOffsetY = parseFloat(imgStates[id].position.y || '0')// 計算鼠標在圖片中的位置startX = e.clientX - currentImgRect.leftstartY = e.clientY - currentImgRect.topdocument.addEventListener('mousemove', move)document.addEventListener('mouseup', stopMove)
}const move = (e) => {if (!isDragging || !currentImgId.value || !currentImgElement) return// 計算新的位置(考慮縮放)const zoom = imgStates[currentImgId.value].zoom || 1const newX = initialOffsetX + (e.clientX - currentImgRect.left - startX) / zoomconst newY = initialOffsetY + (e.clientY - currentImgRect.top - startY) / zoom// 更新位置imgStates[currentImgId.value].position = {x: `${newX}px`,y: `${newY}px`}currentImgElement.style.left = `${newX}px`currentImgElement.style.top = `${newY}px`
}const stopMove = () => {isDragging = falsecurrentImgElement = nulldocument.removeEventListener('mousemove', move)document.removeEventListener('mouseup', stopMove)
}// 縮放圖片
const rollImg = (e) => {// 獲取事件發生的輪播項IDconst itemId = parseInt(e.currentTarget.getAttribute('data-id'))if (itemId !== currentImgId.value) returninitImgState(itemId)// 獲取當前操作的圖片元素const img = e.currentTarget.querySelector('.img')if (!img) return// 確保位置已轉換為像素值if (!imgStates[itemId].positionConverted) {convertPositionToPixels(itemId, img)}// 獲取當前縮放值const transform = img.style.transform || ''let zoom = imgStates[itemId].zoomif (transform.includes('scale')) {const match = transform.match(/scale\(([\d.]+)\)/)if (match && match[1]) {zoom = parseFloat(match[1])}}// 根據滾輪方向調整縮放const delta = e.deltaY || e.wheelDeltaconst direction = delta > 0 ? -1 : 1zoom += direction * 0.1// 限制縮放范圍zoom = Math.max(0.1, Math.min(zoom, 5))// 更新狀態和樣式imgStates[itemId].zoom = zoomimg.style.transform = `scale(${zoom})`e.preventDefault()
}// 組件卸載時清除事件
onBeforeUnmount(() => {stopMove()
})// 初始化所有圖片狀態
onMounted(() => {picLists.forEach(item => {initImgState(item.id)})// 設置初始當前圖片IDif (picLists.length > 0) {currentImgId.value = picLists[0].id}nextTick(() => {if (picSetRefs.value[0]) {const img = picSetRefs.value[0].querySelector('.img')if (img) {convertPositionToPixels(picLists[0].id, img)}}})
})
</script>
我給每張圖片設置不同背景,這個也要注意,不需要的話可以在HTML代碼塊里把背景操作去掉,geturl這個是我自己封裝的工具文件,你們用不到這個可以刪掉把圖片地址弄對就行
樣式
<style scoped lang="scss">
.pic_view {height: 100vh;overflow: hidden;:deep(.el-carousel) {height: 100%;.el-carousel__container {height: 100%;}.el-carousel__item {display: flex;justify-content: center;align-items: center;}}
}.pic_set {position: relative;width: 100%;height: 100%;overflow: hidden;display: flex;justify-content: center;align-items: center;touch-action: none;.img {position: absolute;height: 100%;max-width: 900px;min-height: 400px;margin-left: 2%;cursor: move;transform-origin: center center;transition: transform 0.1s ease;user-select: none;will-change: transform;}.imgLow {width: 100%;height: 80px;margin-top: 30vh;}
}
</style>
樣式加了過度以及中心放大縮小,看更加絲滑
以上就是三塊區域代碼
效果