用p5.js实现动态创意画板

跟之前两篇文章有关联的是,这次也是为了互动媒体大作业写了一篇博客。

这次的作业内容是编写一个绘画系统,提供一系列绘画颜料给用户操作,这颜料可以是画笔、颜料或者滤镜的形式,然后创造出动态/交互的绘画作品。这个绘画系统是对“绘画”的概念进行扩展,但仍然要体现出与传统绘画系统的相似性。

按照习惯我们还是先放出成品图。
(时间原因就只是实现了最基本的功能)

这一次的作业相比较之前的有所进化,我们需要做出一个系统,使得画布上的东西不再局限于静态的东西,可以形式更丰富的展示自己的绘画作品。

一开始脑子里没有太多头绪,就先网上找了一些博客看看能不能激发一下灵感。幸运的是我找到了一篇博客,这篇博客写得挺好,我的这一个作业就是基于这篇博客作者写的框架进行修改的。想要了解的可以戳一戳p5.js可以做什么 之 创意动态绘图板。十分感谢博主的分享!!!

那现在我们正式进入正题。

传统画板与动态画板

问一句,传统画板有啥?

一般来说,对于不从事专业领域的人员,画板大概就是一张纸,而能作用的颜料就是平常我们在文具店能买到的铅笔、彩铅、水彩颜料、油画颜料等等。而对于比较专业一点的,他对于画板的定义又更加广泛了些。数字绘画是常用的艺术表现形式,画家使用ps、sai或者painter等一些软件进行艺术创作。这样,艺术创造编不仅限于纸张,而是可以通过电脑进行绘画。

而我们现在做画板的选择性就更多了。我们可以选择复刻一个软件或者主要实现其中的一项功能。

而我这次主要做的就是形状笔刷的实现。

我们先来看看传统笔刷里面有什么。

在早式传统绘画中的笔刷是这样子的。
这是早期的笔刷。艺术家们如果要进行作品塑造,那就得用上图的笔刷一笔一笔的刻画。

那么电脑绘画是不是也需要这样复杂的操作呢?

回答是,要。

只不过电脑有个更方便的操作就是形状笔刷。


这是我自己在网上找的ps笔刷素材,展示了一个局部缩略图,那么它使用起来是怎么样的呢?

这是我用95号笔刷刷出来的效果。可以看出来叶子的形状已经被我们刷上去了,甚至还带着颜色。

这就是数字绘画中的笔刷。

动态画板

不过既然要创新,那就要从这两个已有的形式找相似点与不同点。

我找到了两个相似点——
1.需要人去画;
2.呈现效果是静态的。

一个最主要的不同点——
数字绘画有形状笔刷可以减少重复性操作。

举个简单的例子,画一片草地。
按照笔者画画的速度,传统绘画需要用笔刷一点一点去细扣,画出一片草地最快五分钟。
数字绘画可以选中对应形状笔刷,用数位笔一刷,三秒一片草地。

可以看出,在不必要的重复操作上,形状笔刷可以是你画画很好的利器了!

于是在设计大作业功能模块的时候,在借鉴之前提到那篇博文的基础上,进行了形状笔刷的添加。具体一点就是实现了动态草地以及动态河流的形状笔刷。

下面是动态图:
我种下了一片草:)
第一次刷可以看出正余弦的图像,多刷几次就像一片草了。

画波光粼粼的湖面~
我画一座山:)
画一个月亮~
(好像变成太阳了…)

代码部分

代码的结构参考的是上面提到的那篇文章p5.js可以做什么 之 创意动态绘图板

代码主体分为两个大框架:
1.功能函数
2.绘制函数

功能函数框架建立
这个函数主要是先把按钮框架打出来。


//FunctionButtonfunction 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");//print("obj[i]");if (this.cmd == "sun") {bR = 200;bG = 200;bB = 200;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;for (var i = 0; i < objs.length; i++) {objs[i].isPlaying = true;}this.cmd = "pause";} else if (this.cmd == "timer") {brushType = "TIMER";} else if (this.cmd == "eraser") {brushType = "ERASER";} else if (this.cmd == "clear") {objs = [];} else if (this.cmd == "save") {saveCanvas("Painting", "png")} else if (this.cmd == "circle") {brushType = "TRIANGLE";pbrushType = "CIRCLE";this.cmd = "triangle";} else if (this.cmd == "triangle") {brushType = "LINES";pbrushType = "TRIANGLE";this.cmd = "lines";} else if (this.cmd == "lines") {brushType = "CIRCLE";pbrushType = "LINES";this.cmd = "circle";} else if (this.cmd == "grass") {brushType = "GRASS";pbrushType = "GRASS";this.cmd = "grass";} else if (this.cmd == "river") {brushType = "RIVER";pbrushType = "RIVER";this.cmd = "river";} else if (this.cmd == "rec") {brushType = "REC";pbrushType = "REC";this.cmd = "rec";}
}

