上一篇文章:填坑!完結娛樂圈明星關系圖譜 發布后,古柳印象里過往留下的坑貌似只剩下 圖像檢索(一):因緣際會與前瞻 的后續實踐代碼(原文里給了參考代碼鏈接)和在豆瓣Top250電影海報上的嘗試效果了。
一想到所有坑都被填了(如果還有啥是我不記得的,請千萬不要提醒我),就覺得真是業界良心,倍感輕松。
于是古柳某日點開 圖像檢索(一):因緣際會與前瞻一文,回顧“佳作”之余,也找了下里面清華美院向帆老師的作品集網站 zeelab Projects。
PS:如果你沒看過這個演講,推薦一看,古柳至今難忘:【一席】向帆:如果把每年的春晚都像蚊香一樣卷起來的話,它就是這樣的
而在這些作品中,古柳更中意且也想實現下類似網頁展示效果的是:AwardPuzzel - zeelab 。下面援引下“官方”介紹,建議去網頁體驗一下:
AwardPuzzel 是一個全國美展油畫類獲獎畫作的數據視覺化作品,收錄了美展第六屆至第十二屆的2276幅獲獎作品,通過動態交互的方式呈現了中國油畫30年間的藝術歷程、形態、色彩、尺寸和地區之間的變化和關系以及中國油畫大師們的藝術思路。
本作品可以被當作研究工具為研究者和評論家使用,亦可作藝術作品欣賞。
我們希望通過這個平臺分享我們的視角,也希望使用者通過自己的瀏覽和觀察得到自己的結論。
全國美展是中國美術界最重要事件,每五年舉辦一次,第六屆是1984年舉辦,第十二屆為2014年舉辦。
雖然古柳不怎么會前端,但自從接觸爬蟲以來,右鍵“審查元素”,查看網頁源代碼的習慣還是有的。
于是不看不知道,一看又引出了后續的諸多故事,借用書上的一句話:“那日也是合該有事”,且聽古柳慢慢道來......
點開網頁源代碼后,找到數據展示和交互的區域對應的代碼自然是不難的。這里為了展示方便,特地丟到 Carbon 里,重點突出下這段代碼。
可以看到 HTML
里主要用了 canvas
標簽,這也沒什么,古柳反正不懂 canvas
,睜眼瞎罷了,也看不出什么名堂。但是卻發現標簽里的 data-processing-soucres
屬性對應的 .pde
文件,特別與眾不同,“聞所未聞,見所未見”,并且想起當初也曾各種搜羅,希冀能復現向帆老師的春晚或美展油畫項目,雖不了了之,但對 processing
這一能實現各種藝術創意的編程語言有了印象。
于是谷歌了下 “HTML+Canvas+Processing”
等關鍵詞,意外地發現:基于 Java
的 Processing
語言的家譜中,還有對應 JavaScript
和 Python
版本,前者即:P5.js
,而這不禁使古柳看到了能在網頁中復現上述效果的希望。
說起來,之前古柳壓根一丁點都沒聽說過 P5.js
,搜了下對應的中文資料也不算多,更偏愛看視頻學習的我,看到萬能的B站上有人搬運了油管上Daniel Shiffman
的教學視頻(1-12節),于是立馬刷了下,p5.js 基礎教程 1-7,并全部跟著敲了遍代碼,雖然無字幕,但還蠻好啃的,有很多針對初學編程的知識講解。(原始鏈接:Code! Programming with p5.js - YouTube)
習得新技能后,立馬用明星關系圖譜的圖片簡單粗暴的拼了下照片墻,雖然離美展油畫的效果差了十萬八千里,但也算是賣出了第一步。
其實以前就沒少拼照片墻,想來大家也都見過爬取微信好友然后拼圖的文章,但古柳還是安利下這篇舊文,里面的圖片絕對值得一看(如果你看完覺得也不咋地,那......也就隨它去吧):用python的PIL庫輕松拼接一百張照片。
再就是幾天前,看到 @愛可可-愛生活
老師的這則微博:Processing 創作的生成藝術 via:おかず?,配圖漂亮就不說了,重點是帶著 Processing
關鍵詞,于是就埋下了想用 P5.js
實現一波的念頭。
幸運地找到了作品的出處:Generative Art #146 via:おかず,欣喜地發現附有 Processing
實現代碼,而且該系列有更多不錯的作品,遂萌發了想將其所有作品用 P5.js
實現一波并開源的想法。
當然因為目前 P5.js
不夠熟練,JavaScript
/ ES6
之類也只是入門,難免有所擔心和顧慮。但在復現這個作品時發現 Processing
和 P5.js
真的很像,很多函數接口官方設計成統一的,極大降低了門檻。
上圖就是古柳用 P5.js
復現的效果,雖然還有些小問題,代碼也不一定最規范,但先行分享,后續再優化哈!可在此網址體驗作品生成效果:https://editor.p5js.org/DesertsX/sketches/GxYHsZg9n
let particles;
const n = 120;
function setup() {
createCanvas(900, 900);
// pixelDensity(2);
colorMode(HSB, 360, 100, 100, 100);
rectMode(CENTER);
newParticles();
}
function draw() {
for (let i in particles) {
let p = particles[i];
p.run();
if (p.isDead()) {
particles.splice(i, 1);
}
}
}
function forms() {
for (let j = 0; j < n; j++) {
let x = random(width), y = random(height);
let s = random(20, 100);
let hs = s / 2;
let c = getCol();
noStroke();
fill(c);
if (random(1) > 0.5) {
for (let i = -s / 2; i < s / 2; i++) {
particles.push(new Particle(x + i, y - hs, c));
particles.push(new Particle(x + i, y + hs, c));
particles.push(new Particle(x - hs, y + i, c));
particles.push(new Particle(x + hs, y + i, c));
}
square(x, y, s);
} else {
for (let a = 0; a < TAU; a += TAU / 360) {
particles.push(new Particle(x + hs * cos(a), y + hs * sin(a), c));
}
circle(x, y, s);
}
}
}
function newParticles() {
// particles = new ArrayList<Particle>();
particles = new Array();
background("#FCFCF0");
forms();
noiseSeed(parseInt(random(100000)));
}
// function mousePressed() {
// newParticles();
// }
function keyPressed() {
// 還沒生效
if (keyCode === 's') {
saveFrame("123.png");
}
}
function getCol() {
let colors = ["#e4572e", "#29335c", "#f3a712", "#a8c686", "#669bbc", "#efc2f0"];
//let colors = ["#880D1E", "#DD2D4A", "#F26A8D", "#F49CBB", "#CBEEF3"];
let idx = parseInt(random(colors.length));
// console.log(idx + colors[idx]);
return colors[idx];
}
class Particle {
constructor(x, y, col) {
this.pos = createVector(x, y);
this.step = 1;
this.angle = random(10);
this.lifeSpan = 100;
this.noiseScale = 800;
this.noiseStrength = 90;
this.col = col;
}
show() {
noStroke();
// fill(this.col, this.lifeSpan);
fill(this.col);
circle(this.pos.x, this.pos.y, 0.5);
}
move() {
this.angle = noise(this.pos.x / this.noiseScale, this.pos.y / this.noiseScale) * this.noiseStrength;
this.pos.x += cos(this.angle) * this.step;
this.pos.y += sin(this.angle) * this.step;
this.lifeSpan -= 0.1;
}
isDead() {
return (this.lifeSpan < 0.0)
}
run() {
this.show();
this.move();
}
}