D3 размыть/уменьшить непрозрачность несвязанных ссылок при перетаскивании определенного узла

Я пытаюсь размыть / понизить непрозрачность несвязанных ссылок при перетаскивании определенного узла. Таким образом, при перетаскивании нужно выделять только связанные ссылки и узлы и размывать те, которые не относятся к перетаскиваемому узлу. Он работает, если он находится вне функции перетаскивания, но он несовместим, когда я держу его внутри него. Просто хаотично мигает. Это должно происходить в перетаскивании?

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>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
13
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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 .

Edgar Kiljak 10.04.2022 12:37

Другие вопросы по теме