利用P5.js创作自画像

mac2026-04-24  6

利用P5.js创作自画像

上一篇博文运用P5.js创作了两幅动态图片,本篇博文将综合运用P5.js的函数,创作一幅以景表意的自画像。 先贴上gif: 说是自画像,实际上豹子和人物加起来才算是真正的自己。这只豹子来源于一部小说,表达固执的性格,左边一起坐着的是一个眺望远方的人。 功能: 打开网页,自动播放肖邦的《夜曲》; 鼠标移动,豹子的眼睛跟随; 人的眼睛一直在眨动; 鼠标移动到萤火虫附近,萤火虫会自动散开,鼠标离开则又聚集到一起。

整个自画像放在森林的背景下,豹子和人坐在树上,叶子在摇晃,萤火虫聚集在一起……首先分解一下这个图像:(1)背景;(2)树叶;(3)豹子;(4)人;(5)萤火虫 接下来分开讲述一下绘制过程 (1)背景: 背景很简单,实际上就是将画布颜色设置为绿色:

background(144,238,144);

(2)树叶: 树叶只有两种,一个是蕨类植物,一个是阔叶植物,两种绘制方式相同,以阔叶植物为例: 叶柄是一条加粗的直线,叶子的整体是由一个半椭圆、三个四边形、一个三角形构成的,可以分解为下图: 单独绘制一个叶子基本上就是靠耐心和细心程度,关键是如何将一片叶子复制到其他部分并且能够调整大小和方向。 这里我采用的是把定义每个形状的关键参数(椭圆中心、半径,三角形三个顶点,四边形四个顶点等)都设置成变量 t 的倍数;同时要定义一个基准点,比如椭圆的中心点,其他部位的绘制均设置成以这个基准点为参考。这样就可以把叶子封装成一个函数,只需要更改基准点和 t 就能够把叶子的代码多次复用。

function leaf(x,y,l,thta,r,g,b,t,wave) {//x y为基准点,t为倍数 push(); translate(x,y); rotate(thta/100); rotate(wave); translate(-x,-y); stroke(r,g,b); strokeWeight(1); fill(r,g,b); arc(x,y,l,l/4,PI,0); triangle(x-l/2*t,y,x-l/4*t,y,x-l/2*t+l/5*t,y+l*(3/20)); quad(x-l/2*t+l/5*t,y,x-l/2*t+l/2*t,y,x-l/2*t+l*(11/20)*t,y+l*(3/20),x-l/2*t+l/4*t,y+l*(3/20)); quad(x-l/2*t+l/2*t,y,x-l/2*t+l*(3/4)*t,y,x-l/2*t+l*(4/5)*t,y+l*(3/20),x-l/2*t+l*(3/5)*t,y+l*(3/20)); quad(x-l/2*t+l*(3/4)*t,y,x-l/2*t+l*(19/20)*t,y,x-l/2*t+l*t,y+l*(3/20),x-l/2*t+l*(17/20)*t,y+l*(3/20)); quad(x-l/2*t+l*(9/10)*t,y,x-l/2*t+l*t,y-3,x-l/2*t+l*(11/10)*t,y+l*(3/20),x-l/2*t+l*(21/20)*t,y+l*(3/20)); strokeWeight(l/20); line(x-l/3*t,y-l/15,x-l*t,y+l/4); pop(); }

另外,为了使叶子能够晃动,要设置一个参数wave,wave随时间大小作做正弦变换,rotate(wave) 让叶子绕自己的中心旋转一定角度。如何让叶子以自己为中心旋转在上一篇博文中已经提到。 (3)豹子: 豹子的绘制是一个工程量巨大的工作。首先把豹子分解成下面的形状: 基本形状只要找好位置就能够构建出来,尾巴、耳朵、前肢都是用贝塞尔曲线绘制的,找准起始点和控制点就能够确定好位置。其他部分像是脸部细节、斑点都需要去尝试寻找位置。 另外,包子的眼睛是可以随着鼠标移动的,需要将眼睛的位置设置成与mouseX、mouseY相关。 (4)人: 人物的脸是由圆形和贝塞尔曲线构成的,贝塞尔曲线主要构成了人的下巴。眼睛的轮廓也是运用了贝塞尔曲线,身体部分都是一些简单的几何体。 值得一提的是眨眼的表现方式,眨眼实际上是两个半圆在做周期性的上下运动,因为上面的半圆被头发遮挡,下面的半圆颜色与脸的颜色一致,所以构造出了眨眼的效果。周期函数和代码如下:

function eyeball(x,y,s) { noStroke(); fill(255,218,185); arc(x,y-20*f(millis()/2000),s,s,PI,0); arc(x,y+20*f(millis()/2000),s,s,0,PI); } function f(X) { x=X%2.5; if(x<1) return 1-x; if(x>=1&&x<1.5) return 0; if(x>=1.5) return x-1.5; }

(5)萤火虫: 萤火虫是一群圆形在原地做周期性运动,这里运用了正弦、余弦、对数函数的性质,鼠标靠近时所在位置会向周围扩散。

worm(w/6,3*h/5); if (mouseX>w/7&&mouseX<w/5&&mouseY>2*h/5&&mouseY<4*h/5) { move++; } else { if (move>0) { move--; } } function worm(x,y) { noStroke(); fill(255,255,0); circle(x-move+10*cos(millis()/500),y-move+15*cos(millis()/500),10); circle(x+move+40+10*cos(millis()/500),y-move+15*cos(millis()/500),10); circle(x-move+50+10*sin(millis()/500),y+move+20+30*cos(millis()/500),10); circle(x+move+10*cos(millis()/500),y+move+40+15*sin(millis()/500),10); circle(x+1.5*move+80+10*cos(millis()/500),y+move+15*cos(millis()/500),10); circle(x-move+20+10*sin(millis()/500),y+move+60+15*cos(millis()/500),10); circle(x+move+30*cos(millis()/500),y-move+70+15*sin(millis()/500),10); circle(x+move+30+10*cos(millis()/500),y+move+15*log(millis()/500),10); circle(x-move+55+10*sin(millis()/500),y+move+60+15*cos(millis()/500),10); circle(x+move+90+20*cos(millis()/300),y-move+70+15*sin(millis()/500),10); }

其他部分: 播放《夜曲》需要预先加载音乐文件,然后在setup中自动播放,注意不要放错位置,放到draw中会出现《夜曲》以不同的起始时间多次同时播放的情况。

function preload() { soundFormats('mp3', 'ogg'); mySound = loadSound('music.mp3'); } function setup() { // put setup code here mySound.setVolume(0.1); mySound.play(); createCanvas(window.innerWidth, window.innerHeight); }

总结: 1.P5.js的艺术作品如果存在重复的情况,可以考虑封装成函数,通过代码复用减少工作量,但是需要注意基准点和放缩倍数的选取; 2.从萤火虫的舞动中发现,通过利用数学函数曲线,可以让萤火虫的运动具有非常优雅的轨迹。

最新回复(0)