vue实现预览图片及视频组件

这篇具有很好参考价值的文章主要介绍了vue实现预览图片及视频组件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

vue图片预览组件,vue,vue vue图片预览组件,vue,vue

 组件代码内容 MediaViewer.vue

<template>
  <teleport to="body">
    <transition name="viewer-fade">
      <div ref="wrapper" :tabindex="-1" class="el-image-viewer__wrapper" :style="{ zIndex }">
        <div class="el-image-viewer__mask" @click.self="hideOnClickModal && hide()"></div>
        <!-- CLOSE -->
        <span class="el-image-viewer__btn el-image-viewer__close" @click="hide">
          <el-icon>
            <Close />
          </el-icon>
        </span>
        <!-- ARROW -->
        <template v-if="!isSingle">
          <span class="el-image-viewer__btn el-image-viewer__prev" :class="{ 'is-disabled': !infinite && isFirst }"
            @click="prev">
            <el-icon>
              <ArrowLeft />
            </el-icon>
          </span>
          <span class="el-image-viewer__btn el-image-viewer__next" :class="{ 'is-disabled': !infinite && isLast }"
            @click="next">
            <el-icon>
              <ArrowRight />
            </el-icon>
          </span>
        </template>
        <!-- ACTIONS -->
        <div v-if="isImage" class="el-image-viewer__btn el-image-viewer__actions">
          <div class="el-image-viewer__actions__inner">
            <el-icon @click="handleActions('zoomOut')">
              <ZoomOut />
            </el-icon>
            <el-icon @click="handleActions('zoomIn')">
              <ZoomIn />
            </el-icon>
            <i class="el-image-viewer__actions__divider"></i>

            <el-icon @click="toggleMode">
              <component :is="mode.icon"></component>
            </el-icon>
            <i class="el-image-viewer__actions__divider"></i>

            <el-icon @click="handleActions('anticlocelise')">
              <RefreshLeft />
            </el-icon>

            <el-icon @click="handleActions('clocelise')">
              <RefreshRight />
            </el-icon>
          </div>
        </div>
        <!-- CANVAS -->
        <div class="el-image-viewer__canvas">
          <template v-for="(url, i) in urlList">
            <img v-if="i === index && isImage" ref="media" :key="url" :src="url" :style="mediaStyle"
              class="el-image-viewer__img" @load="handleMediaLoad" @error="handleMediaError"
              @mousedown="handleMouseDown" />
            <video controls="controls" v-if="i === index && isVideo" ref="media" :key="url" :src="url" :style="mediaStyle"
              class="el-image-viewer__img" @load="handleMediaLoad" @error="handleMediaError"
              @mousedown="handleMouseDown"></video>
          </template>
        </div>
      </div>
    </transition>
  </teleport>
</template>

<script setup>
import { computed, ref, onMounted, watch, nextTick } from 'vue'

const props = defineProps({
  urlList: {
    type: Array,
    default: () => [],
  },
  zIndex: {
    type: Number,
    default: 9999,
  },
  initialIndex: {
    type: Number,
    default: 0,
  },
  infinite: {
    type: Boolean,
    default: true,
  },
  hideOnClickModal: {
    type: Boolean,
    default: false,
  }, // 视频格式
  videoType: {
    type: Array,
  },
  //图片格式
  imgType: {
    type: Array,
  }
},)
const emits = defineEmits(['close', "switch"])
const EVENT_CODE = {
  tab: 'Tab',
  enter: 'Enter',
  space: 'Space',
  left: 'ArrowLeft', // 37
  up: 'ArrowUp', // 38
  right: 'ArrowRight', // 39
  down: 'ArrowDown', // 40
  esc: 'Escape',
  delete: 'Delete',
  backspace: 'Backspace',
}


const isFirefox = function () {
  return !!window.navigator.userAgent.match(/firefox/i)
}

const rafThrottle = function (fn) {
  let locked = false
  return function (...args) {
    if (locked) return
    locked = true
    window.requestAnimationFrame(() => {
      fn.apply(this, args)
      locked = false
    })
  }
}

const Mode = {
  CONTAIN: {
    name: 'contain',
    icon: 'FullScreen',
  },
  ORIGINAL: {
    name: 'original',
    icon: 'ScaleToOriginal',
  },
}

const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel'

let _keyDownHandler = null
let _mouseWheelHandler = null
let _dragHandler = null
const loading = ref(true)
const index = ref(props.initialIndex)
const wrapper = ref(null)
const media = ref(null)
const mode = ref(Mode.CONTAIN)
const transform = ref({
  scale: 1,
  deg: 0,
  offsetX: 0,
  offsetY: 0,
  enableTransition: false,
})

const isSingle = computed(() => {
  const { urlList } = props
  return urlList.length <= 1
})

const isFirst = computed(() => {
  return index.value === 0
})

const isLast = computed(() => {
  return index.value === props.urlList.length - 1
})

const currentMedia = computed(() => {
  return props.urlList[index.value]
})

