<template>
	<div class="custom-video" v-if="videoShow">
		<div class="custom-video_container" ref="custom-video_container">
			<div class="custom-video_video">
				<video ref="custom-video" controls :src="url">
					<p>设备不支持</p>
				</video>
			</div>
		</div>
		<!-- 缩略图，裁剪区域 -->
		<div class="video-controls" @mousedown="handleClick">
			<div class="thumbs" ref="thumbs">
				<div class="inner" v-if="thumbCount" ref="thumbs-inner">
					<div class="inner-item" :style="`width:${videoUnitWidth}px;`" v-for="(item, index) in thumbArr"
						:key="index">
						<video width="100%" preload="metadata" :src="item.url" @canplay="item.loading = false;"></video>
						<div class="inner-loading" v-if="item.loading">
							<div class="loading">
								<div class="loading-con"></div>
							</div>
						</div>
					</div>
				</div>
			</div>
			<div class="control-bars" ref="control-bars">
				<div class="control-bars-mask left" data-direction="left" :style="`width:${leftMovePercentage}%;`">
				</div>
				<div class="control-bars-mask right" data-direction="right" :style="`width:${rightMovePercentage}%;`">
				</div>
				<div class="control-bars-progress" :style="`left:${videoEdit.currentPosition}%;`"
					:class="{ animate: videoEdit.play }">
					<svg width="54px" height="24px" viewBox="0 0 54 24" version="1.1" xmlns="http://www.w3.org/2000/svg"
						xmlns:xlink="http://www.w3.org/1999/xlink">
						<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
							<path
								d="M27,23 L27,24 L26,24 L26,23 C26,20.2385763 23.7614237,18 21,18 L9,18 C4.02943725,18 0,13.9705627 0,9 C0,4.02943725 4.02943725,0 9,0 L45,0 C49.9705627,0 54,4.02943725 54,9 C54,13.9705627 49.9705627,18 45,18 L32,18 L32,18 C29.2385763,18 27,20.2385763 27,23 Z"
								id="time_bg" fill="#FFFFFF"></path>
						</g>
					</svg>
					<span class="text">{{ timeTranslate(currentTime) }}</span>
				</div>
				<div class="control-bars-wrapper" :style="`left:${leftMovePercentage}%;right:${rightMovePercentage}%;`">
					<div ref="move-left-icon" class="control-bars-move left"
						@mousedown="handleMoveDown($event, 'left')"></div>
					<div ref="move-right-icon" class="control-bars-move right"
						@mousedown="handleMoveDown($event, 'right')"></div>
				</div>
			</div>
		</div>
		<!-- 按钮文字 -->
		<div class="video-btn">
			<div class="crop-range" style="font-size: 12px; margin-bottom: 4px; text-align: center;">
				<div style="width: 150px;">输入开始时间</div>
				<span></span>
				<div style="width: 150px;">输入截止时间</div>
			</div>
		</div>
		<!-- 按钮区域 -->
		<div class="video-btn">
			<!-- <div class="toggle" @click="togglePlayStatus">
				<div class="toggle-icon" :class="{ playing: videoEdit.play }"></div>
			</div> -->
			<div class="crop-range">
				<div class="crop-input crop-start">
					<input class="text-right" type="text" placeholder="00" maxlength="3" id="range-0"
						v-model.number="inputStartLeftTime" @blur="onBlur('inputStartLeftTime')" />
					<span>：</span>
					<input class="text-left" type="text" placeholder="00" maxlength="2" id="range-1"
						v-model.number="inputStartRightTime" @blur="onBlur('inputStartRightTime')" />
				</div>
				<span style="color:#fff;margin:0 10px;"></span>
				<div class="crop-input crop-end">
					<input class="text-right" type="text" placeholder="00" maxlength="3" id="range-2" v-model.number="inputEndLeftTime" @blur="onBlur('inputEndLeftTime')" />
					<span>：</span>
					<input class="text-left" type="text" placeholder="00" maxlength="2" id="range-3" v-model.number="inputEndRightTime" @blur="onBlur('inputEndRightTime')" />
				</div>
			</div>
		</div>
	</div>
