本篇文章主要讲怎么一块拖动多个小方块的, 和实现一个自定义的drag
drag
基础代码效果图:
code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#container {
width: 500px;
margin: 50px auto 0;
}
</style>
</head>
<body>
<div id="container">
</div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const svg = d3.select('#container')
.append('svg')
.attr('width', 500)
.attr('height', 500);
const data = [
{ id: 1, fill: 'black', x: 10, y: 10 },
{ id: 2, fill: 'black', x: 50, y: 50 },
{ id: 3, fill: 'black', x: 100, y: 70 },
{ id: 4, fill: 'black', x: 20, y: 100 }
];
draw(); // 绘制
bindDrag(); //绑定drag
function draw() {
const update = svg.selectAll('rect')
.data(data, d => d.id);
//修改层
update.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
//渲染层
const enter = update.enter();
//删除层
const exit = update.exit();
enter.append('rect')
.attr('width', 20)
.attr('height', 20)
.attr('id', d => `rect-${d.id}`) //不能绑定数字咱们给拼接一个字符串 error: Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#4' is not a valid selector.
.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
.attr('stroke', 'blue')
.attr('strokeWidth', 1)
exit.remove()
}
function bindDrag() { // 绑定drag
svg.selectAll('rect').call(drag()); // 使用.call 给每个rect绑定drag事件
}
function drag() {
return d3.drag()
.on('start', dragStarted) // 开始
.on('drag', dragged) // 执行中
.on('end', dragEnded); // 结束
};
let currentMoveElement = null;
function dragStarted(d) { // drag start
console.log(d, 'start log....'); // 获取到的是当前拖拽的item 就是之前绑定的值。如:{id: 1, fill: 'black', x: 10, y: 10}
currentMoveElement = d3.select(`#rect-${d.id}`); // 🎈优化点: 提前获取到元素 不用再drag的时候一直用d3.select获取的
};
function dragged(d) { // drag
console.log(d, 'drag log....');
const currentX = d3.event.x; // 使用d3.event 能获取到当前拖拽的x、y
const currentY = d3.event.y;
if (currentMoveElement) {
currentMoveElement.attr('x', currentX) // 一直修改其位置
.attr('y', currentY);
}
};
function dragEnded(d) { // drag end
console.log(d, 'end log....')
const currentX = d3.event.x; // 使用d3.event 能获取到当前拖拽的x、y
const currentY = d3.event.y;
d.x = currentX; // 再最后更改下绑定的数据,不然就会再下次drag的时候还是获取到之前的坐标,会发现尼拖拽节点跑到初始位置了
d.y = currentY; // 如果有疑问可以注释掉这个函数的代码尝试下
//对应的data原始数据也要更新,假如你有个保存的场景 那尼获取到的坐标就不是最新的了
data.forEach((item) => {
if (item.id === d.id) {
item.x = currentX;
item.y = currentY;
}
})
console.log(data, 'data')
};
</script>
怎么实现选中多个小方块一块拖动?
实现效果图:
新增点击事件部分代码
const selected = [];
bindEvent();
function bindEvent() { // 绑定点击事件(看不懂的去前面d3event事件绑定去看看)
svg.selectAll('rect').on('click', (d) => {
const index = selected.indexOf(d.id);
if (index !== -1) {
selected.splice(index, 1);
} else {
selected.push(d.id);
}
updateColor();
});
};
function updateColor() { // 更新color
data.forEach((item) => {
if (selected.includes(item.id)) {
item.fill = 'red';
}
});
draw(); // 重新绘制,更新需要高亮的点(d3数据绑定函数,不理解怎么更新的可以去看看d3数据绑定篇幅)
}
改动drag code
function bindDrag() { // 绑定drag
svg.selectAll('rect').call(drag()); // 使用.call 给每个rect绑定drag事件
}
function drag() {
return d3.drag()
.on('start', dragStarted) // 开始
.on('drag', dragged) // 执行中
.on('end', dragEnded); // 结束
};
let diffX = 0; diffY = 0;
// 🎈优化点: 提前获取到元素 不用再drag的时候一直用d3.select获取的
let currentMoveElement = null; // 这个数据要变成数组的形式了存放多个[selectDom、selectDom...]
function dragStarted(d) { // drag start
console.log(d, 'start log....'); // 获取到的是当前拖拽的item 就是之前绑定的值。如:{id: 1, fill: 'black', x: 10, y: 10}
if (selected.includes(d.id)) { // 判断是不是拖拽的是高亮选中的节点(ps: 多个节点拖拽)
currentMoveElement = selected.map(item => d3.select(`#rect-${item}`));
} else { // 当前选中的节点拖拽
currentMoveElement = [d3.select(`#rect-${d.id}`)];
}
};
function dragged(d) { // drag
console.log(d, 'drag log....');
const currentX = d3.event.x; // 使用d3.event 能获取到当前拖拽的x、y
const currentY = d3.event.y;
const { x: originX, y: originY } = d3.select(`#rect-${d.id}`).datum() || {}; // datum 获取到之前绑定的值
diffX = currentX - originX; // 计算出元素位置跟拖拽位置的diff(x、y)
diffY = currentY - originY;
if (currentMoveElement) {
currentMoveElement.forEach((item) => { // 统一更改坐标
const { x, y } = item.datum();
item.attr('x', x + diffX) // 一直修改其位置
.attr('y', y + diffY);
})
}
};
function dragEnded(d) { // drag end
console.log(d, 'end log....')
const dragIdMap = new Map;
currentMoveElement.forEach(item => { // 统一更改绑定数据的值
const originData = item.datum();
dragIdMap.set(originData.id, true);
item.datum({ // 重新绑定拖拽后的新坐标
...originData,
x: originData.x + diffX,
y: originData.y + diffY,
})
});
//对应的data原始数据也要更新,假如你有个保存的场景 那尼获取到的坐标就不是最新的了
data.forEach((item) => {
if (dragIdMap.has(item.id)) {
item.x = item.x + diffX;
item.y = item.y + diffY;
}
})
console.log(data, 'data')
};
总结:关键点判断是不是拖拽的是高亮选中的节点 如果拖拽的是选中的高亮节点就多个拖拽。如果拖拽的不是高亮的就是单个拖拽。其他都是跟单个拖拽一样(需要改变绑定的数据、需要更改原始数据)。
自定义drag跟自定义brush多少有点像,都是利用鼠标move事件来达到咱们期望的效果。
效果图:
基础代码在这上面改动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#container {
width: 500px;
margin: 50px auto 0;
}
</style>
</head>
<body>
<div id="container">
</div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const svg = d3.select('#container')
.append('svg')
.attr('width', 500)
.attr('height', 500);
const data = [
{ id: 1, fill: 'black', x: 10, y: 10 },
{ id: 2, fill: 'black', x: 50, y: 50 },
{ id: 3, fill: 'black', x: 100, y: 70 },
{ id: 4, fill: 'black', x: 20, y: 100 }
];
draw(); // 绘制
function draw() {
const update = svg.selectAll('rect')
.data(data, d => d.id);
//修改层
update.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
//渲染层
const enter = update.enter();
//删除层
const exit = update.exit();
enter.append('rect')
.attr('width', 20)
.attr('height', 20)
.attr('id', d => `rect-${d.id}`) //不能绑定数字咱们给拼接一个字符串 error: Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#4' is not a valid selector.
.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
.attr('stroke', 'blue')
.attr('strokeWidth', 1)
exit.remove()
};
const selected = [];
bindEvent(); // 绑定点击事件
function bindEvent() { // 绑定点击事件(看不懂的去前面d3event事件绑定去看看)
svg.selectAll('rect').on('click', (d) => {
const index = selected.indexOf(d.id);
if (index !== -1) {
selected.splice(index, 1);
} else {
selected.push(d.id);
}
d3.event.preventDefault();
updateColor();
})
};
function updateColor() { // 更新color
console.log(selected, 'selected')
data.forEach((item) => {
if (selected.includes(item.id)) {
item.fill = 'red';
}
});
draw(); // 重新绘制,更新需要高亮的点(d3数据绑定函数,不理解怎么更新的可以去看看d3数据绑定篇幅)
}
</script>
custom drag code
下面代码定义了bindDrag
方法,其他就跟之前的drag
一样了。可以用上面多个小方块代码往里面套就好了。
bindDrag(); //绑定drag
function bindDrag() {
let diffX; let diffY
svg.selectAll('rect')
.on('mousedown', function (d) {
// start drag
const { offsetX: x1, offsetY: y1 } = d3.event; // 使用d3.event获取当前的x、y
console.log(d3.event, 'd3.event down');
svg.on('mousemove', function () {
// move drag
const { offsetX: x2, offsetY: y2 } = d3.event;
diffX = x2 - x1;
diffY = y2 - y1;
console.log(d3.event, 'd3.event move');
});
svg.on('mouseup', function () {
// end drag
console.log(d3.event, 'd3.event up');
svg.on('mousemove', null);
svg.on('mouseup', null);
});
});
};
自定义drag完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#container {
width: 500px;
margin: 50px auto 0;
}
</style>
</head>
<body>
<div id="container">
</div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const svg = d3.select('#container')
.append('svg')
.attr('width', 500)
.attr('height', 500);
const data = [
{ id: 1, fill: 'black', x: 10, y: 10 },
{ id: 2, fill: 'black', x: 50, y: 50 },
{ id: 3, fill: 'black', x: 100, y: 70 },
{ id: 4, fill: 'black', x: 20, y: 100 }
];
draw(); // 绘制
function draw() {
const update = svg.selectAll('rect')
.data(data, d => d.id);
//修改层
update.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
//渲染层
const enter = update.enter();
//删除层
const exit = update.exit();
enter.append('rect')
.attr('width', 20)
.attr('height', 20)
.attr('id', d => `rect-${d.id}`) //不能绑定数字咱们给拼接一个字符串 error: Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#4' is not a valid selector.
.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
.attr('stroke', 'blue')
.attr('strokeWidth', 1)
exit.remove()
}
const selected = [];
bindEvent(); // 绑定点击事件
function bindEvent() { // 绑定点击事件(看不懂的去前面d3event事件绑定去看看)
svg.selectAll('rect').on('click', (d) => {
const index = selected.indexOf(d.id);
if (index !== -1) {
selected.splice(index, 1);
} else {
selected.push(d.id);
}
d3.event.preventDefault();
updateColor();
});
};
function updateColor() { // 更新color
console.log(selected, 'selected')
data.forEach((item) => {
if (selected.includes(item.id)) {
item.fill = 'red';
}
});
draw(); // 重新绘制,更新需要高亮的点(d3数据绑定函数,不理解怎么更新的可以去看看d3数据绑定篇幅)
};
bindDrag(); //绑定drag
function bindDrag() {
let diffX; let diffY; let currentMoveElement;
svg.selectAll('rect')
.on('mousedown', function (d) {
// start drag
const { offsetX: x1, offsetY: y1 } = d3.event; // 使用d3.event获取当前的x、y
console.log(d3.event, 'd3.event down');
if (selected.includes(d.id)) { // 判断是不是拖拽的是高亮选中的节点(ps: 多个节点拖拽)
currentMoveElement = selected.map(item => d3.select(`#rect-${item}`));
} else { // 当前选中的节点拖拽
currentMoveElement = [d3.select(`#rect-${d.id}`)];
}
svg.on('mousemove', function () {
// move drag
const { offsetX: x2, offsetY: y2 } = d3.event;
diffX = x2 - x1;
diffY = y2 - y1;
if (currentMoveElement) {
currentMoveElement.forEach((item) => { // 统一更改坐标
const { x, y } = item.datum();
item.attr('x', x + diffX) // 一直修改其位置
.attr('y', y + diffY);
})
}
console.log(d3.event, 'd3.event move');
});
svg.on('mouseup', function () {
// end drag
console.log(d3.event, 'd3.event up');
svg.on('mousemove', null);
svg.on('mouseup', null);
const dragIdMap = new Map;
currentMoveElement.forEach(item => { // 统一更改绑定数据的值
const originData = item.datum();
dragIdMap.set(originData.id, true);
item.datum({ // 重新绑定拖拽后的新坐标
...originData,
x: originData.x + diffX,
y: originData.y + diffY,
})
});
//对应的data原始数据也要更新,假如你有个保存的场景 那尼获取到的坐标就不是最新的了
data.forEach((item) => {
if (dragIdMap.has(item.id)) {
item.x = item.x + diffX;
item.y = item.y + diffY;
}
})
});
});
};
</script>
相信大家经过多个小方块一块拖动,和自定义拖拽对drag有了一个很深的认识了
阅读量:426
点赞量:0
收藏量:0