动画&交互&P5.js

一、背景

编程语言:p5.js,p5.js是JavaScript的程式库,它起源于processing的原旨。

p5.j​​s 看起来与 Processing 非常相似,但有一些变化:

  • 因为 size() 已经被 createCanvas() 代替,所以我们的草图不仅仅是画布,还能创建其他元素。
  • frameRate(num) 设置帧速率,但该 frameRate 变量已被删除。要获取当前帧速率,请用 frameRate() 不带参数调用。
  • JavaScript 并不总能同步加载,下面有几个方法可解决这个问题: 
    ① 所有的加载方法都有一个可选的回调参数。也就是在文件加载完后调用的函数。 
    ② 另外,我们可以将加载调用放在 preload() 函数中,其发生在 setup() 之前。如果存在 preload() 函数,则 setup() 函数会等待,直到加载完所有内容为止。
  • 变量 mousePressed 已替换成 mouseIsPressed 。
  • 除鼠标事件外,还有触摸事件,映射如下所示: 
    ① mouseX 〜 touchX 
    ② mouseY 〜 touchY 
    ③ mousePressed() 〜 touchStarted() 
    ④ mouseDragged() 〜 touchMoved() 
    ⑤ mouseReleased() 〜 touchEnded() 
    ⑥ 有一个 touches[] 数组包含一系列对象,其中 x 和 y 属性对应于每个手指的位置。
  • push/popMatrix() 、push/popStyle() 已替换为 push() 和 pop() ,这相当于同时调用 matrix 和 style 方法。
  • 默认情况下,所有内容都处于全局命名空间中,并且你可以像在 Processing 中一样创建草图。我们称之为 “instance mode” ,用于创建一个 p5 草图,其可与你网页上运行的其他代码一起播放。
  • 在全局模式下,p5 变量和函数名不可用于 setup(),draw(),mousePressed(),等之外(除非它们被放置在由这些方法之一调用的函数内)。这意味着如果你想使用 p5 函数,当在 setup() 之前声明变量时,你需要在 setup() 里面赋值。

参考书籍:中文名《代码本色–用编程模拟自然系统》,英文名是《The Nature of Code》,作者是纽约大学 Tisch 艺术学院艺术教授 Daniel Shiffman。该书用 processing 语言(作者也用其他语言做了相同的事情,可以看作者 github)模拟自然系统,例如模拟加速度、地球引力、粒子系统等等,看起来也蛮有意思。

二、创意

动画&交互&P5.js_第1张图片

动画&交互&P5.js_第2张图片

 

三、功能

鼠标点击画面,画面颜色会随之改变。富有流动性。

粒子系统:使用了《代码本色》一书中Ch4中的粒子效果。

动画&交互&P5.js_第3张图片

交互:鼠标点击,切换线条颜色。 

四、技术


var particles = [];
var nums;
var particleDensity = 4000;
var noiseScale = 800;
var maxLife = 10;
var	simulationSpeed = 0.2;
var fadeFrame = 0;
var backgroundColor;
var visualMode = 0;
var numModes = 4;
var invertColors = false;

function setup(){
	nums = windowWidth * windowHeight / particleDensity;
	backgroundColor = color(20, 20, 20);
	createCanvas(windowWidth, windowHeight);
	background(backgroundColor);
	for(var i = 0; i < nums; i++){
		particles[i] = new Particle();
	}
}

function draw(){
	noStroke();
	
	++fadeFrame;
	if(fadeFrame % 5 == 0){
		if(invertColors){
			blendMode(ADD);
		} else {
			blendMode(DIFFERENCE);
		}
		fill(1, 1, 1);
		rect(0,0,width,height);

		if(invertColors){
			blendMode(DARKEST);
		} else {
			blendMode(LIGHTEST);
		}
		fill(backgroundColor);
		rect(0,0,width,height);
	}
	
	blendMode(BLEND);
	smooth();
	for(var i = 0; i < nums; i++){
		var iterations = map(i,0,nums,5,1);
		var radius = map(i,0,nums,2,6);
		
		particles[i].move(iterations);
		particles[i].checkEdge();
		
		var alpha = 255;
		var particleColor;
		var fadeRatio;
		fadeRatio = min(particles[i].life * 5 / maxLife, 1);
		fadeRatio = min((maxLife - particles[i].life) * 5 / maxLife, fadeRatio);
		var colorCase = visualMode;
		if(visualMode == 0)
		{
			colorCase = int(particles[i].pos.x / width * 3) + 1;
		}
		switch(colorCase)
		{
			case 1:
				var lifeRatioGrayscale = min(255, (255 * particles[i].life / maxLife) + red(backgroundColor));
				particleColor = color(lifeRatioGrayscale, alpha * fadeRatio);
				break;
			case 2:
				particleColor = particles[i].color;
				break;
			case 3:
				particleColor = color(blue(particles[i].color) + 70, green(particles[i].color) + 20, red(particles[i].color) - 50);
				break;
		}
		if(invertColors){
			particleColor = color(255 - red(particleColor), 255 - green(particleColor), 255 - blue(particleColor));
		}
		fill(red(particleColor), green(particleColor), blue(particleColor), alpha * fadeRatio);
		particles[i].display(radius);
	} 
}

function Particle(){
// member properties and initialization
	this.vel = createVector(0, 0);
	this.pos = createVector(random(0, width), random(0, height));
	this.life = random(0, maxLife);
	this.flip = int(random(0,2)) * 2 - 1;
	var randColor = int(random(0,3));
	switch(randColor)
	{
		case 0:
			this.color = color(110,57,204);
			break;
		case 1:
			this.color = color(7,153,242);
			break;
		case 2:
			this.color = color(255,255,255);
			break;
	}
	
// member functions
	this.move = function(iterations){
		if((this.life -= 0.01666) < 0)
			this.respawn();
		while(iterations > 0){
			var angle = noise(this.pos.x/noiseScale, this.pos.y/noiseScale)*TWO_PI*noiseScale*this.flip;
			this.vel.x = cos(angle);
			this.vel.y = sin(angle);
			this.vel.mult(simulationSpeed);
			this.pos.add(this.vel);
			--iterations;
		}
	}

	this.checkEdge = function(){
		if(this.pos.x > width || this.pos.x < 0 || this.pos.y > height || this.pos.y < 0){
			this.respawn();
		}
	}
	
	this.respawn = function(){
		this.pos.x = random(0, width);
		this.pos.y = random(0, height);
		this.life = maxLife;
	}

	this.display = function(r){
		ellipse(this.pos.x, this.pos.y, r, r);
	}
}

function advanceVisual()
{
	visualMode = ++visualMode % numModes;
	if(visualMode == 0){
		invertColors = !invertColors;
		backgroundColor = invertColors ? color(235, 235, 235) : color(20, 20, 20);
	}
	noiseSeed(random()*Number.MAX_SAFE_INTEGER);
	background(backgroundColor);
	for(var i = 0; i < nums; i++){
		particles[i].respawn();
		particles[i].life = random(0,maxLife);
  }
}

function keyPressed()
{
	advanceVisual();
}

function touchStarted()
{
	advanceVisual();
}

 

你可能感兴趣的:(动画&交互&P5.js)