const isVideo = computed(() => {
  const currentUrl = props.urlList[index.value]
  const name = currentUrl.split('.').slice(-1)[0].toLocaleLowerCase()
  const isVideo = props.videoType.find(itemVideo => itemVideo == name);
  if (isVideo) {
    return true
  } else {
    return false
  }
})

const isImage = computed(() => {
  const currentUrl = props.urlList[index.value]
  const name = currentUrl.split('.').slice(-1)[0].toLocaleLowerCase()
  const isImg = props.imgType.find(itemVideo => itemVideo == name);
  if (isImg) {
    return true
  } else {
    return false
  }
})

const mediaStyle = computed(() => {
  const { scale, deg, offsetX, offsetY, enableTransition } =
    transform.value
  const style = {
    transform: `scale(${scale}) rotate(${deg}deg)`,
    transition: enableTransition ? 'transform .3s' : '',
    marginLeft: `${offsetX}px`,
    marginTop: `${offsetY}px`,
  }
  if (mode.value.name === Mode.CONTAIN.name) {
    style.maxWidth = style.maxHeight = '100%'
  }
  return style
})
function hide() {
  deviceSupportUninstall()
  emits('close')
}

function deviceSupportInstall() {
  _keyDownHandler = rafThrottle((e) => {
    switch (e.code) {
      // ESC
      case EVENT_CODE.esc:
        hide()
        break
      // SPACE
      case EVENT_CODE.space:
        toggleMode()
        break
      // LEFT_ARROW
      case EVENT_CODE.left:
        prev()
        break
      // UP_ARROW
      case EVENT_CODE.up:
        handleActions('zoomIn')
        break
      // RIGHT_ARROW
      case EVENT_CODE.right:
        next()
        break
      // DOWN_ARROW
      case EVENT_CODE.down:
        handleActions('zoomOut')
        break
    }
  })

  _mouseWheelHandler = rafThrottle((e) => {
    const delta = e.wheelDelta ? e.wheelDelta : -e.detail
    if (delta > 0) {
      handleActions('zoomIn', {
        zoomRate: 0.015,
        enableTransition: false,
      })
    } else {
      handleActions('zoomOut', {
        zoomRate: 0.015,
        enableTransition: false,
      })
    }
  })

  document.addEventListener('keydown', _keyDownHandler, false)
  document.addEventListener(
    mousewheelEventName,
    _mouseWheelHandler,
    false
  )
}

function deviceSupportUninstall() {
  document.removeEventListener('keydown', _keyDownHandler, false)
  document.removeEventListener(
    mousewheelEventName,
    _mouseWheelHandler,
    false
  )
  _keyDownHandler = null
  _mouseWheelHandler = null
}

function handleMediaLoad() {
  loading.value = false
}

function handleMediaError(e) {
  loading.value = false
}

function handleMouseDown(e) {
  if (loading.value || e.button !== 0) return

  const { offsetX, offsetY } = transform.value
  const startX = e.pageX
  const startY = e.pageY

  const divLeft = wrapper.value.clientLeft
  const divRight =
    wrapper.value.clientLeft + wrapper.value.clientWidth
  const divTop = wrapper.value.clientTop
  const divBottom =
    wrapper.value.clientTop + wrapper.value.clientHeight

  _dragHandler = rafThrottle((ev) => {
    transform.value = {
      ...transform.value,
      offsetX: offsetX + ev.pageX - startX,
      offsetY: offsetY + ev.pageY - startY,
    }
  })
  document.addEventListener('mousemove', _dragHandler, false)
  document.addEventListener(
    'mouseup',
    (e) => {
      const mouseX = e.pageX
      const mouseY = e.pageY
      if (
        mouseX < divLeft ||
        mouseX > divRight ||
        mouseY < divTop ||
        mouseY > divBottom
      ) {
        reset()
      }
      document.removeEventListener(
        'mousemove',
        _dragHandler,
        false
      )
    },
    false
  )

  e.preventDefault()
}

function reset() {
  transform.value = {
    scale: 1,
    deg: 0,
    offsetX: 0,
    offsetY: 0,
    enableTransition: false,
  }
}

function toggleMode() {
  if (loading.value) return

  const modeNames = Object.keys(Mode)
  const modeValues = Object.values(Mode)
  const currentMode = mode.value.name
  const index = modeValues.findIndex((i) => i.name === currentMode)
  const nextIndex = (index + 1) % modeNames.length
  mode.value = Mode[modeNames[nextIndex]]
  reset()
}

function prev() {
  if (isFirst.value && !props.infinite) return
  const len = props.urlList.length
  index.value = (index.value - 1 + len) % len
}

function next() {
  if (isLast.value && !props.infinite) return
  const len = props.urlList.length
  index.value = (index.value + 1) % len
}