</template>
<script>
	export default {
		name: 'vueVideoClip',
		props: {
			url: {
				type: String,
				required: true
			}
		},
		data() {
			return {
				blobURL: '',
				videoState: {
					play: false, // 播放状态
					currentPosition: 0 // 当前播放点距离左边的百分比
				},
				videoEdit: {
					start: 0,
					end: 0,
					duration: 0,
					play: false, // 播放状态
					currentPosition: 0 // 当前播放点距离左边的百分比
				},
				videoDom: null, // video
				duration: 0, // 视频总时长
				currentTime: 0, // 视频当前播放时长 = this.videoDom.currentTime
				objectURL: '',
				videoUnit: 0,
				videoUnitWidth: 0,
				videoRatio: 0,
				isPortraitVideo: false,
				thumbCount: 0,
				thumbArr: [], // 缩略图数组
				leftMovePercentage: 0,
				leftMoveInit: 0,
				rightMovePercentage: 0,
				rightMoveInit: 0,
				videoShow: true,
				
				loadingTap: '',
			}
		},
		mounted() {
			this.loadingTap = this.$loading({
				text: '加载中',
				spinner: 'el-icon-loading',
			});
			this.init();
			window.onresize = () => {
				this.throttle(this.init(), 300)
				this.handleClick(null, this.handleMoveDirection);
			}
		},
		computed: {
			inputStartLeftTime: {
				set(val) {
					val = val * 60 + this.toInt(document.getElementById('range-1').value)
					// if (val >= this.videoEdit.end || val < 0) {
					// 	val = 0
					// }
					this.videoEdit.start = val
				},
				get() {
					return this.timeTranslate(this.videoEdit.start).split(':')[0]
				}
			},
			inputStartRightTime: {
				set(val) {
					val = this.toInt(document.getElementById('range-0').value) * 60 + val
					// if (val >= this.videoEdit.end || val < 0) {
					// 	val = 0
					// }
					this.videoEdit.start = val
				},
				get() {
					return this.timeTranslate(this.videoEdit.start).split(':')[1]
				}
			},
			inputEndLeftTime: {
				set(val) {
					val = val * 60 + this.toInt(document.getElementById('range-3').value)
					// if (val <= this.videoEdit.start || val > this.videoEdit.duration) {
					// 	val = this.videoEdit.duration
					// }
					this.videoEdit.end = val
				},
				get() {
					return this.timeTranslate(this.videoEdit.end).split(':')[0]
				}
			},
			inputEndRightTime: {
				set(val) {
					val = this.toInt(document.getElementById('range-2').value) * 60 + val
					// if (val <= this.videoEdit.start || val > this.videoEdit.duration) {
					// 	val = this.videoEdit.duration
					// }
					this.videoEdit.end = val
				},
				get() {
					return this.timeTranslate(this.videoEdit.end).split(':')[1]
				}
			}
		},
		watch: {
			'videoEdit.start': {
				handler(val) {
					this.currentTime = val
					this.videoEdit.currentPosition =
						(this.currentTime / this.videoEdit.duration) * 100
					this.leftMovePercentage = this.videoEdit.currentPosition
					this.$emit('getTime', {
						start: val,
						end: this.videoEdit.end
					})
				},
				deep: true
			},
			'videoEdit.end': {
				handler(val) {
					this.currentTime = val
					this.videoEdit.currentPosition =
						(this.currentTime / this.videoEdit.duration) * 100
					this.rightMovePercentage = 100 - this.videoEdit.currentPosition
					this.$emit('getTime', {
						start: this.videoEdit.start,
						end: val
					})
				},
				deep: true
			}
		},
		methods: {
			onBlur(str){
				let val = Number(this[str]);
				switch(str){
					case 'inputStartLeftTime':
						val = val * 60 + this.toInt(document.getElementById('range-1').value)
						if (val >= this.videoEdit.end || val < 0) {
							val = 0
						}
						this.videoEdit.start = val
						break;
						
					case 'inputStartRightTime':
						val = this.toInt(document.getElementById('range-0').value) * 60 + val
						if (val >= this.videoEdit.end || val < 0) {
							val = 0
						}
						this.videoEdit.start = val
						break;
						
					case 'inputEndLeftTime':
						val = val * 60 + this.toInt(document.getElementById('range-3').value)
						if (val <= this.videoEdit.start || val > this.videoEdit.duration) {
							val = this.videoEdit.duration
						}
						this.videoEdit.end = val
						break;
						
					case 'inputEndRightTime':
						val = this.toInt(document.getElementById('range-2').value) * 60 + val
						if (val <= this.videoEdit.start || val > this.videoEdit.duration) {
							val = this.videoEdit.duration
						}
						this.videoEdit.end = val
						break;
				}
				
				// console.log(this.videoEdit.end, this.videoEdit.start);
			},
			throttle(fn, threshold, scope) {
				let timer
				return function() {
					const context = scope || this
					const args = arguments
					if (!timer) {
						timer = setTimeout(function() {
							fn.apply(context, args)
							timer = null
						}, threshold)
					}
				}
			},
			init() {
				// 初始化相关元数据
				this.videoDom = this.$refs['custom-video']
				this.leftIconDom = this.$refs['move-left-icon']
				this.rightIconDom = this.$refs['move-right-icon']
				this.thumbsWidth = this.$refs.thumbs.clientWidth
				this.leftMoveInit = this.getOffset(this.leftIconDom).left + 5
				this.rightMoveInit = this.getOffset(this.rightIconDom).left + 5
				this.minWidthPercentage = ((10 / this.thumbsWidth) * 100).toFixed(4) // 最小裁剪区域所占百分比
				this.thumbArr = [];
				this.initMedaData()
				
				// this.transformBlob()
				document.addEventListener('mouseup', ev => {
					this.handleMoveStatus = false
				})
				document.addEventListener('mousemove', ev => {
					if (!this.handleMoveStatus) return
					if (this.handleMoveDirection === 'left') {
						const distanceMoveXLeft = ev.clientX - this.leftMoveInit

						this.leftMovePercentage =
							distanceMoveXLeft > 0 ?
							((distanceMoveXLeft / (this.thumbsWidth - 20)) * 100).toFixed(4) :
							0

						// 控制裁剪百分比最小值
						if (
							this.leftMovePercentage >
							100 - this.rightMovePercentage - this.minWidthPercentage
						) {
							this.leftMovePercentage =
								100 - this.rightMovePercentage - this.minWidthPercentage
						}

						this.videoEdit.start = (
							(this.videoEdit.duration * this.leftMovePercentage) /
							100
						).toFixed(4)
					}
					if (this.handleMoveDirection === 'right') {
						const distanceMoveXRight = this.rightMoveInit - ev.clientX

						this.rightMovePercentage =
							distanceMoveXRight > 0 ?
							((distanceMoveXRight / (this.thumbsWidth - 20)) * 100).toFixed(
								4
							) :
							0

						// 控制裁剪百分比最小值
						if (
							this.rightMovePercentage >
							100 - this.leftMovePercentage - this.minWidthPercentage
						) {
							this.rightMovePercentage =
								100 - this.leftMovePercentage - this.minWidthPercentage
						}

						this.videoEdit.end = (
							this.videoEdit.duration *
							(1 - this.rightMovePercentage / 100)
						).toFixed(4)
					}
					this.handleClick(ev, this.handleMoveDirection)
				})
			},
			toInt(val) {
				return parseInt(val) || 0
			},
			togglePlayStatus() {
				// 播放暂停按钮事件
				this.videoEdit.play ? this.toggleVideoPause() : this.toggleVideoPlay()
			},
			toggleVideoPlay() {
				// 处理当前位置在末尾的时候先初始化开始播放时间
				if (this.videoEdit.end - this.currentTime < 0.01) {
					this.videoDom.currentTime = this.videoEdit.start
				}
				// 为了取消当前点平滑移动到开始点的过渡
				setTimeout(() => {
					this.videoDom.play()
					this.videoEdit.play = true
				}, 50)
			},
			toggleVideoPause() {
				this.videoDom.pause()
				this.videoEdit.play = false
			},
			playEnd() {
				this.videoDom.currentTime = this.videoEdit.start
				this.videoDom.pause()
				this.videoEdit.play = false
			},
			transformBlob() {
				const self = this
				const xhr = new XMLHttpRequest()
				xhr.open('GET', this.url, true)
				xhr.responseType = 'blob'
				xhr.onload = function(e) {
					if (this.status === 200) {
						// 获取blob对象
						const myBlob = this.response
						self.blobURL = URL.createObjectURL(myBlob)
					} else {
						this.$Notice.error({
							title: '读取失败',
							desc: '参数有误，读取视频失败!',
							duration: 5
						})
					}
				}
				xhr.send()
			},
			initMedaData() {
				// 初始化video相关事件
				this.videoDom.addEventListener('loadedmetadata', () => {
					// 获取视频总时长
					this.videoEdit.duration = this.videoDom.duration // 视频总时长
					this.videoEdit.end = this.videoEdit.duration
				})
				const self = this
				this.videoDom.addEventListener('canplay', () => {
					// 监听视频可播放时的状态
					self.videoRatio = this.videoHeight / this.videoWidth
					self.isPortraitVideo = self.videoRatio > 1.5 // 是否是竖向视频
					self.videoUnitWidth = self.isPortraitVideo ? 28 : 88 // 单个缩略图宽度
					self.thumbCount = Math.ceil(self.thumbsWidth / self.videoUnitWidth) // 缩略图个数
					self.videoUnit = self.videoEdit.duration / self.thumbCount
					// 缩略图
					if (self.thumbArr.length !== self.thumbCount) {
						self.thumbArr = []
						for (let i = 0; i < self.thumbCount; i++) {
							self.thumbArr.push({
								url: `${self.url}#t=${self.videoUnit * i}`,
								loading: true
							})
						}
					}
					const innerMoveLeft = Math.round(
						(self.thumbCount * self.videoUnitWidth - self.thumbsWidth) / 2
					)
					self.$nextTick(() => {
						self.$refs['thumbs-inner'].style.marginLeft = `${-innerMoveLeft}px`
					})
					
				})

				this.videoDom.addEventListener('timeupdate', () => {
					// 监听视频播放过程中的时间
					this.videoEdit.currentPosition =
						(this.videoDom.currentTime / this.videoEdit.duration) * 100
					this.currentTime = this.videoDom.currentTime
					if (
						this.videoEdit.end - this.currentTime < 0.01 &&
						this.videoEdit.play
					) {
						this.playEnd()
					}
				})
				
				this.loadingTap.close();
			},
			handleMoveDown(ev, direction) {
				this.handleMoveStatus = true
				this.handleMoveDirection = direction
				this.toggleVideoPause()
				this.handleClick(ev, direction)
			},
			handleClick(ev, direction) {
				// 区分各种情况是为了获取各种情况的当前播放点更准确
				if (direction === 'left') {
					this.videoEdit.currentPosition = this.leftMovePercentage
				} else if (direction === 'right') {
					this.videoEdit.currentPosition = 100 - this.rightMovePercentage
				} else {
					if(ev){
						// 点击中间剪辑区域
						this.videoEdit.currentPosition =
							((ev.clientX - this.leftMoveInit) / this.thumbsWidth) * 100
					}else{
						this.videoEdit.currentPosition = 0;
					}
				}
				this.currentTime = (
					(this.videoEdit.currentPosition * this.videoEdit.duration) /
					100
				).toFixed(4)
				
				this.videoDom.currentTime = this.currentTime
				// 处理两边 mask 点击而非滑动时,拖拽区域的处理
				if(ev){
					if (ev.target.dataset.direction === 'left' && !this.handleMoveStatus) {
						this.leftMovePercentage = this.videoEdit.currentPosition
						this.videoEdit.start = this.currentTime
					}
					if (ev.target.dataset.direction === 'right' && !this.handleMoveStatus) {
						this.rightMovePercentage = 100 - this.videoEdit.currentPosition
						this.videoEdit.end = this.currentTime
					}
				}
				// 百分比取最大值：100 最小值：0
				this.videoEdit.currentPosition =
					this.videoEdit.currentPosition > 100 ?
					100 :
					this.videoEdit.currentPosition
				this.videoEdit.currentPosition =
					this.videoEdit.currentPosition < 0 ? 0 : this.videoEdit.currentPosition
				
			},
			getOffset(node, offset) {
				// 获取当前屏幕下进度条的左偏移量和又偏移量
				if (!offset) {
					offset = {}
					offset.left = 0
					offset.top = 0
				}
				if (node === document.body || node === null) {
					return offset
				}
				offset.top += node.offsetTop
				offset.left += node.offsetLeft
				return this.getOffset(node.offsetParent, offset)
			},
			timeTranslate(t) {
				// 时间转化
				let m = Math.floor(t / 60)
				m < 10 && (m = '0' + m)
				return m + ':' + ((t % 60) / 100).toFixed(2).slice(-2)
			}
		}
	}
