Ví dụ

Trong ví dụ này, chúng ta dùng XHTML, SVG, JavaScriptDOM để tạo chuyển động cho một bầy "mote". Những mote này chịu sự điều khiển bởi hai nguyên tắc cơ bản. Thứ nhất, mỗi mote cố gắng di chuyển về phía con trỏ chuột, và thứ hai, mỗi mote cố gắng di chuyển ra xa vị trí trung bình của bầy mote. Kết hợp lại, ta có được một hành vi rất tự nhiên.

Xem ví dụ. Ví dụ được liên kết này được viết theo các thực hành tốt nhất năm 2006. Ví dụ bên dưới đã được cập nhật theo các thực hành tốt nhất của JavaScript hiện đại. Cả hai đều hoạt động.

xml
<?xml version='1.0'?>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:svg="http://www.w3.org/2000/svg">
  <head>
  <title>Một bầy mote</title>
  <style>
  <![CDATA[
label,
input {
  width: 150px;
  display: block;
  float: left;
  margin-bottom: 10px;
}
label {
  text-align: right;
  width: 75px;
  padding-right: 20px;
}
br {
  clear: left;
}
  ]]>
  </style>
  </head>
  <body onload='update()'>
    <svg:svg id='display' width='400' height='300'>
      <svg:circle id='cursor' cx='200'
