Uniapp + VUE3.0 实现双向滑块视频裁剪效果

效果图

<template>
  <view v-if="info" class="all">
    <video
        :src="info.videoUrl"
        class="video" id="video" :controls="true" object-fit="fill" :show-fullscreen-btn="false"
        play-btn-position="center"
        :autoplay="true" @loadedmetadata="loadedMetadata"></video>
    <view class="slider">
      <view class="thumb-left" @touchmove="e=>handleTouchMove(e,0)" @touchend="e=>handleTouchEnd(e,0)"
            :style="` margin-left: ${thumbLeft}px;`">{{ start }}
      </view>
      <view class="slider-bg"></view>
      <view class="thumb-right" @touchmove="e=>handleTouchMove(e,1)" @touchend="e=>handleTouchEnd(e,1)"
            :style="` margin-right: ${thumbRight}px;`">{{ end }}
      </view>
    </view>
  </view>

</template>

<script lang="ts" setup>
const videoInfo = defineProps(["info"])
const emit = defineEmits(['onChange'])
import {ref, computed, onMounted, getCurrentInstance} from "vue";
import {
  onReady,
} from "@dcloudio/uni-app"

const min = ref(0)
const max = ref(0)
const minInterval = ref(15)//最小裁剪间隔
const thumbLeft = ref(0)
const thumbRight = ref(0)
const start = ref(computed(() => {
  return Math.round((thumbLeft.value) * rate.value)
}))
const end = ref(computed(() => {
  return Math.round((totalWidth.value - thumbRight.value) * rate.value)
}))

const rate = ref(computed(() => {
  return max.value / totalWidth.value
}))
const interval = ref(computed(() => {
  return minInterval.value / rate.value
}))


const instance = getCurrentInstance()
const thumbLeftSize = ref({
  width: 0,
  height: 0,
  left: 0,
  right: 0
})
const thumbRightSize = ref({
  width: 0,
  height: 0,
  left: 0,
  right: 0
})
let dxLeft = 0
let dxRight = 0
const totalWidth = ref(0)
const videoTotalDuration = ref(0)
let videoContext: UniApp.VideoContext = null
let windowWidth = 0
let timer: number = null

function loadedMetadata(e) {
  max.value = Math.floor(e.detail.duration)
  emit('onChange', {start: start.value, end: end.value})
}

onReady(() => {
  videoContext = uni.createVideoContext('video', instance);
  windowWidth = uni.getSystemInfoSync().windowWidth
})
onMounted(() => {
  uni.createSelectorQuery().in(instance).select('.thumb-left').boundingClientRect(data => {
    console.log(data)
    thumbLeftSize.value = data
    console.log(thumbLeftSize.value)
  }).exec();
  uni.createSelectorQuery().in(instance).select('.thumb-right').boundingClientRect(data => {
    console.log(data)
    thumbRightSize.value = data
    console.log(thumbRightSize.value)
    totalWidth.value = thumbRightSize.value.right - thumbLeftSize.value.left - 2 * thumbLeftSize.value.width
  }).exec();
});

function handleTouchMove(e, index: Number) {
  let pageX = e.touches[0].pageX
  if (index == 0) {
    //左边边view
    dxLeft = Math.max(pageX - thumbLeftSize.value.left, 0)
    //修正
    if (dxLeft + dxRight + interval.value > totalWidth.value) {
      dxLeft = totalWidth.value - dxRight
    }
    console.log("pageX:" + pageX, "dxRight:" + dxRight, "dxLeft:" + dxLeft, "thumbRight:" + thumbRight.value, "thumbLeft:" + thumbLeft.value, "width:" + thumbLeftSize.value.width, "windowWidth:" + windowWidth, thumbRightSize.value.right, "totalWidth:" + totalWidth.value)
    if (dxLeft <= interval.value) {
      //左边边界
      thumbLeft.value = 0
      return
    }
    if (dxRight + dxLeft + interval.value > totalWidth.value) {
      thumbLeft.value = windowWidth - thumbRight.value - 2 * thumbLeftSize.value.width - 2 * thumbLeftSize.value.left - interval.value
    } else {
      thumbLeft.value = dxLeft - interval.value
    }
  } else {
    //右边view
    dxRight = Math.max(windowWidth - pageX - thumbRightSize.value.width, 0)
    //修正
    if (dxRight + dxLeft + interval.value > totalWidth.value) {
      dxRight = totalWidth.value - dxLeft
    }
    console.log("pageX:" + pageX, "dxRight:" + dxRight, "dxLeft:" + dxLeft, "thumbRight:" + thumbRight.value, "thumbLeft:" + thumbLeft.value, "width:" + thumbLeftSize.value.width, "windowWidth:" + windowWidth, thumbRightSize.value.right, "totalWidth:" + totalWidth.value)
    if (dxRight <= interval.value) {
      //右边边界
      thumbRight.value = 0
      return
    }
    if (dxRight + dxLeft + interval.value > totalWidth.value) {
      //左边边界修正
      thumbRight.value = windowWidth - thumbLeft.value - 2 * thumbLeftSize.value.width - 2 * thumbLeftSize.value.left - interval.value
    } else {
      thumbRight.value = dxRight - interval.value
    }
  }

}

function handleTouchEnd(e, index: Number) {
  emit('onChange', {start: start.value, end: end.value})
  videoContext.seek(index == 0 ? start.value : end.value);
  videoContext.play();
}
</script>

<style lang="scss" scoped>
.all {
  margin-left: 25rpx;
  margin-right: 25rpx;

  .video {
    height: 400rpx;
    width: 100%;
  }

  .slider {
    display: flex;
    color: white;
    flex-direction: row;
    height: 100rpx;

    .thumb-left {
      width: 50rpx;
      height: 100%;
      color: black;
      display: flex;
      font-size: 12rpx;
      align-items: center;
      justify-content: center;
      background-color: #8EFB7C;
      border-top-left-radius: 20rpx;
      border-bottom-left-radius: 20rpx;
    }

    .slider-bg {
      display: flex;
      flex: 1;
      background-color: #F1FFF0
    }

    .thumb-right {
      width: 50rpx;
      height: 100%;
      color: black;
      display: flex;
      font-size: 12rpx;
      align-items: center;
      justify-content: center;
      background-color: #8EFB7C;
      border-top-right-radius: 20rpx;
      border-bottom-right-radius: 20rpx;
    }
  }
}


</style>
相关推荐
Devil枫3 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
GIS程序媛—椰子4 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山4 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享5 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
程序媛小果5 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
从兄5 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
凉辰6 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式
清灵xmf7 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
小白白一枚1118 小时前
css实现div被图片撑开
前端·css
我喜欢就喜欢8 小时前
基于qt vs下的视频播放
开发语言·qt·音视频