非编辑状态,鼠标在进度条上移动时,监听 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_potato:(#红薯#)