画按钮

FuncBtn.prototype.displayBtn = function() {stroke(0);strokeWeight(1);fill(255, 255, 255);rect(this.x, this.y, this.w, this.h, 5);if (this.cmd == "sun") {//画太阳功能键按钮fill(255, 50, 50);translate(this.x + this.w / 2, this.y + this.h / 2);for (var i = 0; i < 8; i++) {rotate(PI / 4.0);line(0, 0, 8, 8);}resetMatrix();ellipse(this.x + this.w / 2, this.y + this.h / 2, 15, 15);} else if (this.cmd == "moon") {//画月亮功能键按钮fill(255, 255, 50);translate(this.x + this.w / 2, this.y + this.h / 2);arc(-5, 0, 25, 25, PI + HALF_PI, HALF_PI, CHORD);resetMatrix();} else if (this.cmd == "pause") {fill(0);translate(this.x + this.w / 2, this.y + this.h / 2);rectMode(CENTER);rect(-4, 0, 4, 15);rect(4, 0, 4, 15);rectMode(CORNER);resetMatrix();} else if (this.cmd == "play") {fill(0);translate(this.x + this.w / 2, this.y + this.h / 2);triangle(-2, -8, -2, 8, 6, 0);resetMatrix();} else if (this.cmd == "timer") {translate(this.x + this.w / 2, this.y + this.h / 2);noFill();ellipse(0, 0, 22, 22);ellipse(0, 0, 25, 25);fill(0);ellipse(0, 0, 3, 3);strokeWeight(2);line(0, 0, 5, 0);line(0, 0, 0, -7);resetMatrix();} else if (this.cmd == "eraser") {fill(0);noStroke();translate(this.x + this.w / 2, this.y + this.h / 2);textSize(25);textAlign(CENTER);textStyle(BOLD);text("E", 0, 8);resetMatrix();} else if (this.cmd == "clear") {fill(0);noStroke();translate(this.x + this.w / 2, this.y + this.h / 2);textSize(25);textAlign(CENTER);textStyle(BOLD);text("C", 0, 8);resetMatrix();
} else if (this.cmd == "save") {fill(0);noStroke();translate(this.x + this.w / 2, this.y + this.h / 2);textSize(25);textAlign(CENTER);textStyle(BOLD);text("S", 0, 8);resetMatrix();} else if (this.cmd == "circle") {fill(0);translate(this.x + this.w / 2, this.y + this.h / 2);ellipse(6, -2, 10, 10);ellipse(-5, -5, 5, 5);ellipse(3, 8, 4, 4);resetMatrix();} else if (this.cmd == "triangle") {fill(0);translate(this.x + this.w / 2, this.y + this.h / 2);triangle(0, 0, 10, 0, 5, -8);triangle(-5, 8, 5, 8, 0, 0);triangle(-8, -5, -3, -5, -5.5, -9);resetMatrix();} else if (this.cmd == "lines") {fill(0);strokeWeight(2);translate(this.x + this.w / 2, this.y + this.h / 2);line(-5, -10, 5, 0);line(-10, -10, 10, 10);line(-5, 0, 5, 10);resetMatrix();}else if (this.cmd == "grass") {fill(50,200,50);strokeWeight(2);stroke(50,200,50);translate(this.x + this.w / 2, this.y + this.h / 2);triangle(0, 8, 10, 8, 5, -5);triangle(-5, 8, 5, 8, 0, -10);triangle(-8, 8, -3, 8, -5.5,-5);resetMatrix();} else if (this.cmd == "river") {fill(50,50,200);strokeWeight(2);stroke(50,50,200);translate(this.x + this.w / 2, this.y + this.h / 2);line(-8, -5, 0, -5);line(-10, 0, 10, 0);line(-5,5, 5, 5);resetMatrix();} else if (this.cmd == "rec") {fill(0);//strokeWeight(2);//stroke(50,50,200);translate(this.x + this.w / 2, this.y + this.h / 2);rect(-7,8,16,-16);resetMatrix();}
}

后面颜色选择模块的代码和功能按钮的实现差不多,就不再多提。

下面的代码是重头戏!!!
获取当前鼠标的坐标、颜色、形状初始大小,时间设置为0,初始状态播放为true,暂停为false。
我最主要创新点在于草地形状笔刷和动态河流的绘制。

草的基本形状:
triangle(0, 8, 10, 8, 5, -5);
triangle(-5, 8, 5, 8, 0, -10);
triangle(-8, 8, -3, 8, -5.5,-5);
定义了草的变化趋势;
var c=cos(this.timepast) * this.baseSize.y - this.size.y * 0.01;

最终草的函数为:
triangle(0+c, 8, 10, 8, 5c/2, -5c/3);
triangle(-5+c, 8, 5, 8, 0+c/4, -10c/2);
triangle(-8+c, 8, -3, 8, -5.5
c/2,-5*c);
这样可以模拟草的动态变化。

河流基本形状:
line(-8, -5, 0, -5);
line(-10, 0, 10, 0);
line(-5,5, 5, 5);
定义了河流基本单位形状
var c=cos(this.timepast) * this.baseSize.y - this.size.y * 0.01;
(这里和草的区别在于没有绝对值,可以正负交替,会更适合河流动态。)
最终河流函数:
line(-8+c, -5, 0+c, -5);
line(-10c/2, 0, 10c, 0);
line(-5+c,5, 5+c, 5);


//Nodefunction 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;
}
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 == "TRIANGLE") {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));triangle(sin(this.timepast) * this.baseSize.x - this.size.x * 1.5 * 0.5,cos(this.timepast) * this.baseSize.y - this.size.y * 1.5 * 0.5,sin(this.timepast) * this.baseSize.x + this.size.x * 1.5 * 0.5,cos(this.timepast) * this.baseSize.y - this.size.y * 1.5 * 0.5,sin(this.timepast) * this.baseSize.x * 0.5,cos(this.timepast) * this.baseSize.y + this.size.y * 1.5 * 0.9 * 0.5);fill(this.size.x * this.R / 10, this.size.x * this.G / 10, this.size.x * this.B / 10, 255);triangle(sin(this.timepast) * this.baseSize.x - this.size.x * 0.5,cos(this.timepast) * this.baseSize.y - this.size.y * 0.5,sin(this.timepast) * this.baseSize.x + this.size.x * 0.5,cos(this.timepast) * this.baseSize.y - this.size.y * 0.5,sin(this.timepast) * this.baseSize.x * 0.5,cos(this.timepast) * this.baseSize.y + this.size.y * 0.9 * 0.5);resetMatrix();} else if (this.shapeType == "LINES") {//translate(this.position.x,this.position.y);// rotate(this.rotateAngle);strokeWeight(2 + this.size.x / 1.5 * 0.75);stroke(this.size.x * this.R / 8, this.size.x * this.G / 8, this.size.x * this.B / 8, round(sin(this.timepast) * 128));line(this.pmouseX, this.pmouseY, this.mouseX, this.mouseY);strokeWeight(1.5 + this.size.x / 1.5 * 0.5);stroke(this.size.x * this.R / 8, this.size.x * this.G / 8, this.size.x * this.B / 8, 255);line(this.pmouseX, this.pmouseY, this.mouseX, this.mouseY);resetMatrix();} else if (this.shapeType == "GRASS") {//translate(this.position.x,this.position.y);// rotate(this.rotateAngle);translate(this.position.x, this.position.y);var r=this.size.x * 50 / 10;var g=this.size.x * 200 / 10; var b=this.size.x * 50 / 10;fill(r,g,b);//strokeWeight(2);//stroke(50,200,50);translate(this.x + this.w / 2, this.y + this.h / 2);var c=abs(cos(this.timepast) * this.baseSize.y - this.size.y * 0.01);triangle(0+c, 8, 10, 8, 5*c/2, -5*c/3);triangle(-5+c, 8, 5, 8, 0+c/4, -10*c/2);triangle(-8+c, 8, -3, 8, -5.5*c/2,-5*c);resetMatrix();} else if (this.shapeType == "RIVER") {translate(this.position.x,this.position.y);// rotate(this.rotateAngle);//calcWave();//renderWave();var c=cos(this.timepast) * this.baseSize.y - this.size.y * 0.01;var r=this.size.x * this.R / 10;var g=this.size.x * this.G / 10; var b=this.size.x * this.B / 10;fill(r,g,b,round(sin(this.timepast) * 128));strokeWeight(2);stroke(r,g,b);line(-8+c, -5, 0+c, -5);line(-10*c/2, 0, 10*c, 0);line(-5+c,5, 5+c, 5);resetMatrix();} else if (this.shapeType == "REC") {translate(this.position.x,this.position.y);var r=this.R * 1.5;var g=this.G * 1.5; var b=this.B * 1.5;noStroke();fill(r,g,b,128);rect(20,20,-20,-20);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;}
}

