ロゴ メインコンテンツへ
RSSフィード
「Web 開発」に関連する記事一覧

d3.js ドラッグ可能で物理シミュレーションするノードサンプル2

2023/10/22
(この記事の文字数: 121)

d3.js でドラッグ可能で物理シミュレーションするノードのサンプルとして、女の子がバルーンを持っている絵を描画するプログラムを作ってみました。女の子をドラッグすると風船もついてきます。

ソースコードです。画像URL以外はコピペで動くと思います。

  <svg width="400" height="400"></svg>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"
    integrity="sha512-M7nHCiNUOwFt6Us3r8alutZLm9qMt4s9951uo8jqO4UwJ1hziseL6O3ndFyigx6+LREfZqnhHxYjKRJ8ZQ69DQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script>
    const width = document.querySelector("svg").clientWidth;
    const height = document.querySelector("svg").clientHeight;
    const nodeCount = 21;
    const nodesData = [];
    const imageWidth = 100;
    const imageHeight = 100;

    class Node {
      constructor(index, x, y, radius, width, height, href, imageOffsetX, imageOffsetY,
        forceRateY = 1.0
      ) {
        this.index = index;
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.width = width;
        this.height = height;
        this.href = href;
        this.imageOffsetX = imageOffsetX;
        this.imageOffsetY = imageOffsetY;
        this.forceRateY = forceRateY;
      }
    }

    nodesData.push(new Node(0,
      width * 0.5, height - 100,
      1,
      100, 100,
      "blog/images/family_fusen_boy_girl.png",
      70, 40,
      -0.4
    ));
    const radiusRandRate = 0.1;
    const radiusMax = 60;
    const heightOffset = 100;
    const widthOffset = width * 0.9;
    for (let i = 1; i < nodeCount; i++) {
      const radiusRandWidth = radiusMax * radiusRandRate;
      const radius = (radiusRandWidth * Math.random() + radiusMax - radiusRandWidth) * 0.5;
      const node = new Node(i,
        (width - widthOffset) * Math.random() + widthOffset * 0.5,
        (height - heightOffset) * Math.random(),
        radius,
        0,
        0,
        null,
        0,
        0,
      );
      nodesData.push(node);
    }

    class Link {
      constructor(source, target, distance) {
        this.source = source;
        this.target = target;
        this.distance = distance;
      }
    }

    const linksData = [];
    let i = 0;
    for (let j = i + 1; j < nodeCount; j++) {
      linksData.push(new Link(
        i,
        j,
        Math.random() * 120 + 50 + nodesData[i].radius + nodesData[j].radius)
      );
    }

    const link = d3.select("svg")
      .selectAll("line")
      .data(linksData)
      .enter()
      .append("line")
      .attr("stroke-width", 1)
      .attr("stroke", "gray");
    const colorScale = d3.scaleOrdinal(d3.schemeSet3);
    const node = d3.select("svg")
      .selectAll("circle")
      .data(nodesData)
      .enter()
      .append("circle")
      .attr("r", function (d) { return d.radius })
      .attr("fill", function (d, i) {
        return colorScale(i);
      })
      .attr("stroke", "black")
      .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

    const images = d3.select("svg")
      .selectAll("image")
      .data(nodesData)
      .enter()
      .append("image")
      .attr("href", d => d.href)
      .attr("height", d => d.height)
      .attr("width", d => d.width)
      .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

    const simulation = d3.forceSimulation()
      .force("link",
        d3.forceLink()
          .distance(function (d) { return d.distance; })
          .strength(0.03)
          .iterations(16))
      .force("collide",
        d3.forceCollide()
          .radius(function (d) { return d.radius - d.radius * 0.3; })
          .strength(1.1)
          .iterations(16))
      .force("y",
        d3.forceY()
          .strength(d => d.forceRateY * 0.12)
          .y(0))
      ;

    simulation
      .nodes(nodesData)
      .on("tick", ticked);

    simulation.force("link")
      .links(linksData)
      .id(function (d) { return d.index; });

    function ticked() {
      link
        .attr("x1", function (d) { return d.source.x; })
        .attr("y1", function (d) { return d.source.y; })
        .attr("x2", function (d) { return d.target.x; })
        .attr("y2", function (d) { return d.target.y; });
      node
        .attr("cx", function (d) { return d.x; })
        .attr("cy", function (d) { return d.y; });

      images
        .attr("x", d => d.x - d.imageOffsetX)
        .attr("y", d => d.y - d.imageOffsetY);
    }

    function dragstarted(e, d) {
      if (!e.active) simulation.alphaTarget(0.3).restart();
      d.fx = d.x;
      d.fy = d.y;
    }

    function dragged(e, d) {
      d.fx = e.x;
      d.fy = e.y;
    }

    function dragended(e, d) {
      if (!e.active) simulation.alphaTarget(0);
      d.fx = null;
      d.fy = null;
    }
  </script>

  このエントリーをはてなブックマークに追加  

<<「Web 開発」の記事一覧に戻る

コメント(0 件)



コンテンツロード: 0.0076 sec
Copyright(C)2006-2024 puarts All Rights Reserved