function handleActions(action, options = {}) {
  if (loading.value) return
  const { zoomRate, rotateDeg, enableTransition } = {
    zoomRate: 0.2,
    rotateDeg: 90,
    enableTransition: true,
    ...options,
  }
  switch (action) {
    case 'zoomOut':
      if (transform.value.scale > 0.2) {
        transform.value.scale = parseFloat(
          (transform.value.scale - zoomRate).toFixed(3)
        )
      }
      break
    case 'zoomIn':
      transform.value.scale = parseFloat(
        (transform.value.scale + zoomRate).toFixed(3)
      )
      break
    case 'clocelise':
      transform.value.deg += rotateDeg
      break
    case 'anticlocelise':
      transform.value.deg -= rotateDeg
      break
  }
  transform.value.enableTransition = enableTransition
}

watch(currentMedia, () => {
  nextTick(() => {
    const $media = media.value
    if (!$media.complete) {
      loading.value = true
    }
  })
})

watch(index, (val) => {
  reset()
  emits('switch', val)
})

onMounted(() => {
  deviceSupportInstall()
  // add tabindex then wrapper can be focusable via Javascript
  // focus wrapper so arrow key can't cause inner scroll behavior underneath
  wrapper.value?.focus?.()
})


</script>

引用  VideoOrImagePreview.vue

<template>
  <!-- 参数已element-plus图片预览组件相似 -->
  <MediaViewer v-if="isShow" :url-list="realSrcList" :videoType="videoType" :imgType="imgType" @close="closeViewer" />
  <div @click="openViewer" :style="`width:${realWidth};height:${realHeight};`" class="viewer">
    <el-image v-if="isImage" class="viewer-img" fit="cover" :src="realSrc" />
    <div v-if="isVideo" style="z-index: 0;position: relative;">
      <video :src="realSrc" :width="width" style="z-index: -1;position: relative;" :height="height" controls
        disablePictureInPicture="true" controlslist="nodownload noremoteplayback"></video>
    </div>
  </div>
</template>
<script setup>
import { isExternal } from "@/utils/validate";
import MediaViewer from "../MediaViewer"
const props = defineProps({
  src: {
    type: String,
    default: ""
  },
  width: {
    type: [Number, String],
    default: ""
  },
  height: {
    type: [Number, String],
    default: ""
  },
  // 视频格式
  videoType: {
    type: Array,
    default: ["avi", "wmv", "mpg", "mpeg", "mov", "rm", "ram", "swf", "flv", "mp4", "mp3", "wma", "avi", "rm", "rmvb", "flv", "mpg", "mkv"]
  },
  //图片格式
  imgType: {
    type: Array,
    default: ['svgz', 'pjp', 'png', 'ico', 'avif', 'tiff', 'tif', 'jfif', 'svg', 'xbm', 'pjpeg', 'webp', 'jpg', 'jpeg', 'bmp', 'gif']
  }
});
const isShow = ref(false)
// 默认显示第一张处理
const realSrc = computed(() => {
  if (!props.src) {
    return;
  }
  let real_src = props.src.split(",")[0];
  if (isExternal(real_src)) {
    return real_src;
  }
  return import.meta.env.VITE_APP_BASE_API + real_src;
});
// 判断第一张是否为图片
const isImage = computed(() => {
  if (!realSrc.value) {
    return;
  }
  const name = realSrc.value.split('.').slice(-1)[0].toLocaleLowerCase()
  const isImg = props.imgType.find(itemVideo => itemVideo == name);
  if (isImg) {
    return true
  } else {
    return false
  }
})
// 判断第一张是否为视频
const isVideo = computed(() => {
  if (!realSrc.value) {
    return;
  }
  const name = realSrc.value.split('.').slice(-1)[0].toLocaleLowerCase()
  const isVideo = props.videoType.find(itemVideo => itemVideo == name);
  if (isVideo) {
    return true
  } else {
    return false
  }
})
const realSrcList = computed(() => {
  if (!props.src) {
    return;
  }
  let real_src_list = props.src.split(",");
  let srcList = [];
  real_src_list.forEach(item => {
    if (isExternal(item)) {
      return srcList.push(item);
    }
    return srcList.push(import.meta.env.VITE_APP_BASE_API + item);
  });
  return srcList;
});
const realWidth = computed(() =>
  typeof props.width == "string" ? props.width : `${props.width}px`
);

const realHeight = computed(() =>
  typeof props.height == "string" ? props.height : `${props.height}px`
);
// 关闭预览
function closeViewer() {
  isShow.value = false
}
//打开预览
function openViewer() {
  isShow.value = true
}
</script>
<style lang="scss" scoped>
.viewer {
  cursor: pointer;
  border-radius: 4px;
  overflow: hidden;

  .viewer-img {
    width: 100%;
    height: 100%;
    transition: all 0.3s;
  }

  .viewer-img:hover {
    transform: scale(1.2);
  }
}
</style>
/**
 * 判断path是否为外链
 * @param {string} path
 * @returns {Boolean}
 */
 export function isExternal(path) {
  return /^(https?:|mailto:|tel:)/.test(path)
}

使用

<template>
    <videoorimage-preview v-if="arr" :src="arr"                                                 :width="50" :height="50" />
</template>

注:element-plus,vue3文章来源地址https://www.toymoban.com/news/detail-763063.html

到了这里,关于vue实现预览图片及视频组件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包