下面绘制函数因为和一般的p5.js项目文件差不多就不做过多解释,这里给出我项目的链接,有兴趣可以点进去看一看动态形状笔刷

用p5.js实现动态形状笔刷相关推荐

  1. P5.JS绘制动态图形

    P5.JS绘制动态图形 一.平台 第一次使用p5.js进行码绘,此次直接使用P5.JS官网的在线编辑器进行编写,完成后点击file->download即可保存到本地. 在正式绘制之前,我经过小小 ...

  2. 互动媒体技术——基于p5.js实现动态图形临摹与拓展:炫彩光影的千变万化!

    博文索引目录: 1. 引言 2. 临摹结果对比 3. 临摹过程 3.1 准备工作 3.2 原图规律--语言描述 3.3 原图规律--数学与代码描述 3.4 完整代码 4. 创意拓展 4.1 拓展一-- ...

  3. 用P5 JS绘制动态绚丽烟花——动态篇

    上一节我们探究了绘制静态图像手绘与码绘的差异,但是万事万物,变化万千,有很多东西仅仅用静态是无法描述的. 正如恩格斯所言:"整个自然界,从最小的东西到最大的东西,从沙粒到太阳,从原生生物到人 ...

  4. p5.js 实现动态互动画板

    目标 编写一个"绘画系统",提供一系列绘画材料(例如画笔/颜料/滤镜)给用户操作,以创作出动态/交互的绘画作品.这个绘画系统是对"绘画"的概念的扩展,但仍然体现 ...

  5. p5.js之动态魔幻画板

    基于p5.js的魔幻画板 一.起因 本学期的互动媒体技术大作业要求每个人做一个"绘画系统",提供一系列绘画材料(例如画笔/颜料/滤镜)给用户操作,以创作出动态/交互的绘画作品.这个 ...

  6. p5.js实现动态旋转的小球

    实验要求: 从给出的参考资料中的"动态图形艺术"中选取不少于1幅作品,用编程方式临摹并进行拓展.编程语言推荐p5,processing.写一篇文章发表为博文.推文等形式,描述从原作 ...

  7. 第一次码绘——P5.JS实现动态图片临摹

    所临摹图片 临摹过程 首先 我们先仔细观察这张图片的静态图片,确定其图案 我这张图截的效果并不是很好,但也可以看出它是由不同数量的小圆组成一个大圆,那我们应该怎么处理呢?答案是一个个画就完事了(误), ...

  8. 互动媒体技术-用p5.js临摹动态图片

    1,临摹图片 2,图像运动规律 原图中一共有36个等大的四分之三圆形,按照一定的规律转动,可以等效为圆形不动,每个圆上有以其半径为边长的矩形在绕圆形的原点转动,每四个为一组,其他部分都是在重复这四个的 ...

  9. 用P5 JS绘制二维动画场景——静态篇

    绘画 根据wiki百科的定义,绘画是在技术层面上,是一个以表面作为支撑面,再在其之上加上颜色的行为,那些表面可以是纸张.油画布.木材.玻璃.漆器或混凝土等,加颜色的工具可以是画笔.也可以是刀.海绵或是 ...

