Я пытаюсь размыть / понизить непрозрачность несвязанных ссылок при перетаскивании определенного узла. Таким образом, при перетаскивании нужно выделять только связанные ссылки и узлы и размывать те, которые не относятся к перетаскиваемому узлу. Он работает, если он находится вне функции перетаскивания, но он несовместим, когда я держу его внутри него. Просто хаотично мигает. Это должно происходить в перетаскивании?
const link = vis.selectAll("line")
.data(data.links)
.join("line")
.attr("stroke", 'grey')
.attr("fill", "red")
.attr("opacity", 0.2)
const drag = (simulation) => {
const dragstarted = (event) => {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
const dragged = (event) => {
event.subject.fx = event.x;
event.subject.fy = event.y;
node.on("mouseover", function(d) {
let thisNode = this.id;
console.info("? ~ file: index.html ~ line 99 ~ node.on ~ thisNode", thisNode)
console.info("? ~ file: index.html ~ line 106 ~ node.on ~ d", d)
link.attr("opacity", function(d) {
return (d.source.id == thisNode || d.target.id == thisNode) ? 1 : 0.2
});
});
// Set the stroke width back to normal when mouse leaves the node.
node.on('mouseout', function(d) {
link.attr('opacity', 0.2);
});
}
const dragended = (event) => {
if (!event.active) {simulation.alphaTarget(0)};
event.subject.fx = null;
event.subject.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
const node = vis.selectAll(".node")
.data(data.nodes)
.join("path")
.attr("class", "node")
.attr("d", d3.symbol().type(d3.symbolPlus).size(300))
.attr("id", d => d.id)
.attr("r", 10)
.attr("stroke", 'red')
.style("fill", "none")
.call(drag(simulation));
simulation.on("tick", () => {
text.attr("transform", (d) => {
return "translate(" + (d.x + 20) + "," + (d.y + 5) + ")";
});
node.attr("transform", (d) => {
return "translate(" + d.x + "," + d.y + ")";
});
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
})
let data = {"nodes":[{"id":"piquetures hp","group":"big"},{"id":"postcards hp","group":"big"},{"id":"instagram hp","group":"big"},{"id":"tumblr hp","group":"big"},{"id":"soundcloud","group":"big"},{"id":"pq about","group":"mid"},{"id":"pq playlists","group":"mid"},{"id":"pq videos","group":"mid"},{"id":"pq channels","group":"mid"},{"id":"pc about","group":"mid"},{"id":"pc videos","group":"mid"},{"id":"pc playlists","group":"mid"},{"id":"pc channels","group":"mid"},{"id":"tb about","group":"mid"},{"id":"passion project playlist","group":"less mid"},{"id":"other playlists","group":"less mid"},{"id":"old videos","group":"less mid"},{"id":"nz snacks","group":"low"},{"id":"macbook pro","group":"low"},{"id":"asia trip","group":"low"},{"id":"AoT","group":"low"},{"id":"names.","group":"low"},{"id":"vade","group":"low"},{"id":"nyc","group":"low"},{"id":"meet me","group":"low"},{"id":"doubleD","group":"low"},{"id":"nice day!","group":"low"},{"id":"k-pop","group":"low"},{"id":"4 lives","group":"low"},{"id":"adventure awaits","group":"low"},{"id":"jill vid","group":"low"},{"id":"chatty cny","group":"low"},{"id":"fan art","group":"low"},{"id":"bts","group":"low"},{"id":"ig xposts","group":"low"},{"id":"reblogged content","group":"low"},{"id":"fitness stories","group":"low"},{"id":"campaigns","group":"low"},{"id":"other channels","group":"less mid"},{"id":"online art portfolio","group":"less mid"},{"id":"angela","group":"low"}],"links":[{"source":"piquetures hp","target":"instagram hp","do I have to click to see":"","context":""},{"source":"instagram hp","target":"piquetures hp","do I have to click to see":"","context":""},{"source":"tumblr hp","target":"tb about","do I have to click to see":1,"context":"sidebar"},{"source":"tb about","target":"tumblr hp","do I have to click to see":"","context":""},{"source":"postcards hp","target":"piquetures hp","do I have to click to see":"","context":"we put it there"},{"source":"piquetures hp","target":"passion project playlist","do I have to click to see":"","context":"we put it there"},{"source":"passion project playlist","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"piquetures hp","target":"pq about","do I have to click to see":"","context":"youtube given"},{"source":"pq about","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"piquetures hp","target":"pq playlists","do I have to click to see":"","context":"youtube given"},{"source":"pq playlists","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"piquetures hp","target":"pq videos","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"piquetures hp","target":"pq channels","do I have to click to see":"","context":"youtube given"},{"source":"pq channels","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"piquetures hp","target":"AoT","do I have to click to see":"","context":"we put it there"},{"source":"piquetures hp","target":"names.","do I have to click to see":"","context":"we put it there"},{"source":"piquetures hp","target":"doubleD","do I have to click to see":"","context":"we put it there"},{"source":"piquetures hp","target":"meet me","do I have to click to see":"","context":"we put it there"},{"source":"piquetures hp","target":"other channels","do I have to click to see":"","context":"we put it there"},{"source":"piquetures hp","target":"asia trip","do I have to click to see":"","context":"we put it there"},{"source":"piquetures hp","target":"other playlists","do I have to click to see":"","context":"we put it there"},{"source":"AoT","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"AoT","target":"vade","do I have to click to see":1,"context":"within text"},{"source":"AoT","target":"names.","do I have to click to see":1,"context":"within text"},{"source":"AoT","target":"passion project playlist","do I have to click to see":1,"context":"within text"},{"source":"AoT","target":"bts","do I have to click to see":1,"context":"within text"},{"source":"AoT","target":"soundcloud","do I have to click to see":1,"context":"within text"},{"source":"AoT","target":"instagram hp","do I have to click to see":1,"context":"within text"},{"source":"AoT","target":"postcards hp","do I have to click to see":1,"context":"within text"},{"source":"AoT","target":"tumblr hp","do I have to click to see":1,"context":"within text"},{"source":"names.","target":"postcards hp","do I have to click to see":1,"context":"within text"},{"source":"names.","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"names.","target":"passion project playlist","do I have to click to see":1,"context":"within text"},{"source":"names.","target":"soundcloud","do I have to click to see":1,"context":"within text"},{"source":"names.","target":"instagram hp","do I have to click to see":1,"context":"within text"},{"source":"names.","target":"tumblr hp","do I have to click to see":1,"context":"within text"},{"source":"doubleD","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"doubleD","target":"meet me","do I have to click to see":1,"context":"within text"},{"source":"doubleD","target":"soundcloud","do I have to click to see":1,"context":"within text"},{"source":"doubleD","target":"instagram hp","do I have to click to see":1,"context":"within text"},{"source":"doubleD","target":"tumblr hp","do I have to click to see":1,"context":"within text"},{"source":"doubleD","target":"passion project playlist","do I have to click to see":1,"context":"within text"},{"source":"doubleD","target":"k-pop","do I have to click to see":2,"context":"end of video"},{"source":"doubleD","target":"piquetures hp","do I have to click to see":2,"context":"end of video"},{"source":"meet me","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"meet me","target":"instagram hp","do I have to click to see":1,"context":"within text"},{"source":"meet me","target":"soundcloud","do I have to click to see":1,"context":"within text"},{"source":"meet me","target":"AoT","do I have to click to see":2,"context":"end of video"},{"source":"meet me","target":"piquetures hp","do I have to click to see":2,"context":"end of video"},{"source":"4 lives","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"4 lives","target":"other channels","do I have to click to see":1,"context":"within text"},{"source":"4 lives","target":"instagram hp","do I have to click to see":1,"context":"within text"},{"source":"4 lives","target":"soundcloud","do I have to click to see":1,"context":"within text"},{"source":"4 lives","target":"online art portfolio","do I have to click to see":1,"context":"within text"},{"source":"4 lives","target":"old videos","do I have to click to see":1,"context":"within text"},{"source":"4 lives","target":"asia trip","do I have to click to see":1,"context":"within text"},{"source":"vade","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"vade","target":"angela","do I have to click to see":1,"context":"within text"},{"source":"vade","target":"instagram hp","do I have to click to see":1,"context":"within text"},{"source":"vade","target":"online art portfolio","do I have to click to see":1,"context":"within text"},{"source":"nyc","target":"other channels","do I have to click to see":1,"context":"within text"},{"source":"nyc","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"nyc","target":"instagram hp","do I have to click to see":1,"context":"within text"},{"source":"nyc","target":"soundcloud","do I have to click to see":1,"context":"within text"},{"source":"nice day!","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"nice day!","target":"other channels","do I have to click to see":1,"context":"within text"},{"source":"k-pop","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"adventure awaits","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"adventure awaits","target":"other channels","do I have to click to see":1,"context":"within text"},{"source":"piquetures hp","target":"4 lives","do I have to click to see":1,"context":"within embedded playlist"},{"source":"piquetures hp","target":"vade","do I have to click to see":1,"context":"within embedded playlist"},{"source":"piquetures hp","target":"nyc","do I have to click to see":1,"context":"within embedded playlist"},{"source":"piquetures hp","target":"nice day!","do I have to click to see":1,"context":"within embedded playlist"},{"source":"piquetures hp","target":"k-pop","do I have to click to see":1,"context":"within embedded playlist"},{"source":"piquetures hp","target":"adventure awaits","do I have to click to see":1,"context":"within embedded playlist"},{"source":"pq videos","target":"old videos","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"nz snacks","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"macbook pro","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"asia trip","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"AoT","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"names.","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"vade","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"nyc","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"meet me","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"doubleD","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"nice day!","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"k-pop","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"4 lives","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"adventure awaits","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"instagram hp","do I have to click to see":"","context":"at top"},{"source":"pq videos","target":"pq playlists","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"pq channels","do I have to click to see":"","context":"youtube given"},{"source":"pq videos","target":"pq about","do I have to click to see":"","context":"youtube given"},{"source":"pq playlists","target":"other playlists","do I have to click to see":"","context":"we put it there"},{"source":"pq playlists","target":"passion project playlist","do I have to click to see":"","context":"we put it there"},{"source":"pq playlists","target":"pq videos","do I have to click to see":"","context":"youtube given"},{"source":"pq playlists","target":"pq channels","do I have to click to see":"","context":"youtube given"},{"source":"pq playlists","target":"pq about","do I have to click to see":"","context":"youtube given"},{"source":"pq playlists","target":"asia trip","do I have to click to see":"","context":"we put it there"},{"source":"pq channels","target":"pq videos","do I have to click to see":"","context":"youtube given"},{"source":"pq channels","target":"pq playlists","do I have to click to see":"","context":"youtube given"},{"source":"pq channels","target":"pq about","do I have to click to see":"","context":"youtube given"},{"source":"pq channels","target":"other channels","do I have to click to see":"","context":"we put it there"},{"source":"pq about","target":"pq videos","do I have to click to see":"","context":"youtube given"},{"source":"pq about","target":"pq playlists","do I have to click to see":"","context":"youtube given"},{"source":"pq about","target":"pq channels","do I have to click to see":"","context":"youtube given"},{"source":"pq about","target":"instagram hp","do I have to click to see":"","context":"we put it there"},{"source":"pq about","target":"postcards hp","do I have to click to see":"","context":"we put it there"},{"source":"pq about","target":"instagram hp","do I have to click to see":"","context":"at top"},{"source":"pq channels","target":"instagram hp","do I have to click to see":"","context":"at top"},{"source":"pq playlists","target":"instagram hp","do I have to click to see":"","context":"at top"},{"source":"postcards hp","target":"jill vid","do I have to click to see":"","context":"we put it there"},{"source":"postcards hp","target":"chatty cny","do I have to click to see":"","context":"we put it there"},{"source":"postcards hp","target":"pc about","do I have to click to see":"","context":"youtube given"},{"source":"postcards hp","target":"pc videos","do I have to click to see":"","context":"youtube given"},{"source":"postcards hp","target":"pc playlists","do I have to click to see":"","context":"youtube given"},{"source":"postcards hp","target":"pc channels","do I have to click to see":"","context":"youtube given"},{"source":"pc videos","target":"pc about","do I have to click to see":"","context":"youtube given"},{"source":"pc videos","target":"pc playlists","do I have to click to see":"","context":"youtube given"},{"source":"pc videos","target":"pc channels","do I have to click to see":"","context":"youtube given"},{"source":"pc videos","target":"jill vid","do I have to click to see":"","context":"youtube given"},{"source":"pc videos","target":"chatty cny","do I have to click to see":"","context":"youtube given"},{"source":"pc videos","target":"piquetures hp","do I have to click to see":"","context":"at top"},{"source":"pc playlists","target":"pc about","do I have to click to see":"","context":"youtube given"},{"source":"pc playlists","target":"pc videos","do I have to click to see":"","context":"youtube given"},{"source":"pc playlists","target":"pc channels","do I have to click to see":"","context":"youtube given"},{"source":"pc playlists","target":"piquetures hp","do I have to click to see":"","context":"at top"},{"source":"pc channels","target":"piquetures hp","do I have to click to see":"","context":"we put it there"},{"source":"pc channels","target":"pc about","do I have to click to see":"","context":"youtube given"},{"source":"pc channels","target":"pc videos","do I have to click to see":"","context":"youtube given"},{"source":"pc channels","target":"pc playlists","do I have to click to see":"","context":"youtube given"},{"source":"pc channels","target":"piquetures hp","do I have to click to see":"","context":"at top"},{"source":"pc about","target":"pc channels","do I have to click to see":"","context":"youtube given"},{"source":"pc about","target":"pc videos","do I have to click to see":"","context":"youtube given"},{"source":"pc about","target":"pc playlists","do I have to click to see":"","context":"youtube given"},{"source":"pc about","target":"piquetures hp","do I have to click to see":"","context":"at top"},{"source":"pc about","target":"piquetures hp","do I have to click to see":"","context":"we put it there"},{"source":"pc about","target":"instagram hp","do I have to click to see":"","context":"we put it there"},{"source":"pc about","target":"piquetures hp","do I have to click to see":"","context":"we put it there"},{"source":"jill vid","target":"piquetures hp","do I have to click to see":1,"context":"within text"},{"source":"jill vid","target":"instagram hp","do I have to click to see":1,"context":"within text"},{"source":"jill vid","target":"tumblr hp","do I have to click to see":1,"context":"within text"},{"source":"jill vid","target":"soundcloud","do I have to click to see":1,"context":"within text"},{"source":"chatty cny","target":"piquetures hp","do I have to click to see":1,"context":"within text"},{"source":"chatty cny","target":"instagram hp","do I have to click to see":1,"context":"within text"},{"source":"chatty cny","target":"soundcloud","do I have to click to see":1,"context":"within text"},{"source":"chatty cny","target":"names.","do I have to click to see":2,"context":"end of video"},{"source":"chatty cny","target":"piquetures hp","do I have to click to see":2,"context":"end of video"},{"source":"passion project playlist","target":"AoT","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"names.","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"doubleD","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"meet me","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"4 lives","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"vade","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"nyc","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"nice day!","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"k-pop","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"adventure awaits","do I have to click to see":"","context":"youtube given"},{"source":"passion project playlist","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"asia trip","target":"piquetures hp","do I have to click to see":"","context":"youtube given"},{"source":"instagram hp","target":"piquetures hp","do I have to click to see":"","context":"we put it there"},{"source":"campaigns","target":"instagram hp","do I have to click to see":"","context":"instagram given"},{"source":"tumblr hp","target":"fan art","do I have to click to see":"","context":"we put it there"},{"source":"tumblr hp","target":"bts","do I have to click to see":"","context":"we put it there"},{"source":"tumblr hp","target":"ig xposts","do I have to click to see":"","context":"we put it there"},{"source":"tumblr hp","target":"reblogged content","do I have to click to see":"","context":"we put it there"},{"source":"bts","target":"jill vid","do I have to click to see":"","context":"within text"},{"source":"reblogged content","target":"tumblr hp","do I have to click to see":"","context":"we put it there"},{"source":"bts","target":"tumblr hp","do I have to click to see":"","context":"we put it there"},{"source":"ig xposts","target":"tumblr hp","do I have to click to see":"","context":"we put it there"},{"source":"fan art","target":"tumblr hp","do I have to click to see":"","context":"we put it there"},{"source":"tb about","target":"instagram hp","do I have to click to see":"","context":"we put it there"},{"source":"tb about","target":"piquetures hp","do I have to click to see":"","context":"we put it there"},{"source":"tb about","target":"postcards hp","do I have to click to see":"","context":"we put it there"},{"source":"tb about","target":"soundcloud","do I have to click to see":"","context":"we put it there"},{"source":"ig xposts","target":"AoT","do I have to click to see":"","context":"within text"},{"source":"instagram hp","target":"campaigns","do I have to click to see":"","context":"instagram given"},{"source":"instagram hp","target":"fitness stories","do I have to click to see":"","context":"instagram given"},{"source":"fitness stories","target":"instagram hp","do I have to click to see":"","context":"instagram given"}]}
// SVG dimensions set to full width and height of the screen
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
const height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
const drawChart = (data) => {
// Create detached <svg> element.
const vis = d3.select('.main_container')
.append("svg")
.attr("width", width)
.attr("height", height);
const g = vis.append("g")
const simulation = d3.forceSimulation(data.nodes).force("charge", d3.forceManyBody().strength(-300)).force("link", d3.forceLink(data.links)
.id(d => d.id)
.distance(300)).force("center", d3.forceCenter(width / 2, height / 2)).force("collide", d3.forceCollide().strength(10))
const link = vis.selectAll("line")
.data(data.links)
.join("line")
.attr("stroke", 'grey')
.attr("fill", "red")
.attr("opacity", 0.2)
const drag = (simulation) => {
const dragstarted = (event) => {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
const dragged = (event) => {
event.subject.fx = event.x;
event.subject.fy = event.y;
node.on("mouseover", function(d) {
let thisNode = this.id;
console.info("? ~ file: index.html ~ line 99 ~ node.on ~ thisNode", thisNode)
console.info("? ~ file: index.html ~ line 106 ~ node.on ~ d", d)
link.attr("opacity", function(d) {
return (d.source.id == thisNode || d.target.id == thisNode) ? 1 : 0.2
});
});
// Set the stroke width back to normal when mouse leaves the node.
node.on('mouseout', function(d) {
link.attr('opacity', 0.2);
});
}
const dragended = (event) => {
if (!event.active) {simulation.alphaTarget(0)};
event.subject.fx = null;
event.subject.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
const node = vis.selectAll(".node")
.data(data.nodes)
.join("path")
.attr("class", "node")
.attr("d", d3.symbol().type(d3.symbolPlus).size(300))
.attr("id", d => d.id)
.attr("r", 10)
.attr("stroke", 'red')
.style("fill", "none")
.call(drag(simulation));
const text = vis.append("g")
.attr("class", "labels")
.selectAll("text")
.data(data.nodes)
.join("text")
.text(d => d.id);
simulation.on("tick", () => {
text.attr("transform", (d) => {
return "translate(" + (d.x + 20) + "," + (d.y + 5) + ")";
});
node.attr("transform", (d) => {
return "translate(" + d.x + "," + d.y + ")";
});
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
})
// zoom block
const handleZoom = (e) => {
d3.select('svg g')
.attr('transform', e.transform);
}
const zoom = d3.zoom()
.scaleExtent([0.1, 3.5]) //[k0, k1] where k0 is the minimum allowed scale factor and k1 is the maximum allowed scale factor, and returns this zoom behavior. If extent is not specified, returns the current scale extent, which defaults to [0, ∞]. The scale extent restricts zooming in and out.
// .translateExtent([
// [0, 0],
// [width, height]
// ]) // constrain the zoom and pan so that the it can only zoom and pan within specified bounds
.on('zoom', handleZoom);
d3.select('.main_container')
.call(zoom);
}
drawChart(data);
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
<div class = "main_container"></div>
dragged
вызывается во время перетаскивания, поэтому имеет смысл обновлять непрозрачность ссылки в dragstarted
и dragend
, т.е. минимальное количество раз.
Например. в dragstarted
запустите тест на то, подключены ли ссылки, и соответствующим образом обновите непрозрачность (например, 1 для подключенных ссылок, 0,1 для несвязанных ссылок):
const dragstarted = (event) => {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
// update link opacity to 0.1 for non-connected nodes
link.each(function(d) {
const test = d.source.id === event.subject.id || d.target.id === event.subject.id;
d3.select(this).attr("opacity", test ? 1 : 0.1);
});
}
Затем в dragended
вы можете вернуть ссылки к исходному состоянию непрозрачности:
const dragended = (event) => {
if (!event.active) {
simulation.alphaTarget(0)
};
event.subject.fx = null;
event.subject.fy = null;
// reset link opacity to 1
link.attr("opacity", 1);
}
Я обновил несколько элементов в вашем коде: data
для краткости, длина ссылки для демонстрационных целей, уменьшение непрозрачности с 1 до 0,1 (по вашему вопросу) и использование кругов, а не крестов для узла, поскольку кресты кажутся очень сложными для манипулирования. В противном случае рабочий пример соответствует вашему вопросу:
// SVG dimensions set to full width and height of the screen
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
const height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
const drawChart = (data) => {
// Create detached <svg> element.
const vis = d3.select('.main_container')
.append("svg")
.attr("width", width)
.attr("height", height);
const g = vis.append("g");
const simulation = d3.forceSimulation(data.nodes)
.force("charge", d3.forceManyBody().strength(-300))
.force("link", d3.forceLink(data.links).id(d => d.id).distance(100)) // reduced from 300 for demo purpose
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collide", d3.forceCollide().strength(10))
const link = vis.selectAll("line")
.data(data.links)
.join("line")
.attr("stroke", 'grey')
.attr("fill", "red")
.attr("opacity", 1) // changed to 1 so we can lower opacity
const drag = (simulation) => {
const dragstarted = (event) => {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
// update link opacity to 0.1 for non-connected nodes
link.each(function(d) {
const test = d.source.id === event.subject.id || d.target.id === event.subject.id;
d3.select(this).attr("opacity", test ? 1 : 0.1);
});
}
const dragged = (event) => {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
const dragended = (event) => {
if (!event.active) {
simulation.alphaTarget(0)
};
event.subject.fx = null;
event.subject.fy = null;
// reset link opacity to 1
link.attr("opacity", 1);
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
// for demo purpose replaced cross with circle
// as the crosses are difficult to drag
const node = vis.selectAll(".node")
.data(data.nodes)
//.join("path")
//.attr("class", "node")
//.attr("d", d3.symbol().type(d3.symbolPlus).size(300))
//.attr("id", d => d.id)
//.attr("r", 10)
//.attr("stroke", 'red')
//.style("fill", "none")
.join("circle")
.attr("class", "node")
.attr("id", d => d.id)
.attr("r", 8)
.style("fill", "red")
.call(drag(simulation));
const text = vis.append("g")
.attr("class", "labels")
.selectAll("text")
.data(data.nodes)
.join("text")
.text(d => d.id);
simulation.on("tick", () => {
text.attr("transform", (d) => {
return "translate(" + (d.x + 20) + "," + (d.y + 5) + ")";
});
node.attr("transform", (d) => {
return "translate(" + d.x + "," + d.y + ")";
});
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
})
// zoom block
const handleZoom = (e) => {
d3.select('svg g')
.attr('transform', e.transform);
}
const zoom = d3.zoom()
.scaleExtent([0.1, 3.5]) //[k0, k1] where k0 is the minimum allowed scale factor and k1 is the maximum allowed scale factor, and returns this zoom behavior. If extent is not specified, returns the current scale extent, which defaults to [0, ∞]. The scale extent restricts zooming in and out.
// .translateExtent([
// [0, 0],
// [width, height]
// ]) // constrain the zoom and pan so that the it can only zoom and pan within specified bounds
.on('zoom', handleZoom);
d3.select('.main_container')
.call(zoom);
}
drawChart(data);
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
<div class = "main_container"></div>
<script>
const data = {
"nodes": [
{ "id": "A", "group": "big" },
{ "id": "B", "group": "big" },
{ "id": "C", "group": "big" },
{ "id": "D", "group": "big" },
{ "id": "E", "group": "big" }
],
"links": [
{ "source": "A", "target": "B" },
{ "source": "A", "target": "C" },
{ "source": "B", "target": "D" },
{ "source": "B", "target": "E" }
]
}
</script>
Большое спасибо, Робин. Поэтому нет необходимости задействовать узлы, просто работайте напрямую со ссылками, но как в
dragStarted
, так и вdragEnded
.