javascript - D3js: Menyeret sebuah grup dengan menggunakan salah satu anaknya
Jsfiddle: http://jsfiddle.net/6NBy2/
Kode :
var in_editor_drag = d3.behavior.drag()
.origin(function() {
var g = this.parentNode;
return {x: d3.transform(g.getAttribute("transform")).translate[0],
y: d3.transform(g.getAttribute("transform")).translate[1]};
})
.on("drag", function(d,i) {
g = this.parentNode;
translate = d3.transform(g.getAttribute("transform")).translate;
x = d3.event.dx + translate[0],
y = d3.event.dy + translate[1];
d3.select(g).attr("transform", "translate(" + x + "," + y + ")");
d3.event.sourceEvent.stopPropagation();
});
svg = d3.select("svg");
d = {x: 20, y: 20 };
groups = svg
.append("g")
.attr("transform", "translate(20, 20)");
groups
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.style("fill", "green")
.call(in_editor_drag)
.style("opacity", 0.4);
Saya sedang mencoba menyeret sebuah grup dengan menggunakan salah satu anaknya sebagai pegangan. Sederhananya, yang saya coba lakukan adalah, ketika anak dari sebuah grup diseret:
- Dapatkan transformasi translasi dari grup
- Dapatkan jarak seret dari d3.event.dx, d3.event.dy
- Terapkan perbedaan pada atribut transform kelompok
Ketika anak digerakkan, kelompok tidak bergerak seperti yang diharapkan. Kelompok bergerak lebih sedikit dari jarak yang digerakkan, dan mulai melompat-lompat ke sana kemari.
Apa yang salah yang saya lakukan di sini?
Sunting:
jsfiddle yang diperbarui: http://jsfiddle.net/6NBy2/2/
Saya mencoba menyeret seluruh grup dengan menggunakan satu atau lebih anaknya sebagai pegangan untuk menyeret.
Jawaban #1
Ini adalah pertanyaan lama, tetapi belum benar-benar terjawab. Saya mengalami masalah yang sama persis dan ingin menyeret grup hanya dengan satu anak (bukan semua elemen anak dari <g>). Masalahnya adalah, d3.event.dx/y dihitung relatif terhadap posisi <g>. Dan begitu <g> dipindahkan dengan .attr(“transform”, “translate(x, y)”), d3.event.dx/dy disesuaikan ke nilai baru (lebih kecil). Ini mengakibatkan gerakan tersentak dengan kecepatan kira-kira setengah dari kursor. Saya menemukan dua solusi yang mungkin untuk ini:
Pertama (akhirnya saya berakhir dengan pendekatan ini):
Tambahkan rect pegangan seret langsung ke svg dan bukan ke <g>. Jadi posisinya relatif terhadap <svg> dan bukan terhadap <g>. Kemudian pindahkan keduanya (yaitu <rect> dan <g>) dalam fungsi on drag.
var svg = d3.select("svg");
var group = svg
.append("g").attr("id", "group")
.attr("transform", "translate(0, 0)");
group
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.style("fill", "green")
.style("opacity", 0.4);
group
.append("text")
.attr("x", 10)
.attr("y", 5)
.attr("dominant-baseline", "hanging")
.text("drag me");
handle = svg
.append("rect")
.data([{
// Position of the rectangle
x: 0,
y: 0
}])
.attr("class", "draghandle")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 20)
.style("fill", "blue")
.style("opacity", 0.4)
.attr("cursor", "move")
.call(d3.drag().on("drag", function (d) {
console.log("yep");
d.x += d3.event.dx;
d.y += d3.event.dy;
// Move handle rect
d3.select(this)
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
});
// Move Group
d3.select("#group").attr("transform", "translate(" + [d.x, d.y] + ")");
}));<body>
<svg width="400" height="400"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
</body>Kedua:
Periksa elemen mana yang berada di bawah kursor saat peristiwa seret dengan d3.event.sourceEvent.path[0] dan jalankan fungsi seret hanya jika pegangan <rect> diklik. Dengan pendekatan ini, semua elemen dapat dikelompokkan dalam satu <g> (tidak perlu <rect> tambahan di luar grup). Kelemahan dari metode ini adalah, seret juga dieksekusi jika kursor digerakkan di atas pegangan seret dengan tombol mouse ditekan.
var svg = d3.select("svg");
var group = svg
.append("g")
.data([{
// Position of the rectangle
x: 0,
y: 0
}])
.attr("id", "group")
.attr("transform", function (d) {
return "translate(" + d.x + ", " + d.y + ")"
})
.call(d3.drag().on("drag", function (d) {
if (d3.event.sourceEvent.target.classList.value === "draghandle") {
console.log("yep");
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("transform", function (d) {
return "translate(" + [d.x, d.y] + ")"
})
} else {
console.log("nope");
return;
}
}));
group
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.style("fill", "green")
.style("opacity", 0.4);
group
.append("text")
.attr("x", 10)
.attr("y", 5)
.attr("dominant-baseline", "hanging")
.text("drag me");
handle = group
.append("rect")
.attr("class", "draghandle")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 20)
.style("fill", "blue")
.style("opacity", 0.4)
.attr("cursor", "move");<body>
<svg width="400" height="400"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
</body>Jawaban #2
Gunakan drag.container() untuk mengatur accessor kontainer.
Lihat dokumen D3.
