前言

由p5.js实现创意绘板,我分成了动态创意绘板和静态创意绘板。

静态创意绘板

android 涂鸦 马克笔效果 涂鸦笔和马克笔区别_i++

上图仿照水粉画。

android 涂鸦 马克笔效果 涂鸦笔和马克笔区别_i++_02

上述图中实现了对于水粉笔的模仿。
可以调节颜色的变化,对rgb直接进行调节,大小形状也可调节,上图展示的是使用正方形笔刷,颜色是根据画布横坐标来表现,可以看出,左边偏玫红,右边偏绿,笔刷的形状会对深浅有影响。交互的时候还挺好玩的,一个动作在整个画布中不局限于一种颜色,实现画布上颜色的渐变。

不足之处可能也是上述原因,位置的限制,不能做到自由区域颜色的选择。

主要原理是一定区域随机性,实现笔刷的绘制,实际绘制是一个一个小圆,就形成了粉状的质感。

实现代码:

function pastel(mx,my,sx,sy,r,g,b,a)
{    for(var i = 0; i < 1000; i++)
{        
var ratio = mx/width;        
var x = mx + random(-sx, sy);        
var y = my + random(-sx, sy);         
 fill(r, ratio * g, b * (1 - ratio), a);        
 //fill(ratio * 255,255 * (1 - ratio) ,0, 30);          
 //fill(ratio *r,g * (1 - ratio) ,b, a);          
 ellipse(x, y, 2, 2); 
 }
 }

if (keyCode === 65)
 { //A        
  colorMode(RGB,255,255,255,255); 
pastel(mouseX,mouseY,sx,sy,r,g,b,30);             
     }
var r=0;
var g=255;
var b=255;
var sx=20;
var sy=20;
function keyPressed() 
{    
  if(keyCode==82)//R       
   {         r=r+30;        
   }        
   if(keyCode==71)//G        
   {         g=g-30;                 }        
   if(keyCode==66)//B       
    {         b=b-30;                 }            
    if(keyCode==97)//1       
     {         sx=sx-5;                 }       
      if(keyCode==98)//2       
       {            sx=sx+5;                 }        
       if(keyCode==97)//3        
       {         sx=sy-5;                 }       
        if(keyCode==98)//4        
        {            sx=sy+5;                 }


      }

android 涂鸦 马克笔效果 涂鸦笔和马克笔区别_android 涂鸦 马克笔效果_03

上图模仿马克笔。图中线条的粗细变化,我觉得还挺好看的,从图中可以看出,重涂和轻涂,所展示的轨迹不一样,缓慢重涂,出来的效果跟接近于平常所使用马克笔,是很浓重的一抹;但是轻涂,特别是快速的划过,就可能画出另一种圆形,一个一个的圆,也体现模仿马克笔所采用的原理本质,是由一个一个的圆所叠加构成。

区别于传统马克笔,上图画笔的运用方式,如快速涂抹和缓慢重涂,可以展现不同的图案,展现了多边形,但是细腻方面,笔锋方面可能不太比得上传统马克笔,做不到快速细腻的笔锋也是上图所表现的一个不足之处。

android 涂鸦 马克笔效果 涂鸦笔和马克笔区别_android 涂鸦 马克笔效果_04

(小学生涂鸦。。。)
图中是为了仿照现实水彩笔。

与传统最大的区别就是不用替换颜色呐(可能在选取颜色的时候不太方便),颜色的展现随机相连(适合懒人,一支笔有多种颜色),除此之外,就是笔刷颜色的透明度的变化,我觉得这是传统水彩笔实现不了的,快慢会导致笔刷颜色变化,相应也会导致不图透明度的变化,体现不一样色彩效果。

笔触细腻的处理也是上述图中一大不足。

android 涂鸦 马克笔效果 涂鸦笔和马克笔区别_android 涂鸦 马克笔效果_05

上述图对水彩马克笔的模仿,缓慢厚涂和快速浅涂的区别。快涂,会使得的透明度升高,所展现的图案也和慢涂不同,原理是运用了插值变化。

实现代码:

// 彩球的位置
  var x;var y;
  // 彩球位置的插值变化速率
  var lerpPosSpd = 31.0;
// 彩球当前大小s和颜色rgbaf(a:alpha)
var s = 15;var r = 0;var g = 0;var b = 0;var a = 0.1;
// 彩球颜色插值速率
var lerpSpd = 3.0;
// 彩球的目标大小和颜色
var st = 30;var rt = 0;var gt = 0;var bt = 0;var at = 0.1;
// //黑色圈圈   
   if (keyCode == 77)
   { //M        c
   olorMode(RGB,255,255,255,1);      
   circleColor();    
   }      
   //彩色圈圈    
   if (keyCode == 68)
    { //D         
     colorMode(RGB,1,1,1,1);       
     circleColor();     
      }

