封装 预览组件(Vue3)
摘要:页面代码 ˂!-- --˃ <!--autointro-->...
页面代码
<div style="width: 100%;height: 100%;background-color: #f5f5f5;border-radius: 4px;">
<PdfViewer v-if="pdfUrl" :pdfUrl="pdfUrl" />
<div class="preview_box" v-if="imgUrl" @wheel.prevent="handleMouseWheel">
<!-- 图片容器(用于限制拖动范围) -->
<div class="img-container" :style="{ overflow: 'hidden' }">
<img
ref="previewImg"
:style="{
transform: `rotate(${rateDeg * 90}deg) scale(${scale})`,
transition: isDragging ? 'none' : 'all .3s linear',
position: 'relative',
left: `${x}px`,
top: `${y}px`
}"
style="width: 100%; height: 100%;"
:src="allFilesImg[currentImgIndex]"
alt="预览图片"
@mousedown="handleMouseDown"
@mouseup="handleMouseUp"
@mousemove="handleMouseMove"
@mouseleave="handleMouseUp"
>
</div>
<!-- <img ref="previewImg" :style="{ transform: `rotate(${rateDeg * 90}deg) scale(${scale})` }" style="width: 100%;height: 100%;transition: all .3s linear;" :src="allFilesImg[currentImgIndex]" alt=""> -->
<el-icon class="btns_hover right_btn" @click="handleChangeNext"><ArrowRight /></el-icon>
<el-icon class="btns_hover left_btn" @click="handleChangePrev"><ArrowLeft /></el-icon>
<div class="rateBtnsBox">
<el-icon @click="handleChangeSize('-')"><ZoomOut /></el-icon>
<el-icon @click="handleChangeSize('+')"><ZoomIn /></el-icon>
<el-icon @click="isShwoPreView = true"><FullScreen /></el-icon>
<el-icon @click="handleRotate('-')"><RefreshLeft /></el-icon>
<el-icon @click="handleRotate('+')"><RefreshRight /></el-icon>
</div>
</div>
</div>
Vue3 逻辑代码
// ================================================ 预览组件逻辑 ============================================
const imgUrl = ref('')
const pdfUrl = ref('')
const currentImgIndex = ref(0) // 控制显示哪张照片
const rateDeg = ref(0) // 控制旋转度数
const scale = ref(1) // 控制缩放比例
// 新增拖动相关变量
const x = ref(0); // 水平偏移
const y = ref(0); // 垂直偏移
const startX = ref(0); // 鼠标按下时的X坐标
const startY = ref(0); // 鼠标按下时的Y坐标
const isDragging = ref(false); // 是否正在拖动
const previewImg = ref(null); // 图片DOM引用
// 鼠标按下:开始拖动
const handleMouseDown = (e) => {
// 只有缩放比例大于1时才允许拖动
if (scale.value <= 1) return;
isDragging.value = true;
startX.value = e.clientX - x.value;
startY.value = e.clientY - y.value;
// 改变鼠标样式为"抓取中"
if (previewImg.value) {
previewImg.value.style.cursor = 'grabbing';
}
// 阻止默认行为,避免拖动时选中文本或触发其他事件
e.preventDefault();
};
// 鼠标移动:处理拖动
const handleMouseMove = (e) => {
if (!isDragging.value) return;
// 计算新的偏移量
x.value = e.clientX - startX.value;
y.value = e.clientY - startY.value;
};
// 鼠标释放:结束拖动
const handleMouseUp = () => {
if (!isDragging.value) return; // 非拖动状态不处理
isDragging.value = false;
// 恢复鼠标样式
if (previewImg.value) {
previewImg.value.style.cursor = 'grab';
}
};
const isShwoPreView = ref(false)
const isCanPriview = (type) => {
const canArr = ['jpg', 'png', 'gif', 'bmp', 'jpeg', 'pdf', 'pneg', 'webp', 'svg', 'tiff', 'ico']
return canArr.includes(type)
}
const isImg = (type) => {
const canArr = ['jpg', 'png', 'gif', 'bmp', 'jpeg', 'pneg', 'webp', 'svg', 'tiff', 'ico']
return canArr.includes(type)
}
const handlegetDetail = (row) => {
imgUrl.value = ''
pdfUrl.value = ''
const isCan = isCanPriview(row.format)
if(isCan){
if(row.format == 'pdf'){
pdfUrl.value = row.fileUrl
} else {
imgUrl.value = row.fileUrl
findImgIndex(row.fileUrl)
}
} else {
const newFileName = `${row.materialFileName}`
proxy.customizeDownload(downloadFile({filePath: row.fileUrl}), newFileName)
}
}
const findImgIndex = (url) => {
const index = allFilesImg.value.findIndex(v => v == url) || 0
currentImgIndex.value = index
}
const handleRotate = (type) => {
if(type == "+"){
rateDeg.value++
} else {
rateDeg.value--
}
// 旋转时重置位置
x.value = 0;
y.value = 0;
}
const handleChangeSize = (type) => {
let step = 0.1
if(type == "+"){
scale.value += step
} else {
scale.value -= step
// 缩小到1倍以下时重置位置
if (scale.value <= 1) {
x.value = 0;
y.value = 0;
}
}
}
// 鼠标滚轮事件处理(缩放图片)
const handleMouseWheel = (e) => {
// 阻止默认行为(避免页面滚动)
e.preventDefault();
// deltaY 为正表示向下滚动(缩小),为负表示向上滚动(放大)
if (e.deltaY < 0) {
// 放大
scale.value = Math.min(scale.value + 0.1, 5); // 最大5倍
} else {
// 缩小
scale.value = Math.max(scale.value - 0.1, 0.5); // 最小0.5倍
// 缩小到1倍以下时重置位置
if (scale.value <= 1) {
x.value = 0;
y.value = 0;
}
}
};
const handleChangePrev = () => {
rateDeg.value = 0
resetImageState()
if(currentImgIndex.value == 0) {
currentImgIndex.value = allFilesImg.value.length - 1
return
}
currentImgIndex.value--
}
const handleChangeNext = () => {
rateDeg.value = 0
resetImageState()
if(currentImgIndex.value == allFilesImg.value.length - 1) {
currentImgIndex.value = 0
return
}
currentImgIndex.value++
}
// 重置图片状态(切换图片/旋转时调用)
const resetImageState = () => {
x.value = 0;
y.value = 0;
scale.value = 1;
rateDeg.value = 0;
};
css 代码
.preview_box{
width: 100%;
height: 100%;
padding: 0 20px;
position: relative;
background: rgba(0, 0, 0, .2);
}
.preview_box:hover .btns_hover,.preview_box:hover .rateBtnsBox{
display: flex;
align-items: center;
}
.img-container {
width: 100%;
height: 100%;
position: relative;
}
.rateBtnsBox{
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(96, 98, 102, 1);
opacity: .7;
width: 40%;
min-height: 30px;
display: flex;
align-items: center;
justify-content: space-around;
gap: .3125rem;
flex-wrap: wrap;
font-size: 20px;
color: #fff;
border-radius: 10px;
padding: 10px 20px;
border: 1px solid #fff;
display: none;
}
.btns_hover{
display: none;
position: absolute;
top: 50%;
z-index: 222;
right: 5px;
border: 1px solid #fff;
opacity: .7;
border-radius: 50%;
width: 40px;
height: 40px;
transform: translateY(-50%);
cursor: pointer;
background-color: #606266;
color: #fff;
transition: all .3s linear;
}
.left_btn{
left: 5px;
}
本文链接:https://blog.smallhao.fun/?id=38 转载需授权!
Chen’Blog版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!