本次实验的主题是编写一个绘画系统,提供一系列绘画材料例如画笔、颜料、滤镜等给用户操作,以创作出动态、交互的绘画作品。这个绘画是对传统绘画概念的拓展,但仍然要体现出与传统绘画的相似性。
注意:目标可以理解为:
创作一个app,看起来比较像绘画工具,但又不是重复已知的绘画行为,而要体现出绘画出不来的效果:动态/UI或者意图,传统绘画是创作的一个平面视觉图像,这个app可以创作其他东西。
设计思路:
首先,三个基本要求:动态/UI/交互。关于UI和交互其实很多绘图软件里都很常见了,这是当然的,因为一个软件是给人用的,如果没有最基础的友好的ui和交互的话肯定没办法胜过其他软件,最后被淘汰。
所以要确定自己的系统要有UI、可交互。
接下来要大体考虑一下想通过这个绘画系统实现什么样的功能,然后再去通过代码实现。
本次实验借鉴了这篇博客:
https://blog.csdn.net/qq_27534999/article/details/79427721
由于是绘画系统,要让使用者能够进行交互的话要设计GUI界面,因此需要按钮,p5.dom里有提供关于直接在界面上创建按钮的函数,这里使用的方法是通过rect画出按钮然后判断鼠标是否点击在画出的范围内,然后再决定是否要执行接下来的事件。
整个界面布局如下:
画布右边是调色盘,底端是功能键,点击左二按钮会显示不同的三个数字,分别是1、2、3,代表本系统的三种笔刷
笔刷一(地狱恶犬笔刷):
笔刷二(表情包笔刷):
笔刷三:
附加功能:在上面gif里表现得很明显了,这个绘画系统有自带背景音乐,且会根据音调振幅在屏幕上显示出来。
为了实现按钮功能,定义FuncBtn实现重复形状但位置不同的按钮,用ColorBtn来实现画布的调色盘功能。代码不与赘述;
然后有了按钮就要实现相应的功能,这里写了三个方法来画不同的图像,方便被下面的交互运动调用。
function huaji(x,y,size,l){
//stroke(252,186,12);
noStroke();
fill(237,148,14);
ellipse(x,y,1.06*size);
fill(252,222,12);
ellipse(x,y,size);//脸
fill(173,91,10);
arc(x, y, 0.8*size, 0.8*size, 0, PI, CHORD);
fill(252,222,12);
arc(x, y-0.01*size, 0.8*size, 0.7*size, 0, PI, CHORD);//笑
fill(254,240,205);
arc(x-0.25*size, y, 0.8*size, 0.6*size, 1.3*PI,1.7* PI, PIE);
fill(252,222,12);
arc(x-0.25*size, y, 0.8*size, 0.5*size, 1.3*PI,1.7* PI, PIE);
fill(254,240,205);
arc(x+0.25*size, y, 0.8*size, 0.6*size, 1.3*PI,1.7* PI, PIE);
fill(252,222,12);
arc(x+0.25*size, y, 0.8*size, 0.5*size, 1.3*PI,1.7* PI, PIE);//眼壳子
if(l==-1){
fill(173,91,10);
ellipse(x-0.34*size,y-0.28*size,0.1*size);
ellipse(x+0.16*size,y-0.28*size,0.1*size);//眼珠
}
if(l==1){
fill(173,91,10);
ellipse(x-0.1*size,y-0.28*size,0.1*size);
ellipse(x+0.35*size,y-0.28*size,0.1*size);//眼珠
}
fill(58,43,1);
arc(x-0.28*size, y-0.35*size,0.20*size, 0.20*size, 1.1*PI,2.1* PI, OPEN);
fill(252,222,12);
arc(x-0.28*size, y-0.35*size, 0.21*size, 0.16*size, 1*PI,2.2* PI, OPEN);
fill(58,43,1);
arc(x+0.28*size, y-0.35*size,0.20*size,0.20*size, 0.9*PI,1.9* PI, CHORD);
fill(252,222,12);
arc(x+0.28*size, y-0.35*size, 0.21*size, 0.15*size, 0.9*PI,1.9* PI, CHORD);//眉毛
fill(241,74,56);
ellipse(x-0.25*size,y-0.18*size,0.25*size,0.07*size);
ellipse(x+0.25*size,y-0.18*size,0.25*size,0.07*size);
}
function dog(x,y,size)
{
noStroke();
//头
fill(0);
quad(x-1*size,y+0.5*size,x-0.5*size,y+1*size,x+0.5*size,y+1*size,x+1*size,y+0.5*size);
quad(x-1*size,y-0.5*size,x-0.5*size,y-1*size,x+0.5*size,y-1*size,x+1*size,y-0.5*size);
quad(x-1*size,y+0.5*size,x+1*size,y+0.5*size,x+1*size,y-0.5*size,x-1*size,y-0.5*size);
//鼻子
quad(x+0.3*size,y-0.1*size,x+0.3*size,y+0.2*size,x-0.3*size,y+0.2*size,x-0.3*size,y-0.1*size);
//耳朵
fill(0);
triangle(x-1*size,y-0.5*size,x-1*size,y-2*size,x-0.5*size,y-1*size);
triangle(x+0.5*size,y-1*size,x+1*size,y-2*size,x+1*size,y-0.5*size);
//眼睛
fill(255);
ellipse(x+0.65*size,y-0.25*size,0.25*size,0.25*size);
ellipse(x-0.65*size,y-0.25*size,0.25*size,0.25*size);
//ellipse(x-0.25*size,y-0.18*size,0.25*size,0.07*size);
//胡子
}
function simple(x,y,size)
{
noStroke();//无边框
ellipse(x*size,y*size,1*size,0.5*size);
ellipse(x*size,y*size,0.5*size,1*size);
//ellipse(x*size,y-10*size,2*size,2*size);
//ellipse(x*size,y+10*size,2*size,2*size);
//ellipse(x*size,y*size,2*size,2*size);
}
这里为了让后面实现运动,所以给每个坐标一个缩放比例size,方便后续处理
然后为了让画出的图像动起来,定义一个随机偏移量x,一个随机偏移量y,
然后如果点击了相应的按钮,就执行相应的方法,并且传递给方法中的变量x和y的值为浮动值,让图像保持在鼠标曾点击的位置附近运动,这里给传递过来的变量加了周期。
然后为了实现播放音乐并显示音乐条的功能,定义全局变量
var song, fft;
var tx =[];
var ty = [];
var big = [];
var a = 0;
var r = 0;
用preload初始化加载音乐:
这里的preload
是预加载函数,在setup()执行之前执行,用于处理异步操作。所有需要加载的内容(如图片文件、json文件、字体文件等)加载完毕的时候再执行setup()。
在加载内容的时候默认的提示“loading”,如果想要自定义“loading”的样式,则可以在body里加入一段id为“p5_loading”的标签即可,
function preload() {
// we have included both an .ogg file and an .mp3 file
soundFormats('ogg', 'mp3');
// if mp3 is not supported by this browser,
// loadSound will load the ogg file
// we have included with our sketch
song = loadSound('1.mp3');
}
在setup方法中
song.play();
fft = new p5.FFT();
fft.setInput(song);
colorMode(RGB);
for (var i = 0; i < 1024; i++) {//所有的
tx[i] = 500+cos(r)*100;//x坐标
ty[i] = 300+sin(r)*200;//y坐标
r+=2*PI/1024;//增加
}
最后在draw方法中画出运动的音乐条
var spectrum = fft.analyze();
//beginShape();
//.right.get(i)
noStroke();
for (i = 0; i
fill(i/2, 255, 255);
rect(i*1, 600-spectrum[i]*2, 5, spectrum[i]*0.5 );
}
其实一开始在构思的时候也上网查找了很多资料,但是网上关于p5.js的内容不是太多,在查找资料的过程中,一开始想实现的效果是这样的,用webgl实现的流体效果,
p5.js中确实能使用webgl,但是尝试了很久,自己写不出来遂放弃。
总体来说,