+53

Hướng dẫn làm mấy cái chấm bay bay và có dây nối nó với nhau và nối với chuột bằng Canvas

Mở đầu

Sau vài bài thì tôi nhận ra là mềnh hông có khiếu viết mấy bài giới thiệu, phân tích, nên là tôi lại quay lại viết mấy bài hướng dẫn làm mấy thứ làng nhàng vui vui vậy.

Hôm nay hướng dẫn ae làm cái ae làm cái mấy cái chấm bay lung tung, khi di chuột qua thì nó có cái tơ nối với con trỏ nhé, tôi gọi nó là connecting đốt-s

Thực ra là lúc bắt đầu, tôi cũng phân vân có nên làm cái random ra 108 câu tỏ tình hiệu ứng lung linh cực đẹp để ae dùng luôn dịp noen này luôn không, nhưng thấy đài báo bảo giờ đang thừa nam thiếu nữ, ae tỏ tình thành công thì tôi cũng vui, nhưng nguy cơ ế thì gần thêm một đoạn, nên là đợi tôi có người yêu rồi sẽ hỗ trợ ae sau vậy.

Trông như vậy này

Như các bạn có thể thấy, một thứ rất hay, rất thú vị nhưng vô dụng

Bắt đầu nào!

Bắt đầu thực hiện

Step 1: HTML + CSS

Một dòng HTML và CSS thôi, nhưng vì đoạn sau cũng sẽ chia ra nên tôi cũng chia đoạn này ra luôn

<canvas id="canvas"></canvas>
<style>
    canvas {background: #000}
</style>

Step 2: Khai báo canvas

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext('2d');

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

Như các bạn thấy, tôi tạo kích thước rộng, dài của canvas trong js thay vì dùng css ở trên. Nhìn thì tưởng giống nhau, nhưng có chút khác biệt, css nhận định các selector như nhau, còn js sẽ nhận địch canvas là 1 thuộc tính ảnh, nên khi đẩy chiểu rộng, dài vào, nó sẽ dùng attribute widthheight, chính cái này mới là cái xác định của kích thước thật sự của canvas.

Step 3: Khởi tạo mảng "sao"

var stars = [],
    number = 300,
    mouse = {
      x: 0,
      y: 0
    };

// khởi tạo từng sao
for (var i = 0; i < number; i++) {
  stars.push({
    x: Math.random() * canvas.width,
    y: Math.random() * canvas.height,
    radius: Math.random() * 1 + 1,
    vx: Math.floor(Math.random() * 50) - 25,
    vy: Math.floor(Math.random() * 50) - 25,
    speed: Math.floor(Math.random() * 41) - 10,
  });
}

number là số lượng sao.

mouse là vị trí của con trỏ, chúng ta cần nó để có thể tạo cái tơ nối các sao xung quang con trỏ tới nó

xy là vị trị tri ban đầu của sao

radius là kích thước của chấm sao

vxvy là hướng di chuyển của sao, kiểu kẻ vector ấy

speed tất nhiên là tốc độ của sao rồi, đặt số cố định thì các sao sẽ chạy cũng tốc độ, đây tôi đặt random tốc độ từ 50->10, càng nhỏ thì chạy càng nhanh nhé

Bonus thêm cho mn thông thức lấy số random trong 1 khoảng này: Math.random() * (max - min + 1) + min)

Step 4: Chuẩn bị

Không có gì đặc biệt, tạo cái hàm tính khoảng cách 2 điểm và lấy vị trị của chuột thôi. Hình như có cái hàm để tính luôn, nhưng ngồi 1 lúc không nhớ ra nên tôi dùng luôn bitato Pythagoras cho nhẹ đầu, là cái định lý "bình phương cạnh huyền tam giác vuông bằng tổng bình phương các cạnh góc vuông" trong trường hợp bạn không nhớ =))

function distance( point1, point2 ){
  var xs = 0;
  var ys = 0;

  xs = (point2.x - point1.x) ** 2;
  ys = (point2.y - point1.y) ** 2;

  return Math.sqrt( xs + ys );
}

canvas.addEventListener('mousemove', function(e){
  mouse.x = e.clientX;
  mouse.y = e.clientY;
});

Step 5: Vẽ bầu trời sao

Đây là tạo sao này~

function drawStars() {
  stars.map((star) => {
    ctx.beginPath();
    ctx.fillStyle = "#fff";
    ctx.arc(star.x, star.y, star.radius, 0, 2 * Math.PI);
    ctx.fill();
  });
}

