1.0.3 • Published 2 years ago
video-printscreen-selector v1.0.3
Installation
$ npm install video-printscreen-selector
Description
有这么一个需求:上传视频后需要用户上传视频封面,但是用户上传(截取)的封面尺寸跟视频尺寸大小不一,因为封面图片尺寸小于视频尺寸,当开始播放时,图片会给用户一种视频放大了的感觉,体验不太友好。这个组件是通过获取视频的大小,再通过截取视频画面提供上传视频者更方便选择视频封面。
Demo
图中结合ant-design-vue UI 走马灯组件实现选取组件
https://sm.ms/image/dnNhgSuCUk7A2XJ
Usage
示例1:
上传视频文件时实例化(多用于新建)
父组件:
<script>
// 子组件
import videoModal from '@/components/videoController/index.vue'
import VideoController from 'video-printscreen-selector'
beforeUpload (file) {
if (!validMajorBrowser(this)) return false
const that = this
const isJpgOrPng = file.type === 'video/mp4'
// 判断视频格式
if (!isJpgOrPng) {
this.$message.error('目前只能上传 MP4 格式的视频!')
return false
}
// 判断视频大小
const isLt2M = file.size / 1024 / 1024 < 20
if (!isLt2M) {
this.$message.error('视频只能上传 20 M以内!')
return false
}
this.uploading = true
// 实例化
this.videoController = new VideoController()
// 获取视频宽、高、视频长度
this.videoController.getVidewInfo(file)
uploadOSS(file).then((results) => {
// 上传完成后, 赋值视频连接
this.$set(that.form, 'url', results.res.requestUrls[0].toString())
this.$refs.videoUrl.clearValidate()
that.$message.info(`上传成功`)
this.uploading = false
this.showVideoModal = true // 弹出子组件
})
return false
},
// 附赠base64转file文件方法
onSelectImage (img) {
const that = this
// base64转blob
const base64ToBlob = (base64Data) => {
const arr = base64Data.split(',')
const fileType = arr[0].match(/:(.*?)/)[1]
const bstr = atob(arr[1])
let l = bstr.length
const u8Arr = new Uint8Array(l)
while (l--) {
u8Arr[l] = bstr.charCodeAt(l)
}
return new Blob([u8Arr], {
type: fileType
})
}
// blob转file
const blobToFile = (newBlob, fileName) => {
newBlob.lastModifiedDate = new Date()
newBlob.name = fileName
return newBlob
}
// 生成blob
const blob = base64ToBlob(img)
// 实例化水印对象
const watermarkMaker = new Watermark()
// 生成file文件
const file = blobToFile(blob, Date.parse(new Date()))
// 这个是作者另外一个图片水印插件
watermarkMaker.drawWatermarkImg(file, watermark, res => {
// 图片/视频上传方法
uploadOSS(res).then((results) => {
this.$set(that.form, 'image', results.res.requestUrls[0].toString())
that.$message.info(`上传成功`)
})
})
}
</script>
子组件:
<template>
<a-modal class="modal-group" v-model="showModal" title="视频封面" :width="900" :maskClosable="false">
<a-carousel class="swiper-group" arrows dots-class="slick-dots slick-thumb">
<a slot="customPaging" slot-scope="props">
<img :src="getImgUrl(props.i)" @click="onSelectImage(props.i)" />
</a>
<div v-for="(item, i) in imageList" :key="i">
<img :width="videoController.videoWidth / 3" :height="videoController.videoHeight / 3" :src="item" />
</div>
</a-carousel>
<div class="pages">
<a-button type="link" :disabled="page.pageIndex === 1" @click="handlePage(0)">上一页</a-button>
<a-button type="link" :disabled="page.pageIndex === videoController.maxPage" @click="handlePage(1)">下一页</a-button>
</div>
<div class="modal-footer" slot="footer">
<div>
视频长度:{{ videoController.videoDuration }}s
总页数:{{ videoController.maxPage }}
当前页:
<a-input-number v-model="page.pageIndex" :min="1" :max="videoController.maxPage" @change="onChangePage" />
</div>
<div>
<a-button @click="showModal = false">取消</a-button>
<a-button type="primary" @click="submit">确定</a-button>
</div>
</div>
<a-spin class="spin" v-if="loading" tip="图片生成中..." size="large"></a-spin>
<div v-if="loading" class="spin-mask"></div>
</a-modal>
</template>
<script>
import VideoController from 'video-printscreen-selector'
export default {
data () {
return {
showModal: false,
imageList: [],
loading: false,
current: '',
page: {
pageIndex: 1,
pageSize: 10
}
}
},
props: {
value: {
type: Boolean,
default: false
},
videoController: {
type: Object,
required: true,
default: () => {
return new VideoController() // 默认实例化方法
}
},
videoUrl: {
type: String,
default: ''
}
},
watch: {
value (c) {
this.showModal = c
},
showModal (c) {
if (!c) {
this.current = ''
this.$emit('input', c)
} else {
this.getImageList()
}
}
},
methods: {
async getImageList () {
try {
this.loading = true
this.imageList = await this.videoController.createVideoImagesList(this.videoUrl, 'multi', this.page) // 通过这个方法获取所需截取的图片范围
this.$nextTick(() => {
this.current = this.imageList[0] // 默认选择第一张图片
})
} catch (e) {
this.$message.error(e)
} finally {
this.loading = false
}
},
onSelectImage (i) {
this.current = this.imageList[i]
},
getImgUrl (i) {
return this.imageList[i]
},
handlePage (c) {
if (c) {
// 下一页
this.page.pageIndex += 1
} else {
// 上一页
this.page.pageIndex -= 1
}
this.getImageList()
},
onChangePage (c) {
this.page.pageIndex = c
this.getImageList()
},
submit () {
this.$emit('onSelect', this.current)
this.showModal = false
}
}
}
</script>
示例2:
直接通过视频连接获取(多用于编辑)
import VideoController from 'video-printscreen-selector'
<script>
export default {
methods: {
async buildController () {
const controller = new VideoController()
const img = await controller.createVideoImagesList(
'视频链接',
11
)
}
}
}
</script>
API
实例化后可以获取到的参数/方法:
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
videoWidth | 视频宽度 | number | 0 |
videoHeight | 视频高度 | number | 0 |
maxPage | 分页最大页数 | number | 0 |
videoDuration | 视频长度(秒) | number | 0 |
createVideoImagesList | 截取方法,type为single时默认截取视频第1秒,type也可以具体到某一秒(类型必须为number),当type为'multi'为范围截取,此时page参数必传 | function | url, type = 'single', page = { pageIndex: 1, pageSize: 10 } |