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>
相关推荐
起名时在学Aiifox1 分钟前
深入解析 Electron 打包中的 EPERM: operation not permitted 错误
前端·javascript·electron
hachi031322 分钟前
Vue中input disabled时点击事件不触发怎么办?
javascript·vue.js·ecmascript
BestSongC26 分钟前
基于VUE和FastAPI的行人目标检测系统
vue.js·人工智能·yolo·目标检测·fastapi
漫天黄叶远飞39 分钟前
别再把对象当“字典”!JS 零基础也能看懂的“属性账本”拆解笔记
javascript
华仔啊41 分钟前
20个CSS实用技巧,10分钟从小白变大神!
前端·css
起名时在学Aiifox43 分钟前
Vue3 + Element Plus 表格排序实战:基于状态字段的智能排序方案
前端·javascript·vue.js·element plus
yzx9910131 小时前
基于Flask的智能语音增强系统模拟
前端·javascript·html
青衫码上行1 小时前
【Java Web学习 | 第14篇】JavaScript(8) -正则表达式
java·前端·javascript·学习·正则表达式
我的虾分发1 小时前
虾分发是一个键打包封装APP内测分发平台
javascript
昔人'1 小时前
grid: auto-fit 和 auto-fill区别
css·grid