原力 Force

basic force

程式碼

          
            // html 
            <div class="forceBasic"></div>

            // js 
            const currentWidth = d3.select(".forceBasic").style("width");
            const height = 200;
    
            const svg = d3
              .select(".forceBasic")
              .append('svg')
              .attr("width", currentWidth)
              .attr("height", height);

            const data = [
              { name: "A" },{ name: "B" },{ name: "C" },
              { name: "D" },{ name: "E" },{ name: "F" },
              { name: "G" },
            ];
    
            const dots = svg
              .append("g")
              .selectAll("circle")
              .data(data)
              .enter()
              .append("circle")
              .attr("cx", 250)
              .attr("cy", 150)
              .attr("r", 15)
              .style("fill", "green")
              .style("opacity", 0.4);
    
            // 設定力模擬
            const simulation = d3
              .forceSimulation()
              .alphaDecay(0) // 收斂永不停止
              .velocityDecay(0.2) // 設定摩擦係數
              .force("x", d3.forceX()) // 設定X軸平移位置
              .force("y", d3.forceY()) // 設定Y軸移動位置
              // 設定中心點位置
              .force("center", d3.forceCenter().x(250).y(150))
              // 設定節點間電荷力
              .force("charge", d3.forceManyBody().strength(1))
              // 設定節點間彼此的互斥力
              .force(
                "collide",
                d3.forceCollide().strength(0.1).radius(40).iterations(0.2)
              );
    
            // 將力模擬器的節點綁定資料,設定ticks開始時節點的動作
            simulation.nodes(data).on("tick", (d) => {
              dots.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
            });
          
        
force group

程式碼

          
            // html 
            <div class="forceGroup"></div>

            // js
            // 建立SVG
            const currentWidth = d3.select(".forceGroup").style("width");
            const height = 300;
    
            const svg = d3
              .select(".forceGroup")
              .append('svg')
              .attr("width", currentWidth)
              .attr("height", height);
    
            const data = [
              { name: "A", group: 150 },
              { name: "B", group: 150 },
              { name: "C", group: 150 },
              { name: "D", group: 150 },
              { name: "E", group: 150 },
              { name: "F", group: 150 },
              { name: "G", group: 250 },
              { name: "H", group: 250 },
              { name: "I", group: 250 },
              { name: "J", group: 250 },
              { name: "K", group: 250 },
              { name: "L", group: 350 },
              { name: "M", group: 350 },
              { name: "N", group: 350 },
              { name: "O", group: 350 },
            ];
    
            // 設定顏色
            const colorScale = d3
              .scaleOrdinal()
              .domain([150, 250, 350])
              .range(["red", "blue", "orange"]);
    
            // 建立圓點,全都位於正中央
            const node = svg
              .append("g")
              .selectAll("circle")
              .data(data)
              .enter()
              .append("circle")
              .attr("r", 20)
              .attr("cx", 250)
              .attr("cy", 150)
              .style("fill", (d) => colorScale(d.group))
              .style("opacity", "0.6");
    
            // 設定力模擬器
            const simulation = d3
              .forceSimulation()
              .force("x",d3.forceX().strength(0.5).x((d) => d.group))
              .force("y", d3.forceY().strength(0.1).y(150))
              .force("center", d3.forceCenter().x(250).y(150))
              .force("charge", d3.forceManyBody().strength(1))
              .force("collide",
                d3.forceCollide(10).strength(0.1).radius(20).iterations(1)
              );
    
            // 將設定好的力模擬器綁到節點上
            simulation.nodes(data).on("tick", (d) => {
              node.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
            });
          
        
force link

