#21-用Canvas做科技感的动态球!(+什麽时候该用CSS/SVG/Canvas?)

今天正式进入Canvas的世界了!
老样子先看成品:
今天来做点科技感的画面,橘色是滑鼠的游标,这个是满常看到的:

但在这之前,想先谈一下到底是什麽时候该用SVG,或是Canvas呢?
如果想赶快看code的,就请自行跳过下一环节~


动态: CSS/SVG/Canvas 都几?

之前在六角的直播上看到P5.js的直播,传送门
其中讲者分享了这一篇日文文章)
讲述在制作动画时, 该选择 CSS , SVG 还是 Canvas。

我再把作者的概念整理了一下:
https://ithelp.ithome.com.tw/upload/images/20211007/20140247HclCK8I3rC.png

总之就是CSS&SVG都做不到的时候就用Canvas啦 XDD
不过还满清楚的就是~
CSS就是个直来直往的单脑门家伙,只能给他直球(直线&圈圈)
SVG是个心思细腻的抖S,所以最适合S曲线
Canvas像是个无所不能的完美情人,可以做到整体的变形~

不过Canvas有个缺点就是不能选DOM
(接受我的全部不然就拉倒)

大家之後做动画也可以参考下~


科技感动态拆解!

我是个偷吃步的人嘿嘿,
今天主要参考的教学影片在这里
和这里

JS主要Function和相互关系:

1.点点制造机:构造函数模型做点点!包含以下函式:

  • update—>更新座标 让球球移动,然後呼叫border & draw
  • border—>判断是不是跑超过边缘了,超过的话让他换个方向
  • draw—>画球球

2.画线:让点跟其他点连在一起!

  • 2.1 取得距离
  • 2.2 取得rgba 数字,再加上算好的透明度,让距离越近,线越深

3.开始绘制 init:呼叫点点制造机,然後呼叫4.的重复绘制

4.重复绘制:一直画点点,重复呼叫自己

  • 4.1 画点点
  • 4.2 画滑鼠的点点
  1. 滑鼠移动监听器

来看code吧!

//JS
//这边都先宣告全域变数
var w, h, loopId, id, canvas, ctx, particles, mouseParticle, color;

const options = {
  mouseColor : 'rgba(255, 165, 0)', //滑鼠的颜色,让他不一样
  particleColor: "rgb(255,255,255)",
  lineColor: "rgb(0,181,255)",
  particleAmount: 50, //想要多少点点
  defaultRadius: 2,
  variantRadius: 2,
  defaultSpeed: 1,
  variantSpeed: 1,
  linkRadius: 300,
};

const mouse = {
  x: null,
  y: null,
}

//5. 监听器
window.addEventListener('mousemove', function(e){
  mouse.x = event.x;
  mouse.y = event.y;
})


//3. 开始绘制
function init(){

	//canvas起手式
  canvas = document.getElementById('canvas');
  ctx = canvas.getContext('2d');
	//让画布和视窗一样宽高
  h = canvas.height = window.innerHeight;
  w = canvas.width = window.innerHeight;
	
	//点点制造机
  particles = [];
  for(var i = 0; i < options.particleAmount; i++){
    particles.push(new Particle());
  }
  mouseParticle = new Particle(true);
  animationLoop();
}

//4.重复绘制
function animationLoop(){
  ctx.clearRect(0,0,w,h); //要先清除画布~才画新的
  drawParticle();
  drawMouseParticle();
  requestAnimationFrame(animationLoop); //一直叫自己
}

//4.1 画点点
function drawParticle(){
  for (var i = 0; i < particles.length; i++){
    particles[i].update(); //更新位置
    linkPoints(particles[i], particles); //画线
  }
}

//4.2 画滑鼠的点点
function drawMouseParticle(){
  mouseParticle.update(true);
  const isMouse = true;
  linkPoints(mouseParticle, particles, isMouse);
}

