视频剪辑类工具常常需要这个样一个组件,如何用vue实现一个?
实现 resize
上面每一个小块我们称之为item
,item
左右各有一个holder
,item 在其父容器中是绝对定位(position: absolute)。
<div class="item"
:style="{width: boundingRect.width + 'px', left: boundingRect.x + 'px' }">
<div class="holder" @mousedown.stop="start($event, 0)"></div>
<div class="box">
</div>
<div class="holder" @mousedown.stop="start($event, 1)"></div>
</div>
思路
捋一下思路,resize 分两种情况,拖左侧的holder,和拖右侧的holder。
拖右侧的holder:鼠标按下时记录一下当前item的宽高和位置信息,拖拽时改变item
的width
。
拖左侧的holder:在拖右侧的holder逻辑的基础上,再加一个逻辑,不断地改变item
的left
。
注意点
看上面的代码,并没有在 holder 上加 @mousemove 事件,如果依赖鼠标在 holder 上的 mousemove 事件,操作会很不流畅。因为鼠标必须先在holder上移动,才会有事件触发,item 的改变总是延迟一点;最要命的是如果鼠标移动稍微快一点,holder 没跟上,那后续事件就触发不了了。
所以应该把 mousemove 事件绑在 document 上,并在松开鼠标时解绑事件:
/**
* 启动 resize
* @param e 事件
* @param holder 0 左边手柄; 1: 右边手柄
*/
function start(e, holder){
document.addEventListener('mousemove', resize)
document.addEventListener('mouseup', ()=>{
document.removeEventListener('mousemove', resize)
},{once: true})
// 记录操作前item位置信息
snapshot.cursorX = e.pageX // 鼠标位置
snapshot.x = boundingRect.value.x
snapshot.width = boundingRect.value.width
snapshot.holder = holder
}
function resize(e) {
// 鼠标位移
const delta = e.pageX - snapshot.cursorX
if (0 === snapshot.holder) {
// 拉左侧手柄
let newWidth = snapshot.width - delta
newWidth = Math.max(newWidth, boundingRect.value.minWidth)
model.value.duration = newWidth / timeline.scale
const newX = snapshot.x + snapshot.width - newWidth
model.value.start = newX / timeline.scale
} else if (1 === snapshot.holder) {
// 拉右侧手柄
let newWidth = snapshot.width + delta
newWidth = Math.max(newWidth, boundingRect.value.minWidth)
model.value.duration = newWidth / timeline.scale
}
}
碰撞检测
拖动holder往外拉,把item变宽的时候需要做一下碰撞检测,即拉左边的 holder 时,保证不能与前面一个 item 重叠;拉右边的 holder 时,保证不与后面一个item 重叠;拉第一个 item 的左侧 holder 时,left 不能小于 0。
后续
- 最顶部的时间标尺,用canvas实现,以提高性能
- Ctrl + 鼠标滚轮缩放比例尺