arc là để vẽ vòng tròn, tâm ở tọa độ star.xstar.y , bán kính là star.radius

Tiếp theo là vị trí bắt đầu vẽ và vị trí kết thúc nét vẽ, 0 là vị trí hướng 3h, vẽ xuôi chiều kim đồng hồ, 6h là 0.5PI, 9h là 1PI... là lượng giác ấy =)))

Giờ gặp thằng nhóc nào bảo "Cháu muốn là lập trình viên/ lập trình game/ làm hacker" mà không chịu học toán thì ae cứ đấm nó mấy phát cho tỉnh cơn mơ. Quay được về quá khứ, thì tôi cũng muốn đấm mình nữa 😂

Quay lại vấn đề, fillStyle là chọn màu và fill là đổ màu vào, nếu các bạn dùng stroke thay cho fill thì nó sẽ chỉ vẽ ra một vòng tròn thôi, viền thì có màu, nhưng bên trong thì không.

Đây là lối các ông sao với nhau này~

function drawLines() {
  stars.map((starI) => {
    ctx.moveTo(starI.x, starI.y);
    if (distance(mouse, starI) < 250) ctx.lineTo(mouse.x, mouse.y);

    stars.map((starII) => {
      if (distance(starI, starII) < 50) ctx.lineTo(starII.x, starII.y);
    });
  });
  ctx.lineWidth = 0.1;
  ctx.strokeStyle = "white";
  ctx.stroke();
}

Tôi đặt khoảng cách các sao đến mouse là 250, và các sao nỗi với nhau trong khoảng 50px. Bạn nên để khoảng cách này là biến, khai báo luôn đoạn bước 3 nay trên ấy, vậy cho dễ chỉnh sửa, ở đây, tôi viết luôn đây là do quên thích =))

Độ dày của line cũng không nên quá lớn, 0.2 trở xuống là đẹp, tôi để 0.1 cho dễ nhìn thôi, chứ 0.05 nó mới chuẩn.

Giờ thì vẽ bầu trời sao

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  ctx.globalCompositeOperation = "lighter";

  drawStars();

  ctx.beginPath();
  drawLines();
}

clearRect là vẽ hình chữ nhật, là cái nền ấy đen ấy, bắt đầu tại vị trị 0, 0 và có chiều rộng là canvas.width, chiều dài là canvas.height

globalCompositeOperation là để giúp chúng ta xử lý việc các bản vẽ trên canvas sẽ chèn đè, xếp lớp thế nào, ở đây chúng ta muốn "sao" trên nền "trời", mà thường thì bản vẽ sau sẽ đè lên bản vẽ trước thôi, nhưng cứ thêm vào cho chắc =))

ctx.globalCompositeOperation = "lighter" nghĩ là nó sẽ trông vậy canvas lighter 2 màu sẽ được trộn lại với nhau.

Sao lại dùng cái globalCompositeOperation này hả?

I've no idea. 😎

Bỏ đi, tôi thấy nó vẫn chạy, nhưng thêm vào thì trông code nguy hiểm hơn 😌

Step 6: Update vị trị sao

function update() {
  stars.map((star) => {
    star.x += star.vx / star.speed;
    star.y += star.vy / star.speed;

    if (star.x < 0 || star.x > canvas.width) star.vx = -star.vx;
    if (star.y < 0 || star.y > canvas.height) star.vy = -star.vy;
  });
}

function này sẽ update vị trị của từng sao, trong trường hợp sao chạm vào cạnh của khung canvas (đoạn if ấy) thì sẽ nảy ngược lại bằng 1 góc bằng với góc vào, như cái gương ấy, là cái hàm đồ thị bậc 1 thôi =))

Final

function tick() {
  draw();
  update();
  requestAnimationFrame(tick);
}

tick();

cuối cùng là tổng hợp những gì đã làm và thêm cái requestAnimationFrame để có thể chạy được.

Kết

Cái connecting dots này bạn có thể dùng nó làm nền cho website tăng độ "nguy hiểm" khi chào mời khách, nhưng performance sẽ giảm kha khá nhé.

Source code các bạn có thể xem theo link tôi để ngay dưới đây.

Chúc ae thành công và qua được mùa noen lạnh lẽo 😔

souce code: https://codepen.io/hungba124/pen/ZEbxKjw


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí