1.7 開始第一幅“碼繪”——用時間控制變數,讓懵逼臉動起來
阿新 • • 發佈:2018-12-29
引言——碼繪的缺點和優點
在之前的程式中,所有懵逼臉的表情都是僵住不動的。
如此一來,相比於傳統紙面繪畫,碼繪技術還體現不出什麼優點,可以說還有一系列缺點:
- 編寫程式碼麻煩容易出現筆誤,
- 學習程式語言代價高,
- 不夠直觀,
- 缺乏操縱畫筆時的控制感和愉悅感,
- 要用完全邏輯的方式來考慮作畫過程,抒情性、即興性和表意性顯得不足。
但是,用程式設計來作畫,可以實現紙面繪畫做不出的效果,主要就是兩種:動態和互動。
在之前的程式中,互動性已經略有體現,我們已經實現了讓懵逼臉可以跟隨滑鼠移動。
然而由於缺乏動態,表情僵硬,還很難說有”碼繪“什麼特別的優勢。
這一節,我們將讓懵逼臉動起來,它不僅可以跟隨滑鼠移動,自身的尺寸/表情/五官位置還繪隨時間而發生變化。
一個簡單的動畫程式案例
先看一個極簡的程式例:
1 2 3 4 5 6 7 8 9 10 11 |
function setup() { createCanvas(640,480);}function draw() { // 呼叫函式second(),獲得程式執行經過的秒數 var s = second(); // 在螢幕中心畫圓圈,長寬等於秒數 ellipse(320,240,s,s);} |
程式碼也示意了簡單的動畫技術,其要點有二:
- draw()函式隨時間反覆執行;
- 每一次執行draw()函式,通過呼叫函式second()獲得程式當前經過的秒數,並且在繪製圓形時用獲得的時間數值來指定圓形的尺寸。
製造動態的基本方法
從上述簡單的動畫示例,可以匯出製作動態的基本方法,寫成一個函式如下:
1 2 3 4 5 |
function LoopFunction()// 這個函式應該被框架程式持續反覆呼叫{ UpdateEveryThing(); // 更新每一個東西 DrawEveryThing(); // 繪製每一個東西} |
可見,有兩個要領:1.要定義一個在程式框架中被持續反覆呼叫的函式;2.在該函式中,要更新並繪製一些東西; 再對應到p5.js提供的程式框架,可見其draw()函式符合上述要求,只要能夠在draw()函式中更新需要繪製物體的資訊並將它們繪製出來,就能產生動畫效果。
讓懵逼臉移動起來
利用之前寫出的drawConfuseFace()函式,即:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
// 畫懵逼臉function drawConfuseFace( posX, posY, // 臉部中心位置 faceSize, // 臉部尺寸 scaleMouth, // 嘴巴尺度比例,相對於臉部尺寸 scaleLEye, // 左眼尺度比例, 相對於臉部尺寸 scaleREye) // 右眼尺度比例, 相對於臉部尺寸{ // -------------- 1 畫臉 --------------- fill(255);// 填充白色 ellipse(posX,posY,faceSize,faceSize);// 圓圈 // -------------- 2 畫眼睛 --------------- // 2.1 計算眼睛相對於臉中心點的偏移量 var EyeOffsetX = 0.2 * faceSize; // 眼睛橫向偏移量為臉部尺寸的0.2倍 var EyeOffsetY = 0 * faceSize; // 眼睛縱向偏移量為臉部尺寸的0倍 // 2.2 計算眼睛尺寸 // 左右眼尺寸 var LEyeSize = faceSize * scaleLEye; var REyeSize = faceSize * scaleREye; // 左右眼珠尺寸 var LIrisSize = LEyeSize * 0.4; var RIrisSize = REyeSize * 0.4; // 2.2 畫出左眼 fill(255);// 填充白色 ellipse( posX-EyeOffsetX, // 臉的中心位置向左偏移EyeOffsetX posY+EyeOffsetY, // 臉的中心位置向下偏移EyeOffsetY LEyeSize, LEyeSize); // 2.3 畫出右眼 fill(255);// 填充白色 ellipse( posX+EyeOffsetX, posY+EyeOffsetY, REyeSize, REyeSize); // 5 左眼珠 fill(0);// 填充黑色 ellipse( posX-EyeOffsetX, // 位置與左眼一樣 posY+EyeOffsetY, LIrisSize, // 尺寸則採用比左眼小的尺寸 LIrisSize); // 6 右眼珠 fill(0);// 填充黑色 ellipse( posX+EyeOffsetX, posY+EyeOffsetY, RIrisSize, RIrisSize); // -------------- 3 畫嘴巴 --------------- // 3.1 計算嘴巴相對於臉部中心位置的偏移量 var MouthOffsetX = 0.0; var MouthOffsetY = 0.3*faceSize; // 3.2 計算嘴巴尺寸 var MouthWidth = faceSize * scaleMouth; var MouthHeight = MouthWidth/2.0; // 3.3 畫出嘴巴 fill(255); // 填充白色 ellipse( posX + MouthOffsetX, posY + MouthOffsetY, MouthWidth, MouthHeight); // -------------- 4 畫頭髮 --------------- drawOneHair(posX,posY,faceSize,-0.3); drawOneHair(posX,posY,faceSize,-0.2); drawOneHair(posX,posY,faceSize,-0.1); drawOneHair(posX,posY,faceSize,0); drawOneHair(posX,posY,faceSize,0.1); drawOneHair(posX,posY,faceSize,0.2); drawOneHair(posX,posY,faceSize,0.3);}// 繪製一根頭髮function drawOneHair( faceX,faceY, // 臉的中心位置 faceSize, // 臉的尺寸 offsetXOnFaceSize) // 頭髮X座標的的偏移量,以臉部尺寸為單位尺寸{ // ------------- 1 計算尺寸和位置 ---------// // 頭髮相對臉部中心的Y偏移量 var HairOffsetY = faceSize * 0.3; // 計算X偏移量 var offsetX = offsetXOnFaceSize * faceSize; // 頭髮長度 var HairLength = faceSize * 0.4; // --------------- 2 畫頭髮 ---------------// line( faceX - offsetX, faceY - HairOffsetY, faceX - offsetX, faceY - (HairOffsetY + HairLength) );} |
我們重新寫一個簡單的draw()函式,實現一個持續移動的懵逼臉:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// 函式draw():作畫階段function draw() { // -------- 獲取時間 ------------------- // // 獲得毫秒數 var Millis = millis(); // 獲得秒數,相比second(),能獲得小數點後的部分 var Second = millis()/1000; // -------- 計算位置 --------------------// var posX = Second * 80; posX = posX%width; // 讓posX不超過螢幕寬度 var posY = Second * 61; posY = posY%height; // 讓posY不超過螢幕高度 // --------- 繪製懵逼臉------------------// drawConfuseFace(posX,posY,80,0.3,0.2,0.2); } |
這樣便實現了移動的懵逼臉,效果如下:
在上述程式碼中,使用了millis()函式來獲取時間,單位為毫秒,然後再通過除法運算轉化為秒數,這種方式相比直接呼叫second()而言,可以獲得精度更高的時間值。而用second()函式只能獲得整數的讀秒計數。 加粗的程式碼便是實現動態效果的關鍵。對於posX,首先通過語句“varposX=Second*80;"通過語句讓posX的數值正比於秒數Second, 於是,在不同時刻,這幾計算出的posX的實際數值也不同;然後,為了讓懵逼臉始終位於畫布內,用了求餘數的算符 %, 讓posX對畫布寬度width求餘數(width是p5.js提供的變數,其數值就是畫布寬度:https://p5js.org/reference/#/p5/width )。算符%的作用是求餘數,其用法如下列程式碼:
1 2 |
var a = 5%3; // 對5求3的餘數,結果為2,並賦值給avar b = 9%a; // 對9求a的餘數,結果為1, 並賦值給b |
posY的計算方式於posX完全一樣。 於是,在最後呼叫drawConfuseFace()函式時,由於用隨時間發生變化的量posX和posY作為位置,因此畫出來的懵逼臉便隨著時間移動了。