折腾两天也做出一个codepen的大概模型,目前只兼容了chrome。前端代码用的react作为ui库
分析codepen
首先要分析codepen的编辑器界面
- 红色圈起来的地方是由codemirror完成的,只要看源码就知道了
- 蓝色标注的两个拖拽bar,可以修改窗口大小
这两条里 codemirror实现的只要引入就行了,主要难点就在如何做出一个可以任意调整分块大小的编辑器容器。
设计模型
由codepen设计可以看出,每个拖拽的bar可以影响两边的窗口,当其中一边窗口大小不够的时候,会影响那一边相邻的窗口。
假设有3个窗口 A,B,C 拖拽bar有 AB和BC。
- 拖拽AB向右侧移动的时候,A窗口会增加宽度,B窗口减小宽度,当B窗口为0的时候,C窗口会相应减少宽度,返回过来BC向左也是一样的。
推广到n个窗口的时候,情况是类似的。
- 这里将拖拽的距离设置为消费的能量cost
- 拖拽bar其中一侧会增加cost的宽度,另外一侧会由一个或者多个窗口消费cost。
- 如果另一侧不能完全消费cost,那么增加宽度那侧 也不能增加全部cost
按照上面的约束条件,我们就可以设计出拖拽代码
/***
* widths 是一个数组 表示所有窗口的对应宽度
* index 表示当前拖拽bar的坐标,由1开始到widths.length-1;
* cost 是一次拖拽距离
**/
function dragResize(widths, index, cost) {
if (cost > 0) { // 正向拖拽
let noCost = cost;// 记录有多少距离没有消费
for (let i = index; i < widths.length; i++) {
noCost = calcuCost(widths, i, noCost);
if (noCost == 0) break;
}
//另一侧窗口的增加距离为 总消耗 - 未消耗
widths[index - 1] += cost - noCost;
} else if (cost < 0) { // 反向拖拽
cost = -cost;
let noCost = cost;// 记录有多少距离没有消费
for (let i = index - 1; i > -1; i--) {
noCost = calcuCost(widths, i, noCost);
if (noCost == 0) break;
}
//另一侧窗口的增加距离为 总消耗 - 未消耗
widths[index] += cost - noCost;
}
}
/**
* 计算消耗 并返回剩余未消耗
*/
function calcuCost(widths, index, noCost) {
let result = widths[index] - noCost;
if (result <= 0) {
// 记录窗口的消费 如果result<=0表示这个窗口不能消耗完全
noCost = -result; // 剩余多少没有被消费
widths[index] = 0;// 由于消耗不完 所以它的最后宽度变为0
return noCost;
} else {
//记录窗口的消费 如果result>0 表示到这个窗口已经完全消费了拖拽距离
widths[i] = result;// 窗口设置为剩余的宽度
return 0;
}
}
其实上下拖拽也是同理的,只要把widths换成height就可以了。
浏览器拖拽工具类
正常拖拽设计流程就是 mousedown,mousemove,mouseup。mousedown的时候开始监听mousemove,mouseup的时候停止监听。
- 如果监听元素mousemove 鼠标移出元素之后就会产生事件中断的问题
所以这里mousemove和mouseup都监听的document的事件。 - 监听document的mousemove 鼠标移出到浏览器窗口外 ,也会产生事件中断的问题。
所以可以采用captureEvents。只要调用了captureEvents 无论鼠标是否在窗口内都可以监听到。
拖拽所关心的事情
- 每次移动 鼠标当前位置与上次位置的距离变化 dx,dy
- 每次移动 从鼠标按下到现在总位置变化 px, py
- 何时停止移动
/**
* 拖拽中回调函数
* dx 鼠标在x轴的变化距离
* dy 鼠标在y轴的变化距离
* px 鼠标从开始移动 到现在的总x轴变化距离
* py 鼠标从开始移动 到现在的总y轴变化距离
**/
function onDrag(dx,dy,px,py) {
}
/**
* 拖拽结束回调函数
* dx 鼠标在x轴的变化距离
* dy 鼠标在y轴的变化距离
* px 鼠标从开始移动 到现在的总x轴变化距离
* py 鼠标从开始移动 到现在的总y轴变化距离
**/
function onDragEnd(dx,dy,px,py) {
}
/**
* 拖拽函数 调用时机需要在鼠标按下时候
* startX 起始鼠标x轴坐标
* startY 起始鼠标y轴坐标
* onDrag 拖拽中监听函数
* onDragEnd 拖拽结束监听函数
*/
function drag(startX, startY, onDrag, onDragEnd) {
let prevX = startX;
let prevY = startY;
function mouseMove(e) {
onDrag(e.pageX - prevX, e.pageY - prevY, e.pageX - startX, e.pageY - startY);
prevX = e.pageX;
prevY = e.pageY;
}
function mouseUp(e) {
onDragEnd(e.pageX - prevX, e.pageY - prevY, e.pageX - startX, e.pageY - startY);
document.releaseEvents(Event.MOUSEMOVE | Event.MOUSEUP);
document.removeEventListener('mousemove', mouseMove);
document.removeEventListener('mouseup', mouseUp);
}
document.captureEvents(Event.MOUSEMOVE | Event.MOUSEUP);
document.addEventListener('mousemove', mouseMove);
document.addEventListener('mouseup', mouseUp);
}
效果图
这里贴一张效果图。