p5.js码绘:未来风格/科技感的线库
前言
本文记载的是自己用p5.js绘制一些有科技感的线条/图形的过程
一、基本元素选定
1、颜色:
这里我选用了三种颜色作为基调色,分别是:
- 青色 RBG(0,255,255).
- 白色 RGB(255,255,255).
- 黑色 RGB(0,0,0).
黑色作为背景色,青色和白色绘制线条,效果如下图:
2、元素:
为了表现科技感我查阅了一些相关的资料,也找了很多图片,这里放几张。
不难发现科技感的表现主要是从色调(多以蓝色、暗蓝色为主)、透明度(半透明)、几何元素(多为有棱角的几何形状)这三个方面来表现。从这些图片中提取一些共有的元素作为创作素材:
- 网格/矩形
- 粒子链
- 电路图
- 蜂巢结构
二、绘制线条
1.matrix1Line
函数原型: line_matrix1_0126(x0,y0,x1,y1,randness,res)
参数说明: x0,y0为线条的起点坐标,x1为终点横坐标,y1为线宽,randness为随机数限制因子,res为分段数(值越大线条越复杂)。该线条只能放置在水平方向。
function line_matrix1_0126(x0,y0,x1,y1,randness,res)
{
var xp = x0;
var yp = y0;
for(var i=1;i<res;i++)
{
var t = i/res;
var xt = lerpAB(x0,x1,t);
var yt = lerpAB(y0,y1,t);
var biasX = random(-randness,randness);
var biasY = random(-randness,randness);
var x = xt + biasX;
var y = yt + biasY;
let lineWeight=random(2,3);
strokeWeight(lineWeight);
line(xp,yp,x,yp);
line(x,yp,x,y);
stroke(0,255,255);
line(xp,yp,xp,y);
line(xp,y,x,y);
stroke(255);
xp = x;
yp = y;
}
}
静态效果
动态效果
2.matrix2Line
函数原型: line_matrix2_0126(x0,y0,x1,y1,radius,Freq,res)
参数说明:x0,y0为线条的起始坐标,x1,y1为线条的终点坐标,radius为线条宽度的一半,而Freq和res都与线条的复杂程度以及形状相关。
function line_matrix2_0126(x0,y0,x1,y1,radius,Freq,res)
{
var theta = 0;
var xp = x0 + radius * cos(theta);
var yp = y0 + radius * sin(theta);
for(var i=1;i<res;i++)
{
var t = i/res;
var xt = lerpAB(x0,x1,t);
var yt = lerpAB(y0,y1,t);
theta = t*Freq;
var x = xt + radius * cos(theta);
var y = yt + radius * sin(theta);
let lineWeight=random(0,3);
strokeWeight(lineWeight);
line(xp,yp,x,yp);
line(x,yp,x,y);
stroke(0,240,240);
line(xp,yp,xp,y);
line(xp,y,x,y);
stroke(255);
xp = x;
yp = y;
}
}
静态效果:
动态效果(Fre为0-width之间的随机数):
3.circuitryLine
函数原型: line_circuitry_0126(x0,y0,x1,y1,Amp,Freq,res,Wid)
参数说明:x0,y0,x1,y1分别为线条的起始横纵坐标和终点横纵坐标,Amp、Freq为偏移量控制因子,res为分段数,这三者都与线条最终复杂情况有关,Wid为线条宽度的一半。
function line_circuitry_0126(x0,y0,x1,y1,Amp,Freq,res,Wid)
{
var xp = x0 + map(noise(0*Freq),0,1,-1,1);
var yp = y0 + map(noise(0*Freq+10000),0,1,-1,1);
for(var i=1;i<res;i++)
{
var t = i/res;
var xt = lerpAB(x0,x1,t);
var yt = lerpAB(y0,y1,t);
theta = t*Freq;
var x = xt + Amp * map(noise(t*Freq),0,1,-1,1);
var y = yt + Amp * map(noise(t*Freq+10000),0,1,-1,1);
fill(255);
stroke(255);
line(xp,yp,xp,y);
line(xp,y,x,y);
line(xp,yp,x,yp);
line(x,yp,x,y);
rect(xp,yp,8,8);
rect(x-5,y-5,5,5);
let w_size=30;
let h_size=random(Wid,2*Wid);
rect(xp+3*w_size/2-6,yp-h_size/2+1,3,3);
rect(xp+w_size/2+1,yp+h_size/2-6,3,3);
noFill();
rect(xp+w_size/2+1,yp-h_size/2+1,5,20);
rect(xp+3*w_size/2-11,yp+h_size/2-6,10,5);
stroke(0,255,255);
rect(xp+w_size/2,yp-h_size/2,w_size,h_size);
xp = x;
yp = y;
}
}
静态结果:
动态结果:
4.VspringLine
函数原型: line_Vspring_0126(x0,y0,x1,y1,res,Wid)
参数说明:x0,y0分别为线条的起点横纵坐标,x1为线条的终点横坐标,res为分段数,Wid为线宽的一半。该线条也只能放置在水平方向。
function line_Vspring_0126(x0,y0,x1,y1,res,Wid)
{
strokeWeight(2);
stroke(255);
var xp=x0;
var yp=y0;
for(var i=1;i<res;i++)
{
var c1=random(0,2);
if(c1>1){stroke(255);}
else {stroke(0,255,255);}
var plenth=random(20,50);
var wid=random(10,Wid);
ellipse(xp,yp+wid,6,6);
line(xp+3,yp+wid,xp+plenth,yp+wid);
line(xp+plenth,yp+wid,xp+plenth+wid,yp);
ellipse(xp,yp-wid,6,6);
line(xp+3,yp-wid,xp+plenth,yp-wid);
line(xp+plenth,yp-wid,xp+plenth+wid,yp);
line(xp+plenth+wid,yp,xp+2*plenth+wid,yp);
c1=random(0,2);
if(c1>1){stroke(255);}
else {stroke(0,255,255);}
line(xp+plenth+wid-2,yp-10,xp+2*plenth+wid+2,yp-10);
line(xp+plenth+wid-2,yp+10,xp+2*plenth+wid+2,yp+10);
c1=random(0,2);
if(c1>1){stroke(255);}
else {stroke(0,255,255);}
xp=xp+2*plenth+wid;
line(xp,yp,xp+wid,yp+wid);
ellipse(xp+wid+3,yp+wid,6,6);
line(xp,yp,xp+wid,yp-wid);
ellipse(xp+wid+3,yp-wid,6,6);
xp=xp+wid+15;
if(xp>x1){break;}
}
}
静态效果:
动态效果:
5.beehiveLine
函数原型: line_beehive_0126(x0,y0,x1)
参数说明: x0,y0分别表示线条的起点坐标,x1表示线条终点的横坐标。同样该线条只能水平放置,垂直放置需稍做改动。
function line_beehive_0126(x0,y0,x1)
{
strokeWeight(1);
stroke(0,255,255);
let x=x0;
let y=y0;
let radius=20;
for(x=x0;x<x1;x=x+3*radius)
{
var wid=random(1,3.5);
strokeWeight(wid);
noFill();
var c1=random(0,2);
if(c1>1){stroke(255);}
else {stroke(0,255,255);}
beginShape();
for(var i=0;i<7;i++)
{
vertex(x+radius*cos(i*PI/3),y+radius*sin(i*PI/3));
}
endShape(CLOSE);
c1=random(0,2);
if(c1>1){stroke(255);}
else {stroke(0,255,255);}
beginShape();
for(var i1=0;i1<7;i1++)
{
vertex(x+radius*cos(i1*PI/3)+3*radius/2,y+radius*sin(i1*PI/3)+radius*cos(60));
}
endShape(CLOSE);
c1=random(0,2);
if(c1>1){stroke(255);}
else {stroke(0,255,255);}
beginShape();
for(var i2=0;i2<7;i2++)
{
vertex(x+radius*cos(i2*PI/3)+3*radius/2,y+radius*sin(i2*PI/3)-radius*cos(60));
}
endShape(CLOSE);
}
}
静态效果:
动态效果:
6. particleChainLine
函数原型:line_particle_0126(x0,y0,x1,y1,Wid)
参数说明:x0,x1分别表示线条的起点和终点横坐标,y0表示线条的纵坐标中心,y1和Wid限制鼠标作用的范围
function line_particle_0126(x0,y0,x1,y1,Wid)
{
let xp=round(random(0,x1));
let yp=round(random(y0-Wid/4,y0+Wid/4));
//let yp=950;
if(mouseY<(y1-3*Wid)||mouseY>y1+(3*Wid))
{system.origin=(createVector(xp,yp));}
else{
let addx=random(-Wid,Wid);
let addy=random(-Wid,Wid);
system.origin=(createVector(mouseX+addx,mouseY+addy));
}
//system.origin=(createVector(xp,yp));
system.addParticle();
system.run();
}
// A simple Particle class
let Particle = function(position) {
this.acceleration = createVector(random(-0.02, 0.02), random(-0.01, 0.01));
this.velocity = createVector(random(-1, 1), random(-1, 1));
this.position = position.copy();
this.lifespan = 255;
};
Particle.prototype.run = function() {
this.update();
this.display();
};
// Method to update position
Particle.prototype.update = function(){
this.velocity.add(this.acceleration);
//this.velocity = createVector(random(0, 5), random(0, 5));
this.position.add(this.velocity);
this.lifespan -= 1 ;
};
// Method to display
Particle.prototype.display = function() {
stroke(255,255,255,this.lifespan);
strokeWeight(2);
fill(0,255,255,this.lifespan);
ellipse(this.position.x, this.position.y, 12, 12);
};
// Is the particle still useful?
Particle.prototype.isDead = function(){
return this.lifespan < 0;
};
let ParticleSystem = function(position) {
this.origin = position.copy();
this.particles = [];
};
ParticleSystem.prototype.addParticle = function() {
this.particles.push(new Particle(this.origin));
};
ParticleSystem.prototype.run = function() {
for (let i = this.particles.length-1; i >= 0; i--) {
let p = this.particles[i];
p.run();
for(let j = this.particles.length-1; j >= 0; j--){
let q = this.particles[j];
const d = dist(p.position.x, p.position.y,q.position.x,q.position.y);
if(d < 60&&d>30){
let wid=random(0,1.5);
strokeWeight(wid);
stroke(0,255,255);
line(p.position.x, p.position.y,q.position.x,q.position.y);
}
}
if (p.isDead()) {
this.particles.splice(i, 1);
}
}
};
静态效果:
动态效果:
7.fallingstarLine
函数原型: line_particle_1_0126(x0, y0, d, angle, time)
参数说明: 其中x0,y0表示线条的终点坐标,d表示粒子运动的距离(需要结合translate使用),angle表示线条与水平线的夹角,time表示线条每次存在的时间。
function line_particle_1_0126(x0, y0, d, angle, time)
{
push();
let dx=d*cos(angle);
let dy=d*sin(angle);
//translate((0.1*dx*millis())%1000, (0.1*dy*millis())%1000);
system_1.origin=(createVector(x0, y0));
system_1.angle=angle;
if (millis()/1000-system_1.staytime<time) {
system_1.addParticle();
}
system_1.run();
pop();
}
// A simple Particle class
let Particle_1 = function(position,angle) {
this.d=random(-5,0);
this.angle=angle;
this.acceleration = createVector(0.01*this.d*random(cos(this.angle-PI/3),cos(this.angle+PI/3)), 0.01*this.d*random(sin(this.angle-PI/3),sin(this.angle+PI/3)));
this.velocity=createVector(this.d*cos(this.angle),this.d*sin(this.angle));
this.position = position.copy();
this.lifespan = 255;
this.size=random(1, 10);
this.c1=random(0, 3);
};
Particle_1.prototype.run = function() {
this.update();
this.display();
};
// Method to update position
Particle_1.prototype.update = function() {
//this.acceleration = createVector(random(-0.05, 0), random(-0.03, 0.03));
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.lifespan -= 1 ;
};
// Method to display
Particle_1.prototype.display = function() {
c1=random(0, 3);
if (this.c1>1&&this.c1<2) {
fill(0, 255, 255, this.lifespan);
stroke(0, 255, 255, this.lifespan);
} else if (this.c1<=1) {
fill(255, 255, 255, this.lifespan);
stroke(255, 255, 255, this.lifespan);
} else {
fill(255, 255, 255, this.lifespan);
stroke(0, 255, 255, this.lifespan);
}
ellipse(this.position.x, this.position.y, this.size, this.size);
};
// Is the particle still useful?
Particle_1.prototype.isDead = function() {
return this.lifespan < 0;
};
let ParticleSystem_1 = function(position) {
this.angle=0;
this.origin = position.copy();
this.particles = [];
this.staytime=0;
//this.lock=false;
};
ParticleSystem_1.prototype.addParticle = function() {
this.particles.push(new Particle_1(this.origin,this.angle));
};
ParticleSystem_1.prototype.run = function() {
if (millis()/1000-this.staytime>5) {
this.staytime=millis()/1000;
}
for (let i = this.particles.length-1; i >= 0; i--) {
let p = this.particles[i];
p.run();
if (p.isDead()) {
this.particles.splice(i, 1);
}
}
};
静态效果:
动态效果:
三、总结
以上是当前实现的所有线条,在设计这些线条时加入了先前提到的带有科技感的元素图形,但是有些线条在设计之前没有考虑周全导致使用起来有摆放位置的限制,还需要继续改进。