//2. 画线
//point就是特定一点,hubs就是全部的点
function linkPoints(point, hubs, isMouse){
  for (var i = 0; i < hubs.length; i ++){
		//计算目前的距离
    var distance = checkDistance(point.x, point.y, hubs[i].x, hubs[i].y);
		
		//以我们定义好的options.linkRadius为一个评量单位,算出距离的一个介於0~1的衡量单位
		//将透明度定为衡量单位的指标
		//正数-->距离越近,opacity越大,线越明显。负数-->就不画线
    var opacity = 1 - distance / options.linkRadius;

    if(opacity > 0){
			//画线宽度
      ctx.lineWidth = 0.5;
      
			//将评量单位当成透明度画线
      ctx.strokeStyle = `rgba(${getRgbNumber(options.lineColor)[0]},${getRgbNumber(options.lineColor)[1]}, ${getRgbNumber(options.lineColor)[2]}, ${opacity})`;
      
      //如果是滑鼠那一个点的话,就用指定的颜色就好
			if(isMouse) ctx.strokeStyle = options.mouseColor;
      ctx.beginPath();
      ctx.moveTo(point.x, point.y);
      ctx.lineTo(hubs[i].x, hubs[i].y);
      ctx.closePath();
      ctx.stroke();
    }
  }
}

//2.1 判断距离
function checkDistance(x1, y1, x2, y2){
  //就是找直角三角形的斜边距离呀
  //pow-->乘幂,这里做2次方, 找sqrt平方根
  return Math.sqrt(Math.pow(x2 - x1, 2)+ Math.pow(y2 - y1, 2))
}

//2.2. 取得颜色的数字
function getRgbNumber(color){
  //让里面的色彩变成阵列
  //正规表达式//是叙述起手式
  //\d --> 吻合数字,写法等同於 [0-9] 
  // + --> 匹配前一字元 1 至多次
  // /g -->全部搜寻回传全部结果
  return color.match(/\d+/g);
}

// 1. 构造函数模型做点点!
Particle = function(isMouse){
  //多做一个isMouse判断是不是滑鼠的点
  this.isMouse = isMouse ? isMouse : null;
  this.x = isMouse ? mouse.x : Math.random() * w;
  this.y = isMouse ? mouse.y : Math.random () * h;
  this.color = options.particleColor;
  this.radius = isMouse ? 4 : options.defaultRadius * Math.random() * options.variantRadius;
  
	//这边是要前进的方向和速度
  this.speed = options.defaultSpeed* Math.random() * options.variantSpeed;
  
  //将想要的角度算成弧度才能丢到Math.cos()& Math.sin()里面算出将要移动的距离
	this.directionAngle = Math.floor(Math.random()*360);
    
      this.vector = {
        //是要丢弧度进去,2*PI/360*角度    
        x : Math.cos(this.directionAngle) * this.speed,
        y : Math.sin(this.directionAngle) * this.speed
      }
  
  this.update = function(){
    this.border();
    if(this.isMouse){
      this.x = mouse.x;
      this.y = mouse.y;
    }else{
      this.x += this.vector.x;
      this.y += this.vector.y;
    }
    this.draw();
  }
  
  this.border = function(){
  //超过边界时往反方向跑 
    if(this.x >= w || this.x <= 0){
      this.vector.x *= -1
    }
    if(this.y >= h || this.y <= 0){
      this.vector.y *= -1
    }
  }
  
  this.draw = function(){
    ctx.beginPath();
    //画个圆点点~(起始点,结束点,半径,起始角度,结束角度)
    ctx.arc(this.x, this.y, this.radius , 0, Math.PI * 2);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill(); 
  }
}

init();


以上!

要考虑的事情很多,满复杂的 XD
如果有更简单的写法也请让我知道!
今天的code在这里
请多多指教!


<<:  Day23 jQuery 基本教学(三)

>>:  Day25

Day26:TabView

前言 今天来设置 RecipeList APP 的 tab。 实作 我将要做两个选项卡, 第一个是一...

DAY9: 验证码辨识(二)

大家好,昨天我们把图片抓下来之後也标记完了(我个人是用了10000张图片),接下来就是丢进模型训练啦...

DAY02随机森林

首先,先从比较常见的机器学习方法开始,也就是随机森林方法,帮大家快速讲解一下大概(因为主要目的是在写...

伸缩自如的Flask [day14] 档案下载 及 其他传值方法

从官网的攻略介绍来看,因为安全考量,所以平常都应该使用send_from_directory(),而...

Day 24 尽情的自我发挥吧!

2021/10/5是我在铁人赛发文到达200篇的日子,这四年多来,不知不觉的就这样累积了200篇的文...