cy='150' r='7' fill='blue' fill-opacity='0.5'/>
    </svg:svg>

    <p>Một bầy mote, được chi phối bởi hai nguyên tắc cơ bản.
    Thứ nhất, mỗi mote cố gắng di chuyển về phía con trỏ, và
    thứ hai, mỗi mote cố gắng di chuyển ra xa vị trí trung bình
    của bầy mote. Kết hợp lại, ta có được một hành vi trông rất
    tự nhiên.
    </p>

    <div>
    (C) 2006 <a id='email-me' href='#'>Nick Johnson</a>

    <script>
    <![CDATA[
      // ngăn bot spam
      let email = "@riovia.net";
      email = "nick" + email;
      document.getElementById("email-me").href = "mailto:" + email;
    ]]>
    </script>
    Phần mềm này hoàn toàn miễn phí để bạn sử dụng theo bất kỳ cách nào,
    và không đi kèm bất kỳ bảo hành nào.
    </div>

    <form action="" onsubmit="return false;">
      <p>
      <label>Số lượng mote:</label>
        <input id='num_motes' value='5'/>
        <br/>

      <label>Vận tốc tối đa:</label>
        <input id='max_velocity' value='15'/>
        <br/>

      <label>Lực hút về con trỏ:</label>
        <input id='attract_cursor' value='6'/>
        <br/>

      <label>Lực đẩy từ các mote khác:</label>
        <input id='repel_peer' value='5'/>
        <br/>
      </p>
    </form>

  <script>
  <![CDATA[
    // Mảng các mote
    let motes;

    // Lấy phần tử hiển thị.
    function Display() {
      return document.getElementById("display");
    }

    // Xác định kích thước của phần tử hiển thị.
    // Trả về dưới dạng bộ đôi (x,y) trong một mảng
    function Dimensions() {
      // Phần tử kết xuất của chúng ta
      const display = Display();
      const width = parseInt(display.getAttributeNS(null, "width"), 10);
      const height = parseInt(display.getAttributeNS(null, "height"), 10);

      return [width, height];
    }

    // Hàm này được gọi bởi sự kiện di chuyển chuột
    const mouse_x = 200;
    const mouse_y = 150;
    function OnMouseMove(evt) {
      mouse_x = evt.clientX;
      mouse_y = evt.clientY;

      const widget = document.getElementById("cursor");
      widget.setAttributeNS(null, "cx", mouse_x);
      widget.setAttributeNS(null, "cy", mouse_y);
    }
    document.onmousemove = OnMouseMove;

    // Xác định (x,y) của con trỏ
    function Cursor() {
      return [mouse_x, mouse_y];
    }

    // Xác định vị trí trung bình (x,y) của bầy mote
    function AverageMotePosition() {
      if (!motes || motes.length === 0) {
        return [0, 0];
      }

      const sum_x = 0;
      const sum_y = 0;
      for (const mote of motes) {
        sum_x += mote.x;
        sum_y += mote.y;
      }

      return [sum_x / motes.length, sum_y / motes.length];
    }

    // Một bộ sinh số ngẫu nhiên gọn hơn, trả về số nguyên
    function Rand(modulo) {
      return Math.round(Math.random() * (modulo - 1));
    }

    // Lớp Mote
    function Mote() {
      // Kích thước của vùng vẽ.
      const dims = Dimensions();
      const width = dims[0];
      const height = dims[1];

      // Chọn một tọa độ ngẫu nhiên để bắt đầu.
      this.x = Rand(width);
      this.y = Rand(height);

      // Vận tốc ban đầu bằng 0.
      this.vx = this.vy = 0;

      // Một phần tử trực quan, ban đầu là null
      this.elt = null;
    }

    // Biến nó thành một lớp.
    new Mote();

    // Mote::applyForce() — Điều chỉnh vận tốc
    // hướng về vị trí đã cho.
    // Cảnh báo: đây là giả vật lý — không thực sự
    // tuân theo bất kỳ nguyên lý vật lý /thực/ nào.
    Mote.prototype.applyForce = function (pos, mag) {
      if (pos[0] > this.x) {
        this.vx += mag;
      } else if (pos[0] < this.x) {
        this.vx -= mag;
      }

      if (pos[1] > this.y) {
        this.vy += mag;
      } else if (pos[1] < this.y) {
        this.vy -= mag;
      }
    };

    // Mote::capVelocity() — Áp dụng giới hạn trên
    // cho vận tốc của mote.
    Mote.prototype.capVelocity = function () {
      const max = parseInt(document.getElementById("max_velocity").value, 10);

      if (max < this.vx) {
        this.vx = max;
      } else if (-max > this.vx) {
        this.vx = -max;
      }

      if (max < this.vy) {
        this.vy = max;
      } else if (-max > this.vy) {
        this.vy = -max;
      }
    };

    // Mote::capPosition() — Áp dụng giới hạn trên/dưới
    // cho vị trí của mote.
    Mote.prototype.capPosition = function () {
      const dims = Dimensions();
      if (this.x < 0) {
        this.x = 0;
      } else if (this.x >= dims[0]) {
        this.x = dims[0] - 1;
      }

      if (this.y < 0) {
        this.y = 0;
      } else if (this.y >= dims[1]) {
        this.y = dims[1] - 1;
      }
    };

    // Mote::move() — di chuyển một mote, cập nhật màn hình.
    Mote.prototype.move = function () {
      // Áp lực hút về con trỏ.
      const attract = parseInt(document.getElementById("attract_cursor").value, 10);
      const cursor = Cursor();
      this.applyForce(cursor, attract);

      // Áp lực đẩy ra khỏi vị trí trung bình của bầy mote.
      const repel = parseInt(document.getElementById("repel_peer").value, 10);
      const average = AverageMotePosition();
      this.applyForce(average, -repel);

      // Thêm một chút ngẫu nhiên vào vận tốc.
      this.vx += Rand(3) - 1;
      this.vy += Rand(3) - 1;

      // Đặt giới hạn trên cho vận tốc.
      this.capVelocity();

      // Áp dụng vận tốc.
      const old_x = this.x;
      const old_y = this.y;
      this.x += this.vx;
      this.y += this.vy;
      this.capPosition();

      // Vẽ nó.
      if (this.elt === null) {
        const svg = "http://www.w3.org/2000/svg";
        this.elt = document.createElementNS(svg, "line");
        this.elt.setAttributeNS(null, "stroke", "green");
        this.elt.setAttributeNS(null, "stroke-width", "3");
        this.elt.setAttributeNS(null, "stroke-opacity", "0.5");
        Display().appendChild(this.elt);
      }

      this.elt.setAttributeNS(null, "x1", old_x);
      this.elt.setAttributeNS(null, "y1", old_y);

      this.elt.setAttributeNS(null, "x2", this.x);
      this.elt.setAttributeNS(null, "y2", this.y);
    };

    function update() {
      // Lần gọi đầu tiên?
      if (!motes) {
        motes = [];
      }

      // Cần có bao nhiêu mote?
      let num = parseInt(document.getElementById("num_motes").value, 10);
      if (num < 0) {
        num = 0;
      }

      // Đảm bảo chúng ta có đúng số lượng đó...
      // Ít hơn?
      while (motes.length < num) {
        motes.push(new Mote());
      }

      // Hay nhiều hơn?
      if (num === 0) {
        motes = [];
      } else if (motes.length > num) {
        motes = motes.slice(0, num - 1);
      }

      // Di chuyển một mote ngẫu nhiên
      if (motes.length > 0) {
        motes[Rand(motes.length)].move();
      }

      // Và làm lại sau 1/100 giây
      setTimeout(() => update(), 10);
    }
  ]]>
  </script>
  </body>
</html>