这是一个用processing写的趣味画板,是我和室友一起做的一个小玩意儿。
画板的功能介绍如下图所示。
菜单栏详细介绍:
保存/另存为 :可以打开本地的文件夹选择器,默认存储格式为jpg,若用户指定格式,则以用户指定的格式存储图片。保存按钮在使用过一次后,再次点击不会打开文件夹选择器,而是保存到之前填写的存储位置。
背景颜色 :该画板的颜色模式为HSB模式,背景颜色选择区域是一个120x100的矩形区域,点击背景颜色时会清空绘画区域。
画笔颜色 :同背景颜色,只不过点击画笔颜色不会清空绘画区域。
图形选择 :该画板比较简单,图形较少,只有线条,随机圆,移动圆,矩形,圆角矩形,中心对称,花朵(图片),花朵(图形)。
画板的主界面如图所示:
下面就是这个画板的代码了,由四个标签组成,其中三个为class。
DrawPanel_mainfile:
String pic_name="line"; //用于记录图形
PFont font; //字体
int strokeSize=1; //用于记录描边粗细
color strokeColor; //用于记录描边颜色
color backColor; //用于记录绘画区域的背景颜色
int key_d_=0; //用于记录帮助是否打开
String filename; //用于记录之前保存的文件名
//以下四个变量用于画矩形
float x1,x2,y1,y2; //用于记录按钮按下和松开时的坐标
int j1=0; //用于记录是否有鼠标按下的坐标
int j2=0; //用于记录是否有鼠标松开的坐标
int d_; //用于矩形记录鼠标是否按下
//移动圆所需变量
int move_circle_d=0; //用于移动圆记录鼠标是否按下
//圆角矩形所需变量
float rcx1,rcx2,rcy1,rcy2;
int rcj1=0;
int rcj2=0;
int rcd_;
//中心对称所需变量
float csx,csy;
int csj=0;
int csd_;
int line_count=6; //该整数用于记录当前中心对称所产生的线条数
//花朵所需变量
float flowerSize=50; //定义一个全局变量用于增加花朵大小
//可填充颜色的花朵所需变量
PShape s; //创建一个图形对象用来存储花朵的形状
float jiaodu; //用来画花朵弧形的函数
float size=0; //用来记录花朵的大小
color colora=color(random(0,120),random(0,120),random(0,100)); //记录花朵的随机颜色
//按钮图标
PImage[] img=new PImage[13];
MoveCircle[] mcs=new MoveCircle[0];
void setup(){
background(255);
colorMode(HSB, 100); //设置颜色模式为HSB模式
backColor=color(0,0,120);
fill(backColor);
rect(-1,121,1001,680);
size(1000,800);
font=createFont("KaiTi-48.vlw",30); //创建字体为楷体
textFont(font); //将楷体设置为默认字体
textSize(30);
frameRate(30);
for(int i=0;i<13;i++){
img[i]=loadImage(i+".png");
}
//创建图形(花朵)
s=createShape();
s.beginShape();
s.fill(0);
s.noStroke();
for(int i=-40;i<=40;i++){
s.vertex(i,-i*i/10*0.6);
}
for(int i=40;i>=-40;i--){
jiaodu=map(i,40,-40,30,150);
s.vertex(i,-112-40*(sin(radians(jiaodu))-0.5));
}
s.endShape();
//分割线......
fill(0);
line(0,120,1000,120);
line(210,0,210,120);
line(210,60,500,60);
line(500,0,500,120);
line(750,0,750,120);
line(610,0,610,120);
line(860,0,860,120);
image(img[1],110,15,90,90); //另存为
image(img[2],220,5,50,50); //线条
image(img[3],275,5,50,50); //随机圆
image(img[4],330,5,50,50); //移动圆
image(img[5],385,5,50,50); //矩形
image(img[6],440,5,50,50); //圆角矩形
image(img[7],220,65,50,50); //中心对称
image(img[8],275,65,50,50); //花朵
image(img[9],330,65,50,50); //可填充颜色的花朵
image(img[10],510,10,90,90); //背景颜色
image(img[11],760,10,90,90); //画笔颜色
//提示帮助的文字
noFill();
rect(385,60,105,60);
text("帮助(H)",385,100);
//初始的描边颜色
strokeColor=color(0,0,0);
//通过画point来产生一块区域表示颜色选择区域
for (int i = 0; i < 120; i++) {
for (int j = 0; j < 100; j++) {
stroke(i, j, 120);
point(620+i, 10+j);
}
}
for (int i = 0; i < 120; i++) {
for (int j = 0; j < 100; j++) {
stroke(i, j, 120);
point(870+i, 10+j);
}
}
}
void draw(){
//之所以把保存按钮放在draw()函数里是为了消除一个移动圆带来的bug,通过重新产生一个矩形和保存按钮,
//来清除移动圆在(0,0)位置产生的痕迹
fill(0,0,120);
noStroke();
rect(0,0,52,52);
image(img[0],10,15,90,90); //保存
ShapeType st=new ShapeType(pic_name); //创建图形对象
st.drawShape(); //调用画图函数
mouseStyle();
}
void mouseReleased() {
//当点击按钮时,改变pic_name,并修改按钮颜色
if(mouseX>220&&mouseX<270&&mouseY>5&&mouseY<55){
pic_name="line"; //线条
}else if(mouseX>275&&mouseX<325&&mouseY>5&&mouseY<55){
pic_name="random_circle"; //随机圆
}else if(mouseX>330&&mouseX<380&&mouseY>5&&mouseY<55){
pic_name="move_circle"; //移动圆
}else if(mouseX>385&&mouseX<435&&mouseY>5&&mouseY<55){
pic_name="rect"; //矩形
}else if(mouseX>440&&mouseX<490&&mouseY>5&&mouseY<55){
pic_name="radius_rect"; //圆角矩形
}else if(mouseX>220&&mouseX<270&&mouseY>65&&mouseY<115){
pic_name="center_shape"; //中心对称
}else if(mouseX>275&&mouseX<325&&mouseY>65&&mouseY<115){
pic_name="flower"; //花朵图片
}else if(mouseX>330&&mouseX<380&&mouseY>65&&mouseY<115){
pic_name="color_flower"; //可填充颜色的花朵
}
else if(mouseX>870&&mouseX<990&&mouseY>10&&mouseY<110){
strokeColor=color(mouseX-870,mouseY-10,120); //画笔颜色
}else if(mouseX>620&&mouseX<740&&mouseY>10&&mouseY<110&&key_d_==0){ //背景颜色
backColor=color(mouseX-620,mouseY-10,120);
fill(backColor);
noStroke();
rect(-1,121,1001,680);
mcs=new MoveCircle[0]; //需要清空储存移动圆的数组
}else if(mouseX>385&&mouseX<490&&mouseY>60&&mouseY<120){
openHelp(); //调用打开帮助函数
}else if(mouseX>120&&mouseX<210&&mouseY>15&&mouseY<105){
selectOutput("请选择一个文件夹:","selectFile"); //另存为
}else if(mouseX>10&&mouseX<100&&mouseY>15&&mouseY<105){
if(filename==null){
selectOutput("请选择一个文件夹:","selectFile_"); //保存
}else{
save(filename);
}
}
}
//另存为调用的函数
void selectFile(File selection){
if(selection==null){
}else{
if(selection.getAbsolutePath().indexOf(".")>0){ //若用户再文件名处写了后缀
save(selection.getAbsolutePath()); //以用户规定的格式保存
}else{
save(selection.getAbsolutePath()+".jpg"); //默认保存为jpg格式
}
}
}
//保存调用的函数,之所以保存与另存为要分开写,是为了filename的正确存储,另存为按钮并不会filename传值
//这样点击另存为之后,再点保存按钮,不会保存到另存为设置的文件路径。这样filename就只能存储保存按钮
//输入的文件存储路径了
void selectFile_(File selection){
if(selection==null){
}else{
if(selection.getAbsolutePath().indexOf(".")>0){ //若用户再文件名处写了后缀
save(selection.getAbsolutePath()); //以用户规定的格式保存
filename=selection.getAbsolutePath(); //将文件名保存下来
}else{
save(selection.getAbsolutePath()+".jpg"); //默认保存为jpg格式
filename=selection.getAbsolutePath()+".jpg"; //将文件名保存下来
}
}
}
//键盘监听函数
void keyReleased(){
char k=key;
switch(k){
case 'c':
fill(backColor);
noStroke();
rect(-1,121,1001,680);
mcs=new MoveCircle[0];
break;
case 'C':
fill(backColor);
noStroke();
rect(-1,121,1001,680);
mcs=new MoveCircle[0];
break;
case 'h':
openHelp(); //调用打开帮助的函数
break;
case 'H':
openHelp();
break;
case '0':
strokeSize=0;
break;
case '1':
strokeSize=1;
break;
case '2':
strokeSize=2;
break;
case '3':
strokeSize=3;
break;
case '4':
strokeSize=4;
break;
case '5':
strokeSize=5;
break;
case '6':
strokeSize=6;
break;
case '7':
strokeSize=7;
break;
case '8':
strokeSize=8;
break;
case '9':
strokeSize=9;
break;
case 'w':
line_count+=1;
break;
case 's':
line_count-=1;
break;
}
}
//打开、关闭帮助的函数
void openHelp(){
if(key_d_==0){
//先用矩形覆盖背景颜色的区域,然后将帮助图片加载上去
noStroke();
fill(0,0,120);
rect(510,0,230,120);
image(img[12],510,10,230,100);
key_d_=1;
}else{
//同理用矩形覆盖帮助图片区域,再讲背景颜色的区域重新绘制一边
noStroke();
fill(0,0,120);
rect(510,10,230,110);
stroke(0,0,0);
strokeWeight(1);
line(610,0,610,120);
for (int i = 0; i < 120; i++) {
for (int j = 0; j < 100; j++) {
stroke(i, j, 120);
point(620+i, 10+j);
}
}
image(img[10],510,10,90,90);
key_d_=0;
}
}
//监听鼠标样式的函数
void mouseStyle(){
if(mouseX>220&&mouseX<270&&mouseY>5&&mouseY<55){
cursor(HAND);
}else if(mouseX>275&&mouseX<325&&mouseY>5&&mouseY<55){
cursor(HAND);
}else if(mouseX>330&&mouseX<380&&mouseY>5&&mouseY<55){
cursor(HAND);
}else if(mouseX>385&&mouseX<435&&mouseY>5&&mouseY<55){
cursor(HAND);
}else if(mouseX>440&&mouseX<490&&mouseY>5&&mouseY<55){
cursor(HAND);
}else if(mouseX>220&&mouseX<270&&mouseY>65&&mouseY<115){
cursor(HAND);
}else if(mouseX>275&&mouseX<325&&mouseY>65&&mouseY<115){
cursor(HAND);
}else if(mouseX>330&&mouseX<380&&mouseY>65&&mouseY<115){
cursor(HAND);
}else if(mouseX>870&&mouseX<990&&mouseY>10&&mouseY<110){
cursor(CROSS);
}else if(mouseX>620&&mouseX<740&&mouseY>10&&mouseY<110&&key_d_==0){
cursor(CROSS);
}else if(mouseX>385&&mouseX<490&&mouseY>60&&mouseY<120){
cursor(HAND);
}else if(mouseX>120&&mouseX<210&&mouseY>15&&mouseY<105){
cursor(HAND);
}else if(mouseX>10&&mouseX<100&&mouseY>15&&mouseY<105){
cursor(HAND);
}else{
cursor(ARROW);
}
}
ShapeType_class:
class ShapeType{
String shape_name;
//有参构造函数,获取代表形状的参数shape_name
ShapeType(String pic_name){
shape_name=pic_name;
}
//绘画函数,根据shapename来判断需要画什么图形
public void drawShape(){
//当图形为线条时
if(shape_name=="line"){
frameRate(300);
strokeWeight(strokeSize);
stroke(strokeColor);
if(mousePressed&&mouseY>140){
line(mouseX,mouseY,pmouseX,pmouseY);
}
}
//当图形为随机圆时
if(shape_name=="random_circle"){
frameRate(8); //修改帧速率,防止图形产生过快覆盖了原图形
noStroke();
if(mousePressed&&(mouseButton==LEFT)&&mouseY>140){
int r=(int)random(25,45); //随机产生圆半径
int R=(int)random(0,255); //随机red值
int G=(int)random(0,255); //随机green值
int B=(int)random(0,255); //随机blue值
int A=(int)random(0,255); //随机透明度
fill(R,G,B,A);
ellipse(mouseX,mouseY,r,r);
}
}
//当图形为移动圆形时
if(shape_name=="move_circle"){
frameRate(50);
noStroke(); //去掉描边
if(mouseY>140){
if(mousePressed){
move_circle_d=1;
}else{
if(move_circle_d==1){
MoveCircle mc=new MoveCircle(mouseX,mouseY,strokeColor,strokeSize);
mcs=(MoveCircle[])append(mcs,mc); //用append函数添加新元素到对象数组中
}
move_circle_d=0;
}
}
for(int i=0;i<mcs.length;i++){
mcs[i].move(); //遍历对象数组,依次移动数组中的圆
}
}
//当图形为矩形时
if(shape_name=="rect"){
frameRate(30);
noStroke();
if(mouseY>140){
if(mousePressed){
d_=1;
j2=0;
if (j1==0) //按下鼠标时记录此时的鼠标坐标
{
j1=1;
x1=mouseX;
y1=mouseY;
}
}else{
if (d_==1) {
fill(strokeColor);
j1=0;
if (j2==0) //松开鼠标时记录此时的鼠标坐标
{
j2=1;
x2=mouseX;
y2=mouseY;
}
quad(x1, y1, x1, y2, x2, y2, x2, y1); //只需在松开鼠标时绘画,用四边形,不能用矩形
d_=0;
}
}
}
}
//当图形为圆角矩形时
if(shape_name=="radius_rect"){
frameRate(100);
strokeWeight(strokeSize);
stroke(strokeColor);
if(mouseY>140){
if(mousePressed){
rcd_=1;
rcj2=0;
if (rcj1==0) //按下鼠标时记录此时的鼠标坐标
{
rcj1=1;
rcx1=mouseX;
rcy1=mouseY;
}
}else{
if (rcd_==1) {
fill(255);
rcj1=0;
if (rcj2==0) //松开鼠标时记录此时的鼠标坐标
{
rcj2=1;
rcx2=mouseX;
rcy2=mouseY;
}
//quad(rcx1, rcy1, rcx1, rcy2, rcx2, rcy2, rcx2, rcy1);
float rcr=min(abs(rcx1-rcx2),abs(rcy1-rcy2))/3.0; //圆角半径
float rct; //用于交换坐标值时作为中间量
//交换角点值,使得rcx1,rcy1为左上角的坐标,rcx2,rcy2为右下角坐标
if(rcx1>rcx2){
rct=rcx1;rcx1=rcx2;rcx2=rct;
}
if(rcy1>rcy2){
rct=rcy1;rcy1=rcy2;rcy2=rct;
}
fill(strokeColor);
arc(rcx1+rcr/2,rcy1+rcr/2,rcr,rcr,PI,PI+HALF_PI);
arc(rcx2-rcr/2,rcy1+rcr/2,rcr,rcr,PI+HALF_PI,TWO_PI);
arc(rcx2-rcr/2,rcy2-rcr/2,rcr,rcr,0,HALF_PI);
arc(rcx1+rcr/2,rcy2-rcr/2,rcr,rcr,HALF_PI,PI);
rect(rcx1,rcy1+rcr/2,rcx2-rcx1,rcy2-rcr-rcy1);
rect(rcx1+rcr/2,rcy1,rcx2-rcr-rcx1,rcy2-rcy1);
rcd_=0;
}
}
}
}
//当图形为中心对称图形时
if(shape_name=="center_shape"){
frameRate(300);
strokeWeight(strokeSize);
stroke(strokeColor);
if(mouseY>140){
if(mousePressed){
csd_=1;
if(csj==0){
csj=1;
csx=mouseX;
csy=mouseY;
}
translate(csx,csy); //移动坐标系
if(dist(mouseX-csx,mouseY-csy,0,0)<(csy-140)){ //约束中心对称的大小,避免画到工具栏上面
for(int i=0;i<line_count;i++){
line(mouseX-csx,mouseY-csy,pmouseX-csx,pmouseY-csy);
rotate(TWO_PI/line_count); //旋转坐标系
}
}
}else{
if(csd_==1){
csj=0;
}
csd_=0;
}
}
}
//当图形为花朵时
if(shape_name=="flower"){
frameRate(15);
if(mouseY>150){
if(mousePressed){
Flower f=new Flower(mouseX, mouseY);
f.show();
}else{
flowerSize=50;
}
}
}
//当图形为可填充颜色的花朵时
if(shape_name=="color_flower"){
frameRate(10);
if(mouseY>150){
if(mousePressed){
translate(mouseX,mouseY);
scale(flowerSize/100-0.3+size);
s.beginShape();
s.fill(strokeColor);
s.endShape();
if(size<=0.6&&mouseY-(132*(0.2+size))>120){
for(int j=0;j<6;j++){
shape(s,0,0);
rotate(radians(72));
}
fill(colora);
ellipse(0,0,40,40);
size+=0.03;
}
}else{
colora=color(random(0,120),random(0,120),random(0,100));
size=0;
}
}
}
}
}
MoveCircle_class:
class MoveCircle{
float x,y,x1,y1;
float xspeed,yspeed;
color circleColor;
int size;
MoveCircle(float a,float b,color c,int d){
x=a;
y=b;
circleColor=c; //圆形颜色
size=d+1; //记录当前strokeSize,+1是为了保证size!=0,因为圆形半径=size*10
xspeed=random(-1,1);
yspeed=random(-1,1);
}
void move(){
//每隔一段时间修改一次圆形速度,为了修改周期与帧速率匹配,需要设定一个范围
if(millis()%1000<10||millis()%1000>990&&millis()%1000<1000){
xspeed=random(-1,1);
yspeed=random(-1,1);
}
x+=xspeed;
y+=yspeed;
fill(backColor);
ellipse(x1,y1,10*size+2,size*10+2); //该圆形用于清除前一个圆,做到"伪刷新"
if(y-25>140){
fill(circleColor);
ellipse(x,y,size*10,size*10);
x1=x;y1=y;
}
}
}
Flower_class:
class Flower {
PImage flower; //载入花朵图片
float x,y;
Flower(float mx, float my){
x=mx;
y=my;
flower = loadImage("flower.png"); //载入花朵图片
}
void show(){
if(flowerSize<200){
flowerSize+=10;
if((mouseY-flowerSize/2)>120){
image(flower, x-flowerSize/2, y-flowerSize/2,flowerSize,flowerSize);
}
}
}
}
接下去是这个项目所需的一些图片资源:
全部项目资源可从这里下载:processing趣味画板代码及资源