var lastTime = -1;
function circleColor()
{    
var tNow = millis()/1000;   
 if(lastTime<0)   
  {        
  lastTime=tNow;        
  return;    
  }
    var dt = tNow-lastTime;        
    var lerpPosT = lerpPosSpd*dt;    
    if(mouseIsPressed)    
    {        
    // 让x的数值向mouseX的数值靠拢,        
    // 靠拢方式为“线性插值”,靠拢程度(快慢)为lerpPosT        
    x = lerp(x,mouseX,lerpPosT);        
    y = lerp(y,mouseY,lerpPosT);        
    drawThing(x,y);    }
    var lerpT = lerpSpd * dt;    
    s = lerp(s,st,lerpT);    
    r = lerp(r,rt,lerpT);    
    g = lerp(g,gt,lerpT);    
    b = lerp(b,bt,lerpT);    
    a = lerp(a,at,lerpT);
    randomChange(dt);
    lastTime = tNow;
}
function drawThing(x,y)
{   
 fill(r,g,b,a);    
 noStroke();    
 ellipse(x,y,s,s);}
 
var TimeToChange = -1;

function randomChange(dt)
{    
TimeToChange-= dt;    
if(TimeToChange<0)   
 {        
 st = random(10,60);        
 rt = random(0,1);        
 gt = random(0,1);        
 bt = random(0,1);       
  at = random(0.1,0.3);        
 TimeToChange = randomValue();        
 print("TimeToChange:" + TimeToChange);    
 }
 }
 
var lamda = 5;

function randomValue()
{    
var x = 5/lamda;    
var cnt = 0;    
do    
{        x = random(0,5/lamda);        
var y = random(0,1);        
var prob = exp(-lamda*x);        /
 if(cnt>30)        
 {
             break;       
              }       
               cnt ++;   
                }while(prob<y)        
          
                }

动态创意绘板

android 涂鸦 马克笔效果 涂鸦笔和马克笔区别_ci_06

动态创意绘板,可以选择多种笔刷,有圆形、三角形、长条、五边形、四边形、六角星星(星星真好看),六种笔刷,颜色也可多选,画板也可以多选,有夜间模式与白天模式。刷的笔刷都是会动的(图中使用的是静止截图)。

虽然有仿照传统绘画板,保留了绘画板的一些功能,如必刷的选择、清屏(点击C),橡皮擦(点击E),保存文件(点击S),以及多种颜色的可选性,但是从根本上,笔刷所得到的图形,最后是会动的,点击||使得图像静止。

与传统绘画板最大不同是,画出的图案是实时动态变化的,且笔刷也比较灵活,交互性上,也更有趣活波,相对的笔刷可能不太容易制作。

主要实现代码:

对于按钮和物体都需要创建数组来进行管理。

var objs = [];
var btns = [];

显示图案的结构定义。

//显示图案预定义
function Node(position, givenSize, givenR, givenG, givenB) 
{  
this.R = givenR;  
this.G = givenG;  
this.B = givenB;  /
/图案位置  
this.position = createVector(position.x, position.y);  
this.position.x += (random(20) - 10);  
this.position.y += (random(20) - 10);  //  
this.size = createVector(0, 0);  
this.sizeScale = 0.5;  var randomSize = givenSize / 2 + random(10);  
this.baseSize = createVector(randomSize, randomSize);  //  
this.timepast = 0;  this.isPlaying = isPlaying;  //  
this.rotateAngle = random(2 * PI);  //笔刷类型   
this.shapeType = brushType;  
this.pmouseX = pmouseX;  
this.pmouseY = pmouseY;  
this.mouseX = mouseX;  
this.mouseY = mouseY;
}
//功能按钮
function FuncBtn(X, Y, W, H, CMD) 
{  
this.x = X;  
this.y = Y;  
this.w = W;  
this.h = H;  
this.cmd = CMD;
//命令
}
//鼠标在颜色选框内
FuncBtn.prototype.isMouseInBtn = function()
 {  
 if (mouseX >= this.x && mouseX <= this.x + this.w &&    mouseY >= this.y && mouseY <= this.y + this.h) 
 {    return true;  } 
 else 
 {    
 return false;  
 }
 }

点击按钮响应。

FuncBtn.prototype.clickBtn = function() {  print("ClickBtn!");  //  
if (this.cmd == "sun") 
{   
bR = 255;    
bG = 255;    
bB = 255;    
this.cmd = "moon";
  } else if (this.cmd == "moon") {    
  //改变画布的颜色    
  bR = 0;    
  bG = 0;    
  bB = 0;    
  this.cmd = "sun";  
  } else if (this.cmd == "pause") {    
  //运动状态停止    
  isPlaying = false;    
  for (var i = 0; i < objs.length; i++) 
  {      
  objs[i].isPlaying = false;    
  }    
  this.cmd = "play";
  } else if (this.cmd == "play") 
  {    isPlaying = true;    f
  or (var i = 0; i < objs.length; i++) 
  {      
  objs[i].isPlaying = true;    
  }    
  this.cmd = "pause";
  } 
  }
//显示颜色按钮 位置 宽高 颜色
function ColorBtn(X, Y, W, H, givenR, givenG, givenB) 
{  
this.x = X;  
this.y = Y;  
this.w = W;  
this.h = H;  
this.r = givenR;  
this.g = givenG;  
this.b = givenB;
}

星星笔刷和多边形笔刷。

