Vue 비디오 미디어 다 중 재단 구성 요소 의 구현 예시
인터넷 에서 간단하게 찾 아 보 니 기본적으로 클 라 이언 트 안의 도구 이 고 순수한 웹 페이지 의 재단 이 없다.없 으 니 손 을 써 라.
코드 가 GitHub 에 업로드 되 었 습 니 다:https://github.com/fengma1992/media-cut-tool
쓸데없는 말 이 많 지 않 으 니,다음은 어떻게 설계 하 는 지 보 자.
효과 도
그림 의 아래쪽 기능 블록 은 재단 도구 구성 요소 이 고 위의 영상 은 프레젠테이션 용 이 며 물론 오디 오 일 수도 있 습 니 다.
기능 특징:
사고의 방향
전체적으로 보면 하나의 데이터 배열
cropItemList
을 통 해 사용자 의 입력 데 이 터 를 저장 하고 마우스 드래그 든 키보드 입력 이 든 모두 조작cropItemList
을 통 해 양쪽 데이터 연동 을 실현 한다.마지막 으로 사용자 가 원 하 는 재단 을 처리cropItemList
를 통 해 출력 합 니 다.cropItemList
구 조 는 다음 과 같다.
cropItemList: [
{
startTime: 0, //
endTime: 100, //
startTimeArr: [hoursStr, minutesStr, secondsStr], //
endTimeArr: [hoursStr, minutesStr, secondsStr], //
startTimeIndicatorOffsetX: 0, // X
endTimeIndicatorOffsetX: 100, // X
}
]
첫걸음다 중 재단 인 만큼 사용 자 는 어떤 시간 대 를 재단 하 였 는 지 알 아야 합 니 다.이것 은 오른쪽 재단 목록 을 통 해 보 여 줍 니 다.
리스트
목록 에 세 개의 상태 가 존재 합 니 다:
데이터 없 음 상태
데이터 가 없 을 때 내용 이 비어 있 습 니 다.사용자 가 입력 상 자 를 클릭 할 때 자발적으로 데 이 터 를 만 듭 니 다.기본 값 은 비디오 길이 의 1/4 에서 3/4 입 니 다.
데이터 가 하나 있어 요.
이 때 화면 은 매우 간단 하고 유일한 데 이 터 를 보 여 줍 니 다.
여러 개의 데이터 가 있다
여러 개의 데이터 가 있 을 때 추가 로 처리 해 야 합 니 다.첫 번 째 데이터 가 맨 아래 에 있 기 때문에
v-for
로 순환cropItemList
하면 다음 그림 의 상황 이 나타 납 니 다.그리고 첫 번 째 오른쪽 은 버튼 을 추가 하고 나머지 오른쪽 은 삭제 버튼 이다.그래서 우 리 는 1 조 를 따로 제시 하여 쓴 다음 에
cropItemList
역 서 를 하나의renderList
생 성하 고 순환renderList
하 는0 -> listLength - 2
조 를 생 성 한다.됐다.
<template v-for="(item, index) in renderList">
<div v-if="index < listLength -1"
:key="index"
class="crop-time-item">
...
...
</div>
</template>
다음 그림 은 최종 효과:시 분 초 입력
이것 은 바로 세 개의
input
상 자 를 쓰 고type="text"
type=number
입력 상자 오른쪽 에 상하 화살표 가 있 을 것 이 라 고 설정 한 다음 에 input 이 벤트 를 감청 하여 입력 의 정확성 을 확보 하고 데 이 터 를 업데이트 하 는 것 이다.focus 사건 을 감청 하여cropItemList
이 비어 있 을 때 자발적으로 데 이 터 를 추가 해 야 하 는 지 확인 합 니 다.
<div class="time-input">
<input type="text"
:value="renderList[listLength -1]
&& renderList[listLength -1].startTimeArr[0]"
@input="startTimeChange($event, 0, 0)"
@focus="inputFocus()"/>
:
<input type="text"
:value="renderList[listLength -1]
&& renderList[listLength -1].startTimeArr[1]"
@input="startTimeChange($event, 0, 1)"
@focus="inputFocus()"/>
:
<input type="text"
:value="renderList[listLength -1]
&& renderList[listLength -1].startTimeArr[2]"
@input="startTimeChange($event, 0, 2)"
@focus="inputFocus()"/>
</div>
세 션 재생재생 단 추 를 누 르 면
playingItem
을 통 해 현재 재생 되 고 있 는 세 션 을 기록 한 다음 상부 에play
이 벤트 를 보 내 고 재생 시작 시간 을 가 져 옵 니 다.마찬가지 로pause
과stop
사건 도 있어 언론 의 일시 정지 와 정 지 를 통제 했다.
<CropTool :duration="duration"
:playing="playing"
:currentPlayingTime="currentTime"
@play="playVideo"
@pause="pauseVideo"
@stop="stopVideo"/>
/**
*
* @param index
*/
playSelectedClip: function (index) {
if (!this.listLength) {
console.log(' ')
return
}
this.playingItem = this.cropItemList[index]
this.playingIndex = index
this.isCropping = false
this.$emit('play', this.playingItem.startTime || 0)
}
여기 서 재생 을 시작 하 는 것 을 제어 합 니 다.그러면 어떻게 미디어 를 재단 이 끝 날 때 까지 자동 으로 멈 추 게 합 니까?언론 의
timeupdate
사건 을 감청 하고 실시 간 으로 언론 의currentTime
과playingItem
의endTime
을 비교 하여 도달 할 때pause
사건 을 언론 에 알 리 고 중단 했다.
if (currentTime >= playingItem.endTime) {
this.pause()
}
이로써 키보드 입력 의 재단 목록 이 기본적으로 완성 되 었 으 며 마우스 드래그 입력 을 소개 합 니 다.두 번 째 단계
마우스 클릭 과 드래그 를 통 해 입력 하 는 방법 을 소개 한다.
1.마우스 상호작용 논리 확인
새로 추 가 된 재단
마 우 스 를 드래그 구역 에서 클릭 한 후 재단 데 이 터 를 추가 합 니 다.시작 시간 과 종료 시간 이 모두
mouseup
일 때 진도 바 의 시간 이 고 종료 시간 스탬프 가 마 우 스 를 따라 이동 하여 편집 상태 에 들 어 갑 니 다.타임 스탬프 확인
상 태 를 편집 하고 마우스 가 이동 할 때 시간 스탬프 는 마우스 가 진행 바 의 현재 위치 에 따라 움 직 입 니 다.마 우 스 를 다시 클릭 한 후 현재 시간 을 확인 하고 시간 스탬프 를 종료 하고 마 우 스 를 따라 이동 합 니 다.
시간 변경
편집 되 지 않 은 상태 에서 마우스 가 진행 막대 에서 이동 할 때 감청
mousemove
사건 은 임의의 재단 데이터 의 시작 이나 끝 시간 스탬프 에 가 까 울 때 현재 데 이 터 를 밝 히 고 시간 스탬프 를 표시 합 니 다.마우스mousedown
를 누 른 후 시간 표를 선택 하고 시간 변경 데 이 터 를 끌 기 시작 합 니 다.mouseup
이후 변경 사항 을 종료 합 니 다.2.감청 할 마우스 이벤트 확인
마 우 스 는 진도 구역 에서 세 가지 사건 을 감청 해 야 한다.
mousedown
,mousemove
,mouseup
.진도 구역 에 여러 가지 요소 가 존재 합 니 다.간단하게 세 가지 로 나 눌 수 있 습 니 다.mousedown
과mouseup
의 감청 은 당연히 진도 조 자체 에 귀속 된다.
this.timeLineContainer.addEventListener('mousedown', e => {
const currentCursorOffsetX = e.clientX - containerLeft
lastMouseDownOffsetX = currentCursorOffsetX
//
this.timeIndicatorCheck(currentCursorOffsetX, 'mousedown')
})
this.timeLineContainer.addEventListener('mouseup', e => {
// , ,
if (this.isCropping) {
this.stopCropping()
return
}
const currentCursorOffsetX = this.getFormattedOffsetX(e.clientX - containerLeft)
// mousedown mouseup , ,
if (Math.abs(currentCursorOffsetX - lastMouseDownOffsetX) > 3) {
return
}
//
this.currentCursorTime = currentCursorOffsetX * this.timeToPixelRatio
//
if (!this.isCropping) {
this.addNewCropItemInSlider()
//
this.startCropping(this.cropItemList.length - 1)
}
})
mousemove
이것 은 편집 상태 가 아 닐 때 당연히 감청 진도 조 를 통 해 시간 스탬프 가 마 우 스 를 움 직 이 는 것 을 실현 한다.그리고 시작 이나 끝 시간 스탬프 를 선택 하여 편집 상태 에 들 어가 야 할 때 저 는 처음에 시간 스탬프 자 체 를 감청 하여 선택 한 시간 스탬프 의 목적 을 달성 하려 고 생각 했 습 니 다.실제 상황 은 마우스 가 시작 이나 끝 시간 스탬프 에 가 까 울 때 마우스 가 움 직 이 는 시간 스탬프 가 앞 에 가 려 져 있 고 재단 세 션 이론 적 으로 무한 증가 할 수 있 기 때문에 저 는 2*재단 세 션 개mousemove
를 감청 해 야 합 니 다.이 를 바탕 으로 진도 항목 자체 감청
mousemove
에서 만 실시 간 으로 마우스 위치 와 시간 스탬프 위 치 를 비교 하여 해당 위치 에 도 착 했 는 지 확인 하려 면 당연히 하나throttle
를 추가 하여 절 류 를 해 야 한다.
this.timeLineContainer.addEventListener('mousemove', e => {
throttle(() => {
const currentCursorOffsetX = e.clientX - containerLeft
// mousemove
if (currentCursorOffsetX < 0 || currentCursorOffsetX > containerWidth) {
this.isCursorIn = false
// mouseup
if (this.isCropping) {
this.stopCropping()
this.timeIndicatorCheck(currentCursorOffsetX < 0 ? 0 : containerWidth, 'mouseup')
}
return
}
else {
this.isCursorIn = true
}
this.currentCursorTime = currentCursorOffsetX * this.timeToPixelRatio
this.currentCursorOffsetX = currentCursorOffsetX
//
this.timeIndicatorCheck(currentCursorOffsetX, 'mousemove')
//
this.timeIndicatorMove(currentCursorOffsetX)
}, 10, true)()
})
3.드래그 와 시간 스탬프 수 동 실현다음은 타임 스탬프 검사 와 타임 스탬프 이동 검사 코드 입 니 다.
timeIndicatorCheck (currentCursorOffsetX, mouseEvent) {
// ,
if (this.isCropping) {
return
}
// , hover
this.startTimeIndicatorHoverIndex = -1
this.endTimeIndicatorHoverIndex = -1
this.startTimeIndicatorDraggingIndex = -1
this.endTimeIndicatorDraggingIndex = -1
this.cropItemHoverIndex = -1
this.cropItemList.forEach((item, index) => {
if (currentCursorOffsetX >= item.startTimeIndicatorOffsetX
&& currentCursorOffsetX <= item.endTimeIndicatorOffsetX) {
this.cropItemHoverIndex = index
}
//
if (isCursorClose(item.endTimeIndicatorOffsetX, currentCursorOffsetX)) {
this.endTimeIndicatorHoverIndex = index
// ,
if (mouseEvent === 'mousedown') {
this.endTimeIndicatorDraggingIndex = index
this.currentEditingIndex = index
this.isCropping = true
}
}
else if (isCursorClose(item.startTimeIndicatorOffsetX, currentCursorOffsetX)) {
this.startTimeIndicatorHoverIndex = index
// ,
if (mouseEvent === 'mousedown') {
this.startTimeIndicatorDraggingIndex = index
this.currentEditingIndex = index
this.isCropping = true
}
}
})
},
timeIndicatorMove (currentCursorOffsetX) {
// ,
if (this.isCropping) {
const currentEditingIndex = this.currentEditingIndex
const startTimeIndicatorDraggingIndex = this.startTimeIndicatorDraggingIndex
const endTimeIndicatorDraggingIndex = this.endTimeIndicatorDraggingIndex
const currentCursorTime = this.currentCursorTime
let currentItem = this.cropItemList[currentEditingIndex]
//
if (startTimeIndicatorDraggingIndex > -1 && currentItem) {
//
if (currentCursorOffsetX > currentItem.endTimeIndicatorOffsetX) {
return
}
currentItem.startTimeIndicatorOffsetX = currentCursorOffsetX
currentItem.startTime = currentCursorTime
}
//
if (endTimeIndicatorDraggingIndex > -1 && currentItem) {
//
if (currentCursorOffsetX < currentItem.startTimeIndicatorOffsetX) {
return
}
currentItem.endTimeIndicatorOffsetX = currentCursorOffsetX
currentItem.endTime = currentCursorTime
}
this.updateCropItem(currentItem, currentEditingIndex)
}
}
세 번 째 단계재단 이 끝 난 후 다음 단 계 는 당연히 데 이 터 를 백 엔 드 에 버 리 는 것 이다.
사용 자 를 sweet감자:(\#고구마\#)
사용자 가 사용 할 때 작은 손 을 떨 고
단 추 를 한 번 더 누 르 거나 파 킨 슨 이 있어 서 아무리 해도 지연 되 지 않 으 면 데이터 가 같 거나 겹 치 는 부분 이 있 을 수 있 습 니 다.그럼 중복 과 중복 부분 이 있 는 재단 을 걸 러 내야 합 니 다.그냥 코드 를 보 는 게 편 해 요.
/**
* cropItemList
*/
cleanCropItemList () {
let cropItemList = this.cropItemList
// 1. startTime
cropItemList = cropItemList.sort(function (item1, item2) {
return item1.startTime - item2.startTime
})
let tempCropItemList = []
let startTime = cropItemList[0].startTime
let endTime = cropItemList[0].endTime
const lastIndex = cropItemList.length - 1
// ,
cropItemList.forEach((item, index) => {
// ,
if (lastIndex === index) {
tempCropItemList.push({
startTime: startTime,
endTime: endTime,
startTimeArr: formatTime.getFormatTimeArr(startTime),
endTimeArr: formatTime.getFormatTimeArr(endTime),
})
return
}
// currentItem item
if (item.endTime <= endTime && item.startTime >= startTime) {
return
}
// currentItem item
if (item.startTime <= endTime && item.endTime >= endTime) {
endTime = item.endTime
return
}
// currentItem item , ,
if (item.startTime > endTime) {
tempCropItemList.push({
startTime: startTime,
endTime: endTime,
startTimeArr: formatTime.getFormatTimeArr(startTime),
endTimeArr: formatTime.getFormatTimeArr(endTime),
})
// item
startTime = item.startTime
endTime = item.endTime
}
})
return tempCropItemList
}
네 번 째 단계재단 도구 사용:props 및 emit 이 벤트 를 통 해 미디어 와 재단 도구 간 의 통신 을 실현 합 니 다.
<template>
<div id="app">
<video ref="video" src="https://pan.prprpr.me/?/dplayer/hikarunara.mp4"
controls
width="600px">
</video>
<CropTool :duration="duration"
:playing="playing"
:currentPlayingTime="currentTime"
@play="playVideo"
@pause="pauseVideo"
@stop="stopVideo"/>
</div>
</template>
<script>
import CropTool from './components/CropTool.vue'
export default {
name: 'app',
components: {
CropTool,
},
data () {
return {
duration: 0,
playing: false,
currentTime: 0,
}
},
mounted () {
const videoElement = this.$refs.video
videoElement.ondurationchange = () => {
this.duration = videoElement.duration
}
videoElement.onplaying = () => {
this.playing = true
}
videoElement.onpause = () => {
this.playing = false
}
videoElement.ontimeupdate = () => {
this.currentTime = videoElement.currentTime
}
},
methods: {
seekVideo (seekTime) {
this.$refs.video.currentTime = seekTime
},
playVideo (time) {
this.seekVideo(time)
this.$refs.video.play()
},
pauseVideo () {
this.$refs.video.pause()
},
stopVideo () {
this.$refs.video.pause()
this.$refs.video.currentTime = 0
},
},
}
</script>
총결산코드 를 쓰 는 것 보다 블 로 그 를 쓰 는 것 이 훨씬 어려워 서 혼 란 스 럽 게 블 로 그 를 다 썼 다.
몇 개의 작은 디 테 일 목록 을 삭제 할 때의 고도 애니메이션
UI 는 최대 10 개의 재단 세 션 을 보 여 주 며 초과 하면 스크롤 하고 애니메이션 도 추가 삭제 해 야 한다.직접 설치
max-height
끝 날 줄 알 았 는데CSS
transition
애니메이션 은 절대 값 의 height 만 유효 하기 때문에 조금 번 거 롭 습 니 다.줄 을 자 르 는 수량 이 변화 하고 높이 도 변화 하기 때 문 입 니 다.절대 치 를 설정 하면 어 떡 하지...여기 서 HTML 에 있 는 tag 의
attribute
속성data-count
을 통 해 CSS 에 게 제 가 현재 몇 개의 재단 이 있 는 지 알려 주 고 CSS 가data-count
에 따라 목록 높이 를 설정 하도록 합 니 다.
<!-- 10 10, -->
<div
class="crop-time-body"
:data-count="listLength > 10 ? 10 : listLength -1">
</div>
.crop-time-body {
overflow-y: auto;
overflow-x: hidden;
transition: height .5s;
&[data-count="0"] {
height: 0;
}
&[data-count="1"] {
height: 40px;
}
&[data-count="2"] {
height: 80px;
}
...
...
&[data-count="10"] {
height: 380px;
}
}
mousemove
시 사건 의currentTarget
문제DOM 이벤트 의 캡 처 와 거품 이 존재 하기 때문에 진도 막대 위 에 시간 스탬프,재단 세 션 등 요소 가 있 을 수 있 습 니 다.
mousemove
사건 의currentTarget
이 변 할 수 있 고 마우스 거리 진도 막대 의 맨 왼쪽offsetX
에 문제 가 있 을 수 있 습 니 다.만약 에 검 측currentTarget
을 통 해 진도 조 인지 에 도 문제 가 존재 한다.마우스 가 이동 할 때 시간 이 계속 움 직 이기 때문에 가끔 진도 조 에 대응 하 는mousemove
사건 을 촉발 하지 못 한다.해결 방법 은 페이지 로 딩 이 완 료 된 후에 진도 조 의 가장 왼쪽 에서 페이지 의 가장 왼쪽 거 리 를 얻 는 것 이다.
mousemove
사건 은 취하 지 않 고offsetX
페이지 의 가장 왼쪽clientX
을 바탕 으로 하 는 것 이다.그 다음 에 이들 이 감소 하면 마우스 거리 진도 조 의 가장 왼쪽 픽 셀 값 을 얻 을 수 있다.코드 는 위의 추가mousemove
감청 에 쓰 여 있 습 니 다.시간 포맷
재단 도 구 는 초 를
00:00:00
형식의 문자열 로 변환 해 야 하 는 곳 이 많 기 때문에 도구 함 수 를 썼 습 니 다.입력 초,출력dd,HH,mm,ss
4 개key
를 포함 하 는Object
,각각key
은 길이 가 2 인 문자열 입 니 다.ES8 의String.prototype.padStart()방법 으로 실현 하 다.
export default function (seconds) {
const date = new Date(seconds * 1000);
return {
days: String(date.getUTCDate() - 1).padStart(2, '0'),
hours: String(date.getUTCHours()).padStart(2, '0'),
minutes: String(date.getUTCMinutes()).padStart(2, '0'),
seconds: String(date.getUTCSeconds()).padStart(2, '0')
};
}
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Vue Render 함수로 DOM 노드 코드 인스턴스 만들기render에서createElement 함수를 사용하여 DOM 노드를 만드는 것은 직관적이지 않지만 일부 독립 구성 요소의 디자인에서 특수한 수요를 충족시킬 수 있습니다.간단한 렌더링 예는 다음과 같습니다. 또한 v...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.