程式碼

          
            // html 
            <div class="forceLink"></div>

            // js
            // 建立SVG
            const currentWidth = d3.select(".forceGroup").style("width");
            const height = 400;
    
            const svg = d3
              .select(".forceLink")
              .append("svg")
              .attr("width", currentWidth)
              .attr("height", height);
              
            const data = {
              nodes: [
                { id: 1, name: "A" },
                { id: 2, name: "B" },
                { id: 3, name: "C" },
                { id: 4, name: "D" },
                { id: 5, name: "E" },
                { id: 6, name: "F" },
                { id: 7, name: "G" },
                { id: 8, name: "H" },
                { id: 9, name: "I" },
                { id: 10, name: "J" },
              ],
              links: [
                { source: 1, target: 2 },
                { source: 1, target: 3 },
                { source: 1, target: 6 },
                { source: 2, target: 3 },
                { source: 2, target: 7 },
                { source: 3, target: 4 },
                { source: 8, target: 3 },
                { source: 4, target: 5 },
                { source: 4, target: 9 },
                { source: 5, target: 10 },
              ],
            };
    
            const dots = svg
              .append("g")
              .selectAll("circle")
              .data(data.nodes)
              .join("circle")
              .attr("r", 15)
              .style("fill", "green")
              .style("opacity", 0.6)
              .style("cursor", "pointer");
    
            const link = svg
              .selectAll("line")
              .data(data.links)
              .join("line")
              .style("stroke", "#aaa");
              
            // 綁定節點
            const ticked = (d) => {
              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);
    
              dots.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
            };
    
            // 設定力模擬器
            const simulation = d3
              .forceSimulation(data.nodes)
              .force("link",d3.forceLink().id((d) => d.id).links(data.links))
              .force("charge", d3.forceManyBody().strength(-300))
              .force("center", d3.forceCenter(250, 150))
              .on("tick", ticked);
          
        
force hover

程式碼

          
            // html 
            <div class="forceHover"></div>

            // js
            // 建立SVG
            const currentWidth = d3.select(".forceHover").style("width");
            const height = 400;
    
            const svg = d3
              .select(".forceHover")
              .append("svg")
              .attr("width", currentWidth)
              .attr("height", height);

            const data = [
              { r: 13 },{ r: 34 },{ r: 23 },{ r: 33 },
              { r: 13 },{ r: 22 },{ r: 43 },{ r: 38 },
            ];
    
            const dots = svg
              .selectAll("circle")
              .data(data)
              .join("circle")
              .attr("r", (d) => d.r)
              .attr("fill", "blue")
              .attr("opacity", 0.3);
    
            // 設定力模擬器
            const simulation = d3
              .forceSimulation()
              .force("center", d3.forceCenter()
                        .x(parseInt(currentWidth) / 2)
                        .y(height / 2)
              )
              .force("charge", d3.forceManyBody().strength(0.3))
              .force("collide",d3.forceCollide().strength(0.1)
                                  .radius(30).iterations(1)
              );
              
            //綁定
            simulation.nodes(data).on("tick", (d) => {
              dots.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
            });
    
            // 建立tooltips
            const tooltips = d3
              .select(".forceHover")
              .style("position", "relative")
              .append("div")
              .style("display", "none")
              .style("position", "absolute")
              .style("background-color", "white")
              .style("border", "solid")
              .style("border-width", "2px")
              .style("border-radius", "5px")
              .style("padding", "5px");
    
            // 建立mouseover/mouseleave的函式
            const mouseover = (event, d) => {
              d3.select(event.target)
                .attr("stroke", "black")
                .attr("stroke-width", "3px")
                .attr("opacity", 0.7)
                .style("cursor", "pointer");
    
              const pt = d3.pointer(event, event.target);
              tooltips
                .style("display", "block")
                .style("left", pt[0] + 10 + "px")
                .style("top", pt[1] + "px")
                .html(`半徑:${d.r}`);
            };
    
            const mouseleave = (event, d) => {
              d3.select(event.target)
                .attr("stroke", "none")
                .attr("stroke-width", "0")
                .attr("opacity", 0.3);
    
              tooltips.style("display", "none");
            };
    
            // hover
            dots.on("mouseover", mouseover).on("mouseleave", mouseleave);
          
        
force drag