// draw a regular n-gon with n sides
function ngon(n, x, y, d)
 {  
 beginShape();  
 for(var i = 0; i < n; i++) 
 {    
 var angle = TWO_PI / n * i;    
 var px = x + sin(angle) * d / 2;    
 var py = y - cos(angle) * d / 2;    
 vertex(px, py);  
 }  
 endShape(CLOSE);
 }

// draw a regular n-pointed star
function star(n, x, y, d1, d2) 
{  beginShape();  
for(var i = 0; i < 2 * n; i++) 
{    
var d = (i % 2 === 1) ? d1 : d2;    
var angle = PI / n * i;    v
ar px = x + sin(angle) * d / 2;    
var py = y - cos(angle) * d / 2;    
vertex(px, py);  
}  
endShape(CLOSE);
}

笔刷功能实现,以CIRCLE和SQUARE为例,使用sin函数和cos函数改变图像显示的中心位置,使得图像可以运动。

Node.prototype.drawing = function()
 {  
 noStroke();  
 if (this.shapeType == "CIRCLE") 
 {    
 translate(this.position.x, this.position.y);    
fill(this.size.x * this.R / 10, this.size.x * this.G / 10, this.size.x * this.B / 10, round(sin(this.timepast) * 128));    
 ellipse(sin(this.timepast) * this.baseSize.x, cos(this.timepast) * this.baseSize.y, this.size.x * 1.25, this.size.y * 1.25);    
 fill(this.size.x * this.R / 10, this.size.x * this.G / 10, this.size.x * this.B / 10, 255);    
 ellipse(sin(this.timepast) * this.baseSize.x, cos(this.timepast) * this.baseSize.y, this.size.x, this.size.y);    resetMatrix();
    }else if(this.shapeType == "SQUARE")
    {    
    translate(this.position.x, this.position.y);    
    rotate(this.rotateAngle);    
    fill(this.size.x * this.R / 10, this.size.x * this.G / 10, this.size.x * this.B / 10, round(sin(this.timepast) * 128));    
    // rectMode(CENTER);    
    rect(sin(this.timepast) * this.baseSize.x, cos(this.timepast) * this.baseSize.y, this.size.x * 1.25, this.size.y * 1.25);    
    fill(this.size.x * this.R / 10, this.size.x * this.G / 10, this.size.x * this.B / 10, 255);    // rectMode(CENTER);    
    rect(sin(this.timepast) * this.baseSize.x, cos(this.timepast) * this.baseSize.y, this.size.x,this.size.y);   
    resetMatrix();    
    }

显示图像的更新。

Node.prototype.update = function() 
{  this.size = createVector(this.baseSize.x + sin(this.timepast) * this.baseSize.x * this.sizeScale,    
this.baseSize.y + sin(this.timepast) * this.baseSize.y * this.sizeScale);  
if (this.isPlaying) {    this.timepast += 1 / FPS;  
}
}

判断是否处于运动中,便于静止处理。

if(isPlaying)
{    
btns.push(new FuncBtn(5, 5 + 30 * 14, 30, 30, "pause"));  }
else{    
btns.push(new FuncBtn(5, 5 + 30 * 14, 30, 30, "play"));  }  
btns.push(new FuncBtn(5, 5 + 30 * 15, 30, 30, "timer"));  
btns.push(new FuncBtn(5, 5 + 30 * 16, 30, 30, "eraser"));  
btns.push(new FuncBtn(5, 5 + 30 * 17, 30, 30, "clear"));  
btns.push(new FuncBtn(5, 5 + 30 * 18, 30, 30, "save"));
}

鼠标按下的处理,处于可选择区域后的点击的处理。

if (mouseIsPressed && (mouseX > 40 || isMenuHide)) 
{    
if (brushType == "CIRCLE" || brushType == "LINES" || brushType == "TRIANGLE"    ||brushType == "SQUARE"||brushType == "PENTAGON"||brushType == "STAR"    ||brushType == "COLORCIRCLE"||brushType == "PASTEL")     
{      
var position = createVector(mouseX, mouseY);      //      
objs.push(new Node(position, sqrt(sq(mouseX - pmouseX) + sq(mouseY - pmouseY)), R, G, B));    }    
//Eraser    
else if (brushType == "ERASER" && objs.length > 0) 
{      
for (var i = 0; i < objs.length; i++) 
{        
if (sqrt(sq(objs[i].position.x - mouseX) + sq(objs[i].position.y - mouseY)) <= eraserRange) 
{          
objs.splice(i, 1);          
break;
        }      
        }    
        } 
        else if (brushType == "TIMER" && objs.length > 0) 
        {      for (var i = 0; i < objs.length; i++) 
        {        if (sqrt(sq(objs[i].position.x - mouseX) + sq(objs[i].position.y - mouseY)) <= timerRange) 
        {          objs[i].timepast += 2 / FPS;          objs[i].isPlaying = false;        
        }      
        }    
        }  
        }

图像的显示与更新。

for (var i = 0; i < objs.length; i++) 
{    
objs[i].drawing();    
objs[i].update();  
}

参考文献:

http://mc.dfrobot.com.cn/forum.php?mod=viewthread&tid=22951