</script>
<style scoped lang="scss">
	.custom-video {
		user-select: none;
		margin: 0 auto;
		// min-height: 100%;
		padding: 20px;
		border-radius: 20px;
		min-width: 500px;
		
		.custom-video_video{
			text-align: center;
		}

		&_container {
			height: 290px;
			margin: 0 auto;
			position: relative;
			overflow: hidden;
		}

		&_video {
			height: 100%;
			
			video {
				height: 100%;
			}
		}
	}

	.thumbs {
		white-space: nowrap;
		overflow: hidden;
		height: 50px;
		background: #ffc7d1;

		.inner {
			height: 50px;
			display: flex;

			&-item {
				position: relative;
			}

			&-loading {
				position: absolute;
				top: 0;
				left: 0;
				height: 100%;
				width: 100%;
			}
		}

		video {
			object-fit: cover;
		}
	}

	.video-controls {
		position: relative;
		margin: 50px 0 15px;
		overflow: hidden;
	}

	.control-bars {
		position: absolute;
		right: 0;
		bottom: 0;
		top: 0;
		left: 0;

		&-wrapper {
			position: absolute;
			right: 0;
			bottom: 0;
			top: 0;
			left: 0;
			border: 1px solid #04f0fb;
			z-index: 10;
			min-width: 10px;
		}

		&-mask {
			position: absolute;
			top: 0;
			bottom: 0;
			background-color: rgba(25, 30, 64, 0.8);

			&.left {
				left: 0;
			}

			&.right {
				right: 0;
			}
		}

		&-progress {
			position: absolute;
			height: 80px;
			top: -15px;
			width: 1px;
			background: #ffffff;
			border-radius: 100px;

			&.animate {
				transition: all 0.3s;
			}

			svg {
				position: absolute;
				top: -10px;
				left: -26px;
			}

			.text {
				position: absolute;
				width: 54px;
				height: 18px;
				top: -10px;
				left: -26px;
				font-size: 12px;
				line-height: 18px;
			}
		}

		&-move {
			position: absolute;
			width: 10px;
			height: 10px;
			background: #04f0fb;
			top: 50%;
			transform: translateY(-50%);
			border-radius: 50%;
			cursor: ew-resize;
			z-index: 2;

			&:active {
				background: #fff;
			}

			&.left {
				left: -5px;
			}

			&.right {
				right: -5px;
			}
		}
	}

	.video-btn {
		display: flex;
		align-items: center;
		justify-content: space-between;

		.toggle {
			width: 70px;
			height: 30px;
			line-height: 30px;
			border-radius: 30px;
			background-color: rgba(0, 0, 0, 0.3);
			position: relative;
			transition: background-color 0.2s;
			cursor: pointer;

			&-icon {
				position: absolute;
				display: inline-block;
				top: 50%;
				left: 50%;
				transform: translate(-50%, -50%);
				width: 0;
				height: 0;
				border-top: 8px solid transparent;
				border-bottom: 8px solid transparent;
				border-left: 12px solid #fff;
				opacity: 1;
				transition: all 0.2s;

				&.playing {
					width: 12px;
					height: 15px;
					border-left: solid 4px #fff;
					border-right: solid 4px #fff;
					border-top: solid 0 transparent;
					border-bottom: solid 0 transparent;
					box-sizing: border-box;
				}
			}
		}

		.crop {
			&-range {
				display: flex;
				align-items: center;
				font-size: 14px;
			}

			&-input {
				height: 30px;
				border-radius: 15px;
				overflow: hidden;
				padding: 0 14px;
				display: flex;
				align-items: center;

				input {
					float: left;
					width: 50px;
					border: 1px solid #ddd;
					font-size: 14px;
					border-radius: 20px;
					text-align: center;
				}

				span {
					width: 8px;
					line-height: 28px;
					height: 28px;
					float: left;
					margin: 0;
					padding: 0;
				}
			}
		}
	}

	.loading {
		width: 100%;
		height: 100%;
		position: relative;
		display: flex;
		align-items: center;
		justify-content: center;

		&-con {
			position: absolute;
			animation: loading 1s linear infinite;
			width: 25px;
			height: 25px;
			border-radius: 50%;
			box-shadow: 0 2px 0 0 #fff; // ffc7d1
			transform-origin: 50% 50%;
		}
	}

	@keyframes loading {
		0% {
			transform: rotate(0deg);
		}

		50% {
			transform: rotate(180deg);
		}

		100% {
			transform: rotate(360deg);
		}
	}
	
	.text-right{
		text-align: right;
	}
	
	.text-left{
		text-align: left;
	}
</style>
