P5.js创意自画像编程
要求自画像创作坐标的计算颜色填充动态背景与交互功能的添加背景动态图添加声音与图片时间停止效果
最终代码
要求
主题:用编程方式创作一幅介绍自己的作品
作品: 一件编程创意作品,必须实现动态效果或交互效果;作品录制一段一分钟内的视频;作品可以是具象化地描绘自己的形象,也可以是任何形式表现自己的兴趣、追求、特色、经历等;
自画像创作
想了好久自己的自画像是什么样子,最后决定画一个JOJO的奇妙冒险里的迪奥。不是因为自己长得像,大概是我也不想做人了吧。因为是要码绘,所以过于复杂的图案代码敲起来太浪费时间,就设计了一个Q版的造型。最终图如下: 最终成果图:
坐标的计算
由于dio爷那飘逸的发型,图像中大部分需要运用曲线。查了查p5的参考https://p5js.org/zh-Hans/reference,p5中的曲线用的是贝塞尔曲线算法。 那么问题就出现了,如何才能准确的找到自己在纸上画的图案的控制点与锚点坐标呢? 看到贝塞尔曲线,突然想到之前做过的图形学作业,制作一个简单的画图系统。其中的曲线就用到了贝塞尔曲线算法,可以通过移动鼠标在屏幕获取鼠标坐标,就能看到锚点与控制点坐标了。于是就翻了翻之前的作业,虽然简陋了点,但刚好能解决这个问题。不管白猫黑猫,逮到耗子就是好猫。剩下的只是繁重的坐标的记录。
颜色填充
用bezier曲线画好图形后,用fill()函数填充。发现并不能达到我们想要的效果。 虽然已经形成封闭的形状,但fill()函数并没有完美的填充,大概是顶点的遍历问题。解救办法是改用bezierVertex()函数。
动态背景与交互功能的添加
背景动态图
因为人物来自动漫,并且是dio爷标志性”扣no迪奥哒“动作,所以背景希望添加上漫画的网线效果。具体代码参考了网上的生成光芒线的代码https://wow.techbrood.com/fiddle/32556
var angle
= 0;
var offset
= 30;
var offset2
= 20000;
var scalar
= 90;
var speed
= 1;
function setup() {
createCanvas(500, 500);
background(200);
stroke(255, 0, 0)
strokeWeight(.1);
}
function draw() {
var y1
= offset
+ sin(angle
) * scalar
;
var y2
= offset2
+ sin(angle
+ 0.4) * scalar
;
for (var i
= 0; i
< 1; i
++) {
translate(width
/ 2, height
/ 2)
rotate(angle
)
push()
y1
= offset
+ sin(angle
) * scalar
;
rotate(angle
)
line(0, 10 + angle
/ 9, 1000, y2
);
angle
+= speed
;
pop()
}
}
添加声音与图片
通过P5.js官网的在线编辑器,很容易在项目中添加声音与图像资源,具体操作参考https://www.php.cn/js-tutorial-395952.html。但将项目下载并在网页打开就无法加载音频图片资源,还不知道怎么解决。添加好资源后加上鼠标交互就能达到很好打效果。
function loading() {
if (mouseIsPressed
) {
if (mouseButton
=== RIGHT) {
mySound
.setVolume(1);
mySound
.play();
imageMode(CENTER);
image(img
, 200, 200);
tint(0, 153, 204, 126);
}
if (mouseButton
=== LEFT) {
mySound
.setVolume(1);
mySound1
.play();
imageMode(CENTER);
image(img2
, 200, 200);
imageMode(CENTER);
image(img1
, 200, 200);
tint(0, 153, 204, 126);
}
}
}
时间停止效果
迪奥在动漫中喊出“the world !”会产生时停。所以想着在右上角添加一个时钟,点击左键会加载图片和音频并让始终停止。 下面是时钟代码,艺术字体参考网上:https://mlln.cn/2018/06/06/p5.js%E6%95%99%E7%A8%8B06-%E6%98%BE%E7%A4%BA%E6%96%87%E5%AD%97/
var showEllipse
= false;
let value
= 0;
function preload() {
let style
= document
.createElement('link')
style
.rel
= "stylesheet"
style
.href
= 'https://fonts.googleapis.com/css?family=Gaegu'
document
.getElementsByTagName('head')[0].appendChild(style
)
}
function setup() {
createCanvas(800, 800);
textFont("Gaegu");
fill(123, 0, 0)
stroke(255)
}
function draw() {
background(220);
time();
}
function mouseClicked() {
if (mouseButton
=== CENTER) {
showEllipse
= !showEllipse
;
if (value
=== 0) {
noLoop();
value
= 255;
} else {
loop();
value
= 0;
}
}
}
function time() {
let y
= year();
let m
= month();
let d
= day();
let h
= hour();
let mi
= minute();
let s
= second();
let millisecond
= millis();
textSize(26);
text('current time: ' + y
+ '-' + m
+ '-' + d
+ ' ' + h
+ ':' + mi
+ ':' + s
+ ':' + millisecond
, 25, 60);
if (showEllipse
) {
ellipse(200, height
/ 2, 50, 50);
}
}
function sleep(milliseconds
) {
setTimeout(function() {
var start
= new Date().getTime();
while ((new Date().getTime() - start
) < milliseconds
) {
}
}, 0);
}
代码很简单,用了p5中写好的时间函数获取系统当前时间。问题是如何产生时停效果?试了很多方法,什么计时器setTimeout()函数或自定义的sleep()函数都不好使,最后发现p5中有一个noLoop()函数。 通过停止 p5.js 持续重复执行 draw() 内的代码达到时停效果。但又出现了一个问题:通过鼠标交互,左键一下,时间停止,假如停在了当前的15点25分12秒,如果是时停,那么再次点击鼠标时,时间应该从25分13秒开始,然而再次点击,时间只会继续跳到系统当前时间。所以这不是世界的时停,这是绯红之王的时删,感觉我参透了JOJO(捂脸笑哭)。而且我不知道怎么改,觉得最开始我应该画一个迪亚波罗。 还有一个问题就是显示的时间总是后一秒盖住前一秒,代码在其他跑就没这个问题,不知道怎么回事,有待改良。
最终代码
var img
;
var img1
;
var img2
;
var sign
;
let value
= 0;
var angle
= 0;
var offset
= 30;
var offset2
= 20000;
var scalar
= 90;
var speed
= 8;
function preload() {
soundFormats('mp3', 'ogg');
mySound1
= loadSound('theworld1.mp3');
mySound
= loadSound('buzuorenle.mp3');
img
= loadImage("buzuoren.png");
img1
= loadImage("theworld1.png");
img2
= loadImage("theworld2.png");
let style
= document
.createElement('link')
style
.rel
= "stylesheet"
style
.href
= 'https://fonts.googleapis.com/css?family=Gaegu'
document
.getElementsByTagName('head')[0].appendChild(style
)
}
function setup() {
createCanvas(900, 580);
background(210);
sign
= 0;
textFont("Gaegu");
}
function draw() {
time();
loading();
noFill();
stroke(255, 170, 149);
strokeWeight(4);
beginShape();
fill(255, 230, 215);
vertex(283, 405);
bezierVertex(303, 381, 339, 385, 341, 402);
bezierVertex(274, 426, 309, 474, 256, 503);
bezierVertex(151, 528, 216, 355, 283, 405);
endShape();
noFill();
bezier(283, 405, 301, 428, 275, 448, 250, 415);
bezier(247, 414, 299, 438, 270, 469, 234, 438);
bezier(234, 440, 281, 455, 257, 485, 224, 457);
bezier(224, 456, 271, 475, 251, 501, 213, 480);
stroke(255, 216, 68);
strokeWeight(4);
beginShape();
fill(255, 238, 176);
vertex(391, 433);
bezierVertex(367, 437, 338, 460, 321, 478);
bezierVertex(378, 506, 571, 498, 645, 450);
bezierVertex(594, 424, 559, 405, 523, 398);
bezierVertex(523, 398, 391, 433, 391, 433);
endShape();
beginShape();
fill(255, 230, 215);
vertex(402, 381);
bezierVertex(408, 391, 408, 391, 411, 407);
bezierVertex(317, 483, 611, 419, 492, 388);
bezierVertex(492, 388, 481, 367, 481, 367);
bezierVertex(481, 367, 402, 381, 402, 381);
endShape();
stroke(255, 170, 149);
strokeWeight(4);
bezier(402, 381, 411, 400, 416, 413, 417, 423);
bezier(482, 376, 488, 384, 492, 392, 499, 400);
beginShape();
fill(255, 230, 215);
vertex(588, 285);
bezierVertex(642, 178, 731, 342, 597, 320);
bezierVertex(539, 435, 284, 416, 324, 311);
bezierVertex(295, 357, 248, 274, 316, 268);
bezierVertex(316, 268, 316, 160, 316, 160);
bezierVertex(316, 160, 459, 100, 459, 100);
bezierVertex(459, 100, 592, 153, 592, 153);
bezierVertex(592, 153, 588, 285, 588, 285);
endShape();
stroke(255, 216, 68);
strokeWeight(4);
beginShape();
fill(255, 238, 176);
vertex(358, 207);
bezierVertex(346, 196, 336, 214, 337, 251);
bezierVertex(337, 251, 326, 237, 326, 237);
bezierVertex(318, 247, 328, 263, 332, 294);
bezierVertex(321, 276, 308, 256, 307, 253);
bezierVertex(302, 236, 303, 277, 303, 277);
bezierVertex(289, 264, 274, 267, 272, 250);
bezierVertex(262, 231, 283, 210, 259, 189);
bezierVertex(259, 189, 283, 203, 283, 203);
bezierVertex(268, 169, 278, 145, 310, 121);
bezierVertex(310, 121, 309, 151, 309, 151);
bezierVertex(307, 125, 355, 112, 363, 67);
bezierVertex(363, 67, 379, 107, 379, 107);
bezierVertex(385, 63, 455, 76, 478, 40);
bezierVertex(478, 40, 466, 74, 466, 74);
bezierVertex(497, 45, 563, 113, 618, 83);
bezierVertex(618, 83, 591, 110, 591, 110);
bezierVertex(617, 104, 639, 115, 663, 144);
bezierVertex(663, 144, 624, 138, 624, 138);
bezierVertex(646, 134, 659, 178, 671, 221);
bezierVertex(644, 204, 647, 218, 629, 209);
bezierVertex(669, 214, 656, 279, 716, 292);
bezierVertex(698, 308, 674, 308, 665, 308);
bezierVertex(665, 308, 696, 327, 696, 327);
bezierVertex(676, 334, 655, 330, 634, 324);
bezierVertex(621, 339, 628, 347, 640, 362);
bezierVertex(625, 362, 611, 356, 602, 351);
bezierVertex(602, 351, 600, 382, 600, 382);
bezierVertex(587, 375, 577, 367, 568, 355);
bezierVertex(582, 342, 590, 330, 597, 320);
bezierVertex(731, 342, 642, 178, 588, 285);
bezierVertex(589, 258, 575, 237, 561, 230);
bezierVertex(553, 242, 546, 250, 538, 257);
bezierVertex(548, 235, 544, 219, 529, 218);
bezierVertex(525, 194, 503, 127, 449, 169);
bezierVertex(424, 124, 384, 167, 358, 207);
endShape();
noFill();
bezier(671, 221, 647, 195, 626, 228, 606, 189);
bezier(358, 207, 346, 196, 336, 214, 337, 251);
bezier(341, 152, 324, 186, 303, 211, 337, 251);
bezier(326, 237, 318, 247, 328, 263, 332, 294);
bezier(544, 149, 591, 184, 565, 218, 540, 257);
bezier(586, 286, 599, 259, 609, 233, 624, 234);
bezier(302, 236, 308, 256, 321, 276, 332, 294);
stroke(0, 128, 64);
strokeWeight(4);
beginShape();
fill(161, 190, 160);
vertex(507, 193);
bezierVertex(476, 206, 425, 203, 392, 192);
bezierVertex(392, 192, 390, 225, 390, 225);
bezierVertex(416, 230, 480, 237, 508, 221);
bezierVertex(508, 221, 502, 193, 507, 193);
endShape();
bezier(448, 245, 410, 223, 405, 180, 450, 196);
bezier(448, 245, 494, 223, 488, 177, 450, 196);
stroke(255, 216, 68);
strokeWeight(4);
beginShape();
fill(255, 238, 176);
vertex(398, 244);
bezierVertex(308, 193, 392, 149, 429, 134);
bezierVertex(442, 124, 440, 155, 449, 168);
bezierVertex(467, 144, 472, 132, 485, 142);
bezierVertex(514, 153, 568, 223, 489, 249);
bezierVertex(527, 203, 489, 149, 449, 176);
bezierVertex(424, 144, 377, 193, 398, 244);
endShape();
stroke(255, 170, 149);
strokeWeight(4);
beginShape();
fill(255);
vertex(480, 337);
bezierVertex(443, 386, 423, 401, 408, 344);
bezierVertex(428, 348, 468, 343, 480, 337);
endShape();
beginShape();
fill(255, 255, 255);
vertex(480, 337);
bezierVertex(487, 342, 491, 353, 494, 361);
bezierVertex(501, 347, 503, 334, 505, 330);
bezierVertex(498, 331, 492, 336, 480, 337);
endShape();
beginShape();
fill(242, 43, 1);
vertex(419, 283);
bezierVertex(390, 313, 404, 327, 345, 262);
bezierVertex(345, 262, 419, 283, 419, 283);
endShape();
beginShape();
fill(242, 43, 1);
vertex(573, 267);
bezierVertex(492, 326, 521, 315, 478, 282);
bezierVertex(478, 282, 573, 267, 573, 267);
endShape();
stroke(255, 216, 68);
strokeWeight(4);
beginShape();
fill(255, 238, 176);
vertex(351, 237);
bezierVertex(351, 237, 408, 272, 408, 272);
bezierVertex(408, 272, 416, 259, 416, 259);
bezierVertex(416, 259, 351, 237, 351, 237);
endShape();
beginShape();
fill(255, 238, 176);
vertex(540, 241);
bezierVertex(540, 241, 477, 260, 477, 260);
bezierVertex(477, 260, 480, 274, 480, 274);
bezierVertex(480, 274, 540, 241, 540, 241);
endShape();
beijing();
}
function beijing() {
stroke(0);
strokeWeight(1);
var y1
= offset
+ sin(angle
) * scalar
;
var y2
= offset2
+ sin(angle
+ 0.4) * scalar
;
for (var i
= 0; i
< 1; i
++) {
translate(width
/ 2, height
/ 2)
rotate(angle
)
push()
y1
= offset
+ sin(angle
) * scalar
;
rotate(angle
)
line(0, 10 + angle
/ 9, 1000, y2
);
angle
+= speed
;
pop();
}
}
function loading() {
if (mouseIsPressed
) {
if (mouseButton
=== RIGHT) {
mySound
.setVolume(1);
mySound
.play();
imageMode(CENTER);
image(img
, 200, 200);
tint(0, 153, 204, 126);
}
if (mouseButton
=== LEFT) {
mySound
.setVolume(1);
mySound1
.play();
imageMode(CENTER);
image(img2
, 200, 200);
imageMode(CENTER);
image(img1
, 200, 200);
tint(0, 153, 204, 126);
}
}
}
function time() {
fill(123, 0, 0)
stroke(255)
let h
= hour();
let mi
= minute();
let s
= second();
let millisecond
= millis();
textSize(35);
text(h
+ ':' + mi
+ ':' + s
, 650, 60);
}
function mouseClicked() {
if (mouseButton
=== LEFT) {
if (value
=== 0) {
noLoop();
value
= 255;
} else {
loop();
value
= 0;
}
}
}