最新文章

  1. Springboot+Thymeleaf+layui框架的配置与使用
  2. ruby gem 本地安装方法
  3. python零基础实例-python零基础练手项目100+
  4. 简述用 MAT 分析 Android 应用OOM
  5. 序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
  6. 微信公众平台消息接口开发(11)地理位置查询附近商家
  7. int类型的实参与int*类型的形参不兼容_谁告诉的你们Python是强类型语言!站出来,保证不打你!...
  8. boost::math::bernoulli_b2n用法的测试程序
  9. SAP UI5 初学者教程之五:视图控制器初探 试读版
  10. css找某个元素的下个子元素,CSS可以检测一个元素有多less个子元素?
  11. 47-点分十进制表示法
  12. abb机器人goto指令用法_详解ABB机器人编程含中英文指令对照
  13. python爬虫论文总结与展望怎么写_论文总结与展望怎么写
  14. Bootstrap(包括scss)复习
  15. 教务(选课)管理系统
  16. 内容管理系统CMS简介
  17. c++输入10个数/输入n个数,求其平均值
  18. DllNotFoundException
  19. 一位女程序员的奋斗路程
  20. linux 硬盘对拷,比ghost好用

热门文章

  1. i5 12600kf参数i5 12600kf功耗
  2. Kaggle淋巴结病理切片有无癌细胞鉴别建模:Logistic+SVM+RandomForest+CNN
  3. 影像组学病理切片案例教学课程(免费赠送)另有现成的代码,模型供大家上手使用
  4. 用touchstart、touchmove、touchend简陋实现左右滑动【触摸事件】
  5. 页游arpg服务端java_ARPG手机网游java服务端主程初哥遇到的一些坑总结
  6. 微软Chromium Edge浏览器发布,附带Edge浏览器下载地址!
  7. Vue3 使用 百度地图
  8. 大学计算机实验报告信息的表示与转换,大学计算机实验报告一(8页)-原创力文档...
  9. USB host 控制芯片CH374
  10. python实现双屏同时播放视频_多画面同时播放视频 如何在一个视频画面中播放多个视频?...