程式碼

          
            // html 
            <div class="forceDrag"></div>
            
            // js
            // 建立SVG
            const currentWidth = d3.select(".forceDrag").style("width");
            const height = 400;
    
            const svg = d3
              .select(".forceDrag")
              .append("svg")
              .attr("width", currentWidth)
              .attr("height", height);
    
            // 資料
            const data = [
              { name: "A" },{ name: "B" },{ name: "C" },
              { name: "D" },{ name: "E" },{ name: "F" },
              { name: "G" },
            ];
    
            // 建立原點,目前全部都在同個位置
            const dots = svg
              .selectAll("circle")
              .data(data)
              .join("circle")
              .attr("r", 25)
              .attr("cx", 50)
              .attr("cy", 50)
              .style("fill", "#19d3a2")
              .style("fill-opacity", 0.3)
              .attr("stroke", "#b3a2c8")
              .style("stroke-width", 4)
              .style("cursor", "pointer");
    
            // 建立力模擬圖
            const simulation = d3
              .forceSimulation()
              .force("center", d3.forceCenter().x(200).y(150))
              .force("charge", d3.forceManyBody().strength(1))
              .force(
                "collide",
                d3.forceCollide().strength(0.1).radius(30).iterations(1)
              );
    
            simulation.nodes(data).on("tick", (d)=> {
              dots.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
            });
    
            // 綁定拖曳事件
            dots.call(
              d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended)
            );
    
            // 拖曳開始
            function dragstarted(event, d) {
              d3.select(this).style("fill-opacity", 0.6);
              d.fx = d.x;
              d.fy = d.y;
              simulation.alphaTarget(0.03).restart();
            }
            // 拖曳期間
            function dragged(event, d) {
              d.fx = event.x;
              d.fy = event.y;
            }
            // 拖曳結束
            function dragended(event, d) {
              simulation.alphaTarget(0.03);
              d3.select(this).style("fill-opacity", 0.3);
              d.fx = null;
              d.fy = null;
            }
          
        
force drag and link

程式碼

          
            // html 
            <div class="forceDragLink"></div>

            // js
            // 建立SVG
            const currentWidth = d3.select(".forceDragLink").style("width");
            const height = 400;
    
            const svg = d3
              .select(".forceDragLink")
              .append("svg")
              .attr("width", currentWidth)
              .attr("height", height);
              
            const data = {
              nodes: [
                { id: 1, name: "A" },
                { id: 2, name: "B" },
                { id: 3, name: "C" },
                { id: 4, name: "D" },
                { id: 5, name: "E" },
                { id: 6, name: "F" },
                { id: 7, name: "G" },
                { id: 8, name: "H" },
                { id: 9, name: "I" },
                { id: 10, name: "J" },
              ],
              links: [
                { source: 1, target: 2 },
                { source: 1, target: 3 },
                { source: 1, target: 6 },
                { source: 2, target: 3 },
                { source: 2, target: 7 },
                { source: 3, target: 4 },
                { source: 8, target: 3 },
                { source: 4, target: 5 },
                { source: 4, target: 9 },
                { source: 5, target: 10 },
              ],
            };

            const dots = d3
              .select(".forceDragLink")
              .append("g")
              .selectAll("circle")
              .data(data.nodes)
              .enter()
              .append("circle")
              .attr("r", 15)
              .style("fill", "green")
              .style("opacity", 0.6)
              .style("cursor", "pointer");
    
            const link = d3
              .select(".forceDragLink")
              .selectAll("line")
              .data(data.links)
              .join("line")
              .style("stroke", "#aaa");
    
            // 綁定節點
            const ticked = (d) => {
              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);
    
              dots.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
            };
    
            // 設定力模擬器
            const simulation = d3
              .forceSimulation(data.nodes)
              .force("link",d3.forceLink().id((d) => d.id).links(data.links))
              .force("charge", d3.forceManyBody().strength(-300))
              .force("center", d3.forceCenter(250, 150))
              .on("tick", ticked);
    
            // 綁定拖曳事件
            dots.call(
              d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended)
            );
    
            function dragstarted(event, d) {
              d3.select(this).style("fill", "pink");
              d.fx = d.x;
              d.fy = d.y;
              // 停止後需要重新開始
              simulation.alphaTarget(0.03).restart();
            }
    
            function dragged(event, d) {
              d.fx = event.x;
              d.fy = event.y;
            }
    
            function dragended(event, d) {
              d3.select(this).style("fill", "green").style("opacity", 0.6);
    
              d.fx = null;
              d.fy = null;
            }