首页前端组件库封装 预览组件(Vue3)

封装 预览组件(Vue3)

分类前端组件库时间2025-10-16 16:52:57发布RustStream浏览154
摘要:页面代码 ˂!-- --˃ <!--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版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

根据类型获取对应的时间段(1-本月,2-上个月,3-近三个月,4-近一年) uni-app 开发小程序 地图选点

游客 回复需填写必要信息
召唤伊斯特瓦尔