天天看點

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

引言:重複與美

這一節裡,我們将要學到一項強大的技能——循環。 有了循環,我們就能夠指揮計算機幫我們去做大量重複性的事情。

直覺上,提到“重複”,往往聯想到枯燥乏味。的确,從操作而言,重複性地做單調乏味地任務繪讓人感到煩躁不安。 然而,重複卻又是不可避免的。而且,很有有趣的東西也是通過重複操作實作的。 在繪畫中,重複性也展現得非常多。并且相當多的畫作都是通過“重複”來展現其趣味性。 為此,我們先賞析一些畫作。

埃舍爾

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

這一幅是荷蘭著名畫家埃舍爾的作品。 從它的結構可以明顯看出,這就是将平面空間分割成了重複的結構。實際上每一塊貼磚都是完全一樣的。

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

這幅作品在重複中增添了一定的變化,這種變化制造了強烈的趣味感,讓觀賞者總有一種好奇心,想去搞清楚它的規律。

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

這幅作品在重複中增加了尺度的變化,它實際上是一幅“分形“(fractals)圖案。

草間彌生

下面再看看怪婆婆草間彌生的作品。

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

這幅照片中,她的服飾/飾品/盒子/桌面/背景全都是她本人的标志性圖案”波點“。 她作品的最大特色就是不斷重複的”波點”。然而, 仔細觀察,其實每一樣樣物品上的波點都有所不同,波點的形狀/顔色/排列方式都有所變化。通過這些重複中的變化,将最簡單元素“點”構成了豐富的形态。 再看其它作品,雖然都是“波點”,都是重複,但卻樣樣不同!各有各的趣味。

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

梵高

上述兩位都是搞“圖案”的。圖案也是很容易聯系上“重複”一詞。但是,在表意性繪畫中,重複也同樣重要。 我們再看幾幅梵高的作品。

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

仔細觀察它們,可以看出,梵高也是一位善用“重複”的大師。他喜歡用重複的圖案來填充平面區域,他喜歡用重複的筆觸來表現對象。 但是,他在重複的過程中,卻注入了強烈的即興表現。他的每一個筆觸都有所不同,其中既有他主觀的控制,也有他當時的情緒/下意識/動作的不确定性。

分形藝術

近幾十年來,還有一門專門運用”重複“的美術流派——分形藝術。 下面時幾幅分形藝術圖案。

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

另外附一個程式驅動的動圖: https://www.shadertoy.com/view/4df3Rn

仔細看這些圖檔,發現它們的美感完全來自于重複,而且主要是兩種特征:1.局部和整體重複;2.重複中略有變化。

Craig Mullins

可别以為隻有非寫實繪畫才喜歡重複。下面這位搞電腦繪畫的,風格方面主要時寫實的。但又在寫實中加入了強烈的寫意性。

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

觀察這幅宏大的場景,可以發現,景觀結構都是重複的:道路/燈/遠處的建築物/建築物的細節部件等等。但在這種重複的有序結構中,整體上存在一種變化的節律,而且重複的細節結構也包含變化,例如每一個燈都略有不同。

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

在這一幅《狙擊手》作品中,也存在大量重複結構,而且他的筆觸其實也是重複的。同樣的,這些重複性中由融入了節律和變化,進而制造了極大的豐富性。

第四維上的重複

第四維即時間(根據愛因斯坦相對論),在時間上的重複,在傳統靜态繪畫中是不可能實作的,但用程式設計就可以做到,請看下面作品:

先看靜止狀态:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

怎麼樣,很無聊吧。。。

下面再看看動起來的樣子:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:
1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

是否感到突然就有了耐人尋味的趣味?

這麼比較,是希望讀者明白,可以将動态看作一種全新的“媒介”,它可以用于表現以往的靜态構成方式所無法表達的美感。

看了這麼多例子,相信大家也明白了,重複本身就是一項進階的繪畫手法,隻要運用得當,可以制造出極大的豐富性/特殊的風格/引人入勝的趣味感。

這裡,筆者總結一下用重複制造“美”的基本法則:

雷同的重複導緻無聊,變化的重複制造美。

然而,重複雖然很棒,但是要操作重複卻又是一件很惱火的事情。

好消息是,現在有了計算機,有了這個”俯首甘為孺子牛“的勞模,我們就有了更好的駕馭”重複“的幫手了,計算機最擅長的就是幹這種重複的勞動。 為了讓計算機聽命于我們,我們必須要學會給他下指令,也就是我們即将要學會的強大技能——循環。

用while循環來繪制懵逼臉的頭發

循環有幾種寫法,最簡單的即while循環。 我們先直接用它來改造之前的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
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120      
// 函數setup() : 準備階段
       
       
        function 
        setup
        () 
        {
       
       	
        // 建立畫布,寬度640像素,高度480像素
       
       	
        // 畫布的坐标系統為,左上角坐标為(0,0),
       
       	
        // x方向水準向右,y方向垂直向下,機關像素
       
       	
        createCanvas
        (
        600
        ,
        400
        );
       
       
        }
       
       
        

       
       
        // 函數draw():作畫階段
       
       
        function 
        draw
        () 
        {
       
       
        	// 在數百哦位置畫懵逼臉	
       
       	
        drawConfuseFace
        (
        mouseX
        ,
        mouseY
        ,
        200
        ,
        0.4
        ,
        0.2
        ,
        0.3
        );
       
       
        }
       
       
        

       
       
        // 畫懵逼臉
       
       
        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 畫頭發 --------------- 	
       
       
        	var offsetX01 = -0.3;
       
       
        	// while(bool Condition){}:
       
       
        	// 當條件滿足時,就執行{}内容,然後再判斷()的條件
       
       
        	while(offsetX01<0.4) 
       
       
        	{
       
       
        		drawOneHair(posX,posY,faceSize,offsetX01);
       
       
        		// 警戒:必須要讓while()括号中的條件會變成false,否則,
       
       
        		// 就會形成死循環,程式陷入永世輪回,
       
       
        		// 不斷地執行{}中的語句而無法退出。
       
       
        		offsetX01 += 0.1; // += 運算符用途:A+=B 等價于 A=A+B;
       
       
        	}	
       
       
        }
       
       
        

       
       
        // 繪制一根頭發
       
       
        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
        ) 
        );
       
       
        

             

其繪制效果與之前的程式一樣,即在滑鼠位置畫一個懵逼臉。 注意,這裡畫出一簇頭發,但與之前相比,代碼卻精簡了。

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

下面,重點觀察代碼中的加粗部分:

//  -------------- 4 畫頭發 --------------- 	
  
  
   	var offsetX01 = -0.3;
  
  
   	// while(bool Condition){}:
  
  
   	// 當條件滿足時,就執行{}内容,然後再判斷()的條件
  
  
   	while(offsetX01<0.4) 
  
  
   	{
  
  
   		drawOneHair(posX,posY,faceSize,offsetX01);
  
  
   		// 警戒:必須要讓while()括号中的條件會變成false,否則,
  
  
   		// 就會形成死循環,程式陷入永世輪回,
  
  
   		// 不斷地執行{}中的語句而無法退出。
  
  
   		offsetX01 += 0.1; // += 運算符用途:A+=B 等價于 A=A+B;
  
  
   	}	
        

其句法提煉出來即:

1
 2
 3
 4      
while
        (
        bool 
        Condition
        )
       
       
        {
       
           
        // 重複執行的内容
       
       
        }
             

其執行流程圖示如下:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

在使用while循環的時候,通常而言,需要注意:

  • ()中不能直接用常量true,這是為了避免陷入死循環,永世不得超生;
  • 在{}中要保證()中的條件在有限次執行重複内容會變為false,這也是為了避免陷入永世輪回。

看來,循環這一招雖然強大,但也有陷入死循環的風險。不過,還有一個技巧避免死循環,用break;語句。 在while循環的執行代碼中,即{}中,可以加入一句break; 其作用是直接退出循環。 在剛剛開始使用while循環時,要倍加小心,避免陷入死循環,其實可以始終采取下列套路作為開端:

1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12      
var count = 0; // 用count來對循環次數計數,若計數過大,則直接退出循環
       
       
        while(bool Condition) 
       
       
        {
       
           
        	// 實際運作的内容,代碼略
       
       
        	
        // 這裡寫想要重複做的内容,要考慮在有限循環次數下讓Condition變為false
       
           
       
           
        	
        // 用if語句判斷是否該直接退出循環
       
       
        	if(count>99999) // 這裡99999可以替換為一個比較大的數,其實就是限制while循環最大循環次數
       
       
        	{
       
       
        		break;
       
       
        	}
       
       
        	count ++; // 用自增運算符++,讓count增加1
       
       
        }
             

注意,這個初學者套路中,有兩個技巧: 在while循環中嵌套了if語句,這就說明,循環和分支流程是可以自由嵌套使用的;

循環的另一招:do-while

while循環在執行時,有可能一次也不執行{}中的内容。為了實作至少做一次{}中的内容,可以換用do-while循環,其句法如下:

1
 2
 3
 4
 5
 6      
do
       
       
        {
       
           
        // 重複執行内容
       
           
        // 這部分内容至少會執行一次
       
       
        }
       
       
        while
        (
        bool 
        Condition
        )
             

其運算流程如下:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

用for循環繪制一排懵逼臉

下面,就是循環的終極奧義——for循環。我們即将用它來畫一群懵逼臉。

首先,我們先用for循環畫一排懵逼臉,代碼如下:

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
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132      
// 函數setup() : 準備階段
       
       
        function 
        setup
        () 
        {
       
       	
        // 建立畫布,寬度640像素,高度480像素
       
       	
        // 畫布的坐标系統為,左上角坐标為(0,0),
       
       	
        // x方向水準向右,y方向垂直向下,機關像素
       
       	
        createCanvas
        (
        600
        ,
        400
        );
       
       
        }
       
       
        

       
       
        // 函數draw():作畫階段
       
       
        function 
        draw
        () 
        {
       
       	
        // 用變量x作為循環變量
       
       
        	for(var x=50;x<640;x+=100)
       
       
        	{
       
       
        		var y = 200; // 每一個懵逼臉的縱向位置固定為200
       
       
        		drawConfuseFace(x,y,120,0.4,0.2,0.3); // 用循環變量x來指定位置
       
       
        	}	
       
       
        }
       
       
        

       
       
        // 畫懵逼臉
       
       
        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 畫頭發 --------------- 	
       
       	
        var 
        offsetX01 
        = 
        -
        0.3
        ;
       
       	
        // while(bool Condition){}:
       
       	
        // 當條件滿足時,就執行{}内容,然後再判斷()的條件
       
       	
        var 
        count 
        = 
        0
        ;
       
       	
        while
        (
        offsetX01
        <
        0.4
        ) 
       
       	
        {
       
       		
        drawOneHair
        (
        posX
        ,
        posY
        ,
        faceSize
        ,
        offsetX01
        );
       
       		
        // 警戒:必須要讓while()括号中的條件會變成false
       
       		
        offsetX01 
        += 
        0.1
        ; 
        // += 運算符用途:A+=B 等價于 A=A+B;
       
       
        

       
       		
        if
        (
        count
        >
        99999
        )
       
       		
        {
       
       			
        break
        ;
       
       		
        }
       
       		
        count 
        ++
        ;
       
       	
        }	
       
       
        }
       
       
        

       
       
        // 繪制一根頭發
       
       
        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
        ) 
        );
       
       
        }
             

程式運作結果如下:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

for循環的句法為:

for (語句 1; 語句 2; 語句 3)

{

    被執行的代碼塊

}

其執行流程如下:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

for循環的句法結構了解起來比較困難。但是可以以最常見的套路來了解其用法:

1
 2
 3
 4
 5
 6      
// 在()中使用一個循環變量
       
       
        for 
        (
        var 
        i
        =
        0
        ; 
        i
        <
        10
        ; 
        i
        ++
        )
       
       
        {
       
           
        // 重複行為
       
           
        // 代碼中可以對變量i進行讀寫
       
       
        }
             

對應這個套路,循環流程可以圖示為:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

在上述示意的套路中,一般來說,不宜在{}中對循環變量i的取值進行改變,這樣就保證了i的數值隻在”語句3“發生變化。于是,循環體{}的執行次數就可以用i來完全限定了。在這個示例中,語句1将i初值賦為0,而語句2中條件是"i<10",且每一次運作語句3都對i的數值增加1,于是,隻要不再{}中改變i的數值,則{}中代碼的執行次數即為10次。

用for循環繪制一個懵逼臉方陣

在循環中可以繼續嵌套循環。 比如,一種經典套路就是用兩個for循環嵌套使用,進而繪制一個陣列的圖形。

這個套路的代碼如下:

1
 2
 3
 4
 5
 6
 7
 8      
for
        (
        var 
        col
        =
        0
        ;
        col
        <
        MaxCol
        ;
        col
        ++
        ) 
        // 循環MaxCol次
       
       
        // 第一層循環
       
       
        {
       
       
        	// 每一次執行第一層循環時,都要完整執行完每一次第二層循環
       
       	
        for
        (
        var 
        row 
        =
        0
        ;
        row
        <
        MaxRow
        ;
        row
        ++
        ) 
        // 循環MaxRow次
       
       
        	// 第二層循環
       
       	
        {
       
       		
        // 循環内容
       
       		
        // 這裡往往都會使用循環變量col和row的值,但盡量不要去改變它們的數值
       
       	
        }		
       
       
        }
             

将這個套路運用到我們的懵逼臉程式中,就可以繪制一個懵逼臉方陣。 代碼如下:

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
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136      
// 函數setup() : 準備階段
       
       
        function 
        setup
        () 
        {
       
       	
        // 建立畫布,寬度640像素,高度480像素
       
       	
        // 畫布的坐标系統為,左上角坐标為(0,0),
       
       	
        // x方向水準向右,y方向垂直向下,機關像素
       
       	
        createCanvas
        (
        600
        ,
        400
        );
       
       
        }
       
       
        

       
       
        // 函數draw():作畫階段
       
       
        function 
        draw
        () 
        {
       
       	
        // 	用變量col和row作為循環變量
       
       
        	for(var col=0;col<8;col++) // 畫8列
       
       
        	{
       
       
        		for(var row =0;row<6;row++) // 畫6行
       
       
        		{
       
       
        			var x = col*70; // x坐标為i的70倍,即X方向間隔為70像素
       
       
        			var y = row*60; // y坐标為i的60倍,即Y方向間隔為60像素
       
       
        			drawConfuseFace(x,y,60,0.4,0.2,0.3); // 用循環變量x來指定位置
       
       
        		}		
       
       
        	}	
       
       
        }
       
       
        

       
       
        // 畫懵逼臉
       
       
        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 畫頭發 --------------- 	
       
       	
        var 
        offsetX01 
        = 
        -
        0.3
        ;
       
       	
        // while(bool Condition){}:
       
       	
        // 當條件滿足時,就執行{}内容,然後再判斷()的條件
       
       	
        var 
        count 
        = 
        0
        ;
       
       	
        while
        (
        offsetX01
        <
        0.4
        ) 
       
       	
        {
       
       		
        drawOneHair
        (
        posX
        ,
        posY
        ,
        faceSize
        ,
        offsetX01
        );
       
       		
        // 警戒:必須要讓while()括号中的條件會變成false
       
       		
        offsetX01 
        += 
        0.1
        ; 
        // += 運算符用途:A+=B 等價于 A=A+B;
       
       
        

       
       		
        if
        (
        count
        >
        99999
        )
       
       		
        {
       
       			
        break
        ;
       
       		
        }
       
       		
        count 
        ++
        ;
       
       	
        }	
       
       
        }
       
       
        

       
       
        // 繪制一根頭發
       
       
        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
        ) 
        );
       
       
        }
             

程式運作效果如下:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

每一個懵逼臉都完全一樣,看起來顯得單調,那麼我們試試讓它們略有不同。 這也時再實踐引言部分提出的“法則”:雷同的重複導緻無聊,變化的重複制造美。 這裡,簡單辦法就是,每一次繪制時,都運用循環變量col和row來計算五官尺寸,進而每一個的尺寸都略有不同。 代碼如下:

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
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147      
// 函數setup() : 準備階段
       
       
        function 
        setup
        () 
        {
       
       	
        // 建立畫布,寬度640像素,高度480像素
       
       	
        // 畫布的坐标系統為,左上角坐标為(0,0),
       
       	
        // x方向水準向右,y方向垂直向下,機關像素
       
       	
        createCanvas
        (
        600
        ,
        400
        );
       
       
        }
       
       
        

       
       
        // 函數draw():作畫階段
       
       
        function 
        draw
        () 
        {
       
       	
        // 	用變量col和row作為循環變量
       
       	
        for
        (
        var 
        col
        =
        0
        ;
        col
        <
        8
        ;
        col
        ++
        ) 
        // 畫8列
       
       	
        {
       
       		
        for
        (
        var 
        row 
        =
        0
        ;
        row
        <
        6
        ;
        row
        ++
        ) 
        // 畫6行
       
       		
        {
       
       			
        var 
        x 
        = 
        col
        *
        70
        ; 
        // x坐标為i的70倍,即X方向間隔為70像素
       
       			
        var 
        y 
        = 
        row
        *
        60
        ; 
        // y坐标為i的60倍,即Y方向間隔為60像素
       
       			
        // 運用循環變量col和row計算五官尺寸
       
       
        			var faceSize = 40 + col*2 + row*2;
       
       
        			var mouthScale = 0.5 - (col * 0.03 + row * 0.03);
       
       
        			var lEyeScale = col * 0.02 + row * 0.01;
       
       
        			var rEyeScale = row * 0.01 + row * 0.02;
       
       
        			// 繪制懵逼臉
       
       
        			drawConfuseFace(
       
       
        				x,y,
       
       
        				faceSize,
       
       
        				mouthScale,
       
       
        				lEyeScale,
       
       
        				rEyeScale); 
       
       		
        }		
       
       	
        }	
       
       
        }
       
       
        

       
       
        // 畫懵逼臉
       
       
        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 畫頭發 --------------- 	
       
       	
        var 
        offsetX01 
        = 
        -
        0.3
        ;
       
       	
        // while(bool Condition){}:
       
       	
        // 當條件滿足時,就執行{}内容,然後再判斷()的條件
       
       	
        var 
        count 
        = 
        0
        ;
       
       	
        while
        (
        offsetX01
        <
        0.4
        ) 
       
       	
        {
       
       		
        drawOneHair
        (
        posX
        ,
        posY
        ,
        faceSize
        ,
        offsetX01
        );
       
       		
        // 警戒:必須要讓while()括号中的條件會變成false
       
       		
        offsetX01 
        += 
        0.1
        ; 
        // += 運算符用途:A+=B 等價于 A=A+B;
       
       
        

       
       		
        if
        (
        count
        >
        99999
        )
       
       		
        {
       
       			
        break
        ;
       
       		
        }
       
       		
        count 
        ++
        ;
       
       	
        }	
       
       
        }
       
       
        

       
       
        // 繪制一根頭發
       
       
        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
        ) 
        );
       
       
        }
             

其結果如下:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

for循環比while循環和do-while循環具有更大的自由度。 其實,僅僅使用上述for循環的套路,即可滿足大多數情況的使用需求。

此外,for循環還有一種 for-in 的形态,要用它,還需要初步進入“資料結構”這個技能分支,是以這裡先略去不談,待後續章節再掌握它。

讓懵逼臉能與滑鼠互動

盡管用循環可以很快畫出一大堆懵逼臉,但與傳統畫法相比,這個作品本身還未有特别的亮點,因為全憑手繪也可以搞定! 現在我們再做一點改造,讓作品可以展現碼繪的兩個特色——動态和互動!

第一,動态性。 我們希望這些懵逼臉可以随時間發生變化。 預期它們的嘴巴處于反複張開合攏的過程中,那麼嘴巴的比例應該時一個随着時間而反複變化的量。

第二,互動性。 我們希望這些懵逼臉可以相應滑鼠位置的變化。 預期它們 可以感覺到自己和滑鼠位置的距離,并且根據距離遠近來改變自己的形态。 在之前的教材中已經講解到,在程式中可以随時獲得滑鼠位置,即通路變量mouseX和mouseY。 那麼,我們現在就是要在繪制每個懵逼臉之前,計算其與滑鼠位置的距離,并基于此計算臉的尺寸和五官比例。

先看看修改後的代碼及效果,其中,紅色代碼為實作”動态性“的關鍵,而藍色代碼為實作“互動性”的關鍵。

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
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175      
// 函數setup() : 準備階段
       
       
        function 
        setup
        () 
        {
       
       	
        // 建立畫布,寬度640像素,高度480像素
       
       	
        // 畫布的坐标系統為,左上角坐标為(0,0),
       
       	
        // x方向水準向右,y方向垂直向下,機關像素
       
       	
        createCanvas
        (
        600
        ,
        400
        );
       
       
        }
       
       
        

       
       
        var mouthScaleBase = 0.25;
       
       
        var mouthScaleBaseChange = 0.005;
       
       
        

       
       
        // 函數draw():作畫階段
       
       
        function 
        draw
        () 
        {
       
       	
        //background(255); // 重新整理整個畫布為白色	
       
       
        

       
       	
        mouthScaleBase += mouthScaleBaseChange;
       
       
        	if(mouthScaleBase>0.5||mouthScaleBase<0.0)
       
       
        	{
       
       
        		mouthScaleBaseChange = -mouthScaleBaseChange;
       
       
        	}	
       
       
        

       
       	
        // 	用變量col和row作為循環變量
       
       	
        for
        (
        var 
        col
        =
        0
        ;
        col
        <
        8
        ;
        col
        ++
        ) 
        // 畫8列
       
       	
        {
       
       		
        for(
        var 
        row 
        =
        0
        ;
        row
        <
        6
        ;
        row
        ++
        ) 
        // 畫6行
       
       		
        {			
       
       			
        // 1 計算懵逼臉中心位置(x,y)
       
       			
        var 
        x 
        = 
        col
        *
        70 
        ; 
        // x坐标為i的70倍,即X方向間隔為70像素
       
       			
        var 
        y 
        = 
        row
        *
        60 
        ; 
        // y坐标為i的60倍,即Y方向間隔為60像素
       
       			
       
       			
        // 根據位置(x,y)與滑鼠位置(mx,my)的距離來計算五官尺寸	
       
       
        

       
       			
        // 2 計算滑鼠位置離這個懵逼臉的距離
       
       
        			// 用一種簡單方法表示距離:
       
       
        			// 滑鼠位置和臉部中心位置的橫縱坐标的內插補點的絕對值的總和
       
       
        			var dx = abs(x-mouseX); 
       
       
        			var dy = abs(y-mouseY);
       
       
        			var distance = dx + dy; // 距離
       
       
        			// 讓距離不超過200
       
       
        			if(distance>200)
       
       
        			{
       
       
        				distance = 200;
       
       
        			}
       
       
        

       
       			
        // 3 根據距離來計算臉部和五官尺寸
       
       			
        var faceSize = 40 + distance*0.2;
       
       			
        var mouthScale = mouthScaleBase;			
       
       			
        var lEyeScale = distance * 0.001;
       
       
        			var rEyeScale = 0.4 - distance * 0.001;
       
       			
       
       			
        // 4 繪制懵逼臉
       
       			
        drawConfuseFace
        (
       
       				
        x
        ,
        y
        ,			
        // 臉部中心位置(x,y)
       
       				
        faceSize,		
        // 下列四個就是臉部和五官尺寸
       
       				
        mouthScale,
       
       				
        lEyeScale,
       
       
        				rEyeScale
        ); 
       
       		
        }		
       
       	
        }	
       
       
        }
       
       
        

       
       
        // 畫懵逼臉
       
       
        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 畫頭發 --------------- 	
       
       	
        var 
        offsetX01 
        = 
        -
        0.3
        ;
       
       	
        // while(bool Condition){}:
       
       	
        // 當條件滿足時,就執行{}内容,然後再判斷()的條件
       
       	
        var 
        count 
        = 
        0
        ;
       
       	
        while(
        offsetX01
        <
        0.4
        ) 
       
       	
        {
       
       		
        drawOneHair
        (
        posX
        ,
        posY
        ,
        faceSize
        ,
        offsetX01
        );
       
       		
        // 警戒:必須要讓while()括号中的條件會變成false
       
       		
        offsetX01 
        += 
        0.1
        ; 
        // += 運算符用途:A+=B 等價于 A=A+B;
       
       
        

       
       		
        if
        (
        count
        >
        99999
        )
       
       		
        {
       
       			
        break;
       
       		
        }
       
       		
        count 
        ++
        ;
       
       	
        }	
       
       
        }
       
       
        

       
       
        // 繪制一根頭發
       
       
        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
        ) 
        );
       
       
        }
             

運作效果如下:

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

動态的實作

從圖中可見,動态性主要表現在懵逼臉們的嘴巴随着時間而反複張開合攏。 首先看看紅色代碼時如何實作動态的。

在draw()函數的定義之前,定義了兩個變量:

var mouthScaleBase = 0.25;

var mouthScaleBaseChange = 0.005;

其中,mouthScaleBase就是用于指定嘴巴比例的變量,即之後的代碼: var mouthScale = mouthScaleBase;

由于這兩個變量定義在函數之外,于是它們的作用域在整個程式,稱這兩個為“全局變量”。 任何位置都可以對他們進行讀寫操作 。 在draw()函數中可以直接對它們進行讀寫,由此定義了它們的變化規律:

function draw() {

// ......

// 每次運作draw()都将對mouthScaleBase 和mouthScaleBaseChange進行如下運算: mouthScaleBase += mouthScaleBaseChange; 

if(mouthScaleBase>0.5||mouthScaleBase<0.0)

{

mouthScaleBaseChange = -mouthScaleBaseChange;

}

//...... }

我們現在已經知道,draw()是一個反複調用的函數。于是,上述代碼也将反複調用,每一次調用, mouthScaleBase 的數值都會發生變化,其變化量就是mouthScaleBaseChange,于是,當mouthScaleBaseChange數值為正的時候,mouthScaleBase 即會增大,反之,若mouthScaleBaseChange數值為負時,mouthScaleBase 即會減小。

而變量mouthScaleBaseChange定義了mouthScaleBase在每一次draw()時的變化量,相當于mouthScaleBase的“變化率”。 這裡用了一個條件判斷語句來讓mouthScaleBaseChange發生變化,其條件為“mouthScaleBase>0.5||mouthScaleBase<0.0”,翻譯成口語即“當mouthScaleBase大于0.5或mouthScaleBase小于0的時候”,變化方式就是取反,即代碼“mouthScaleBaseChange = -mouthScaleBaseChange;” 注意,這裡再次說明“=”的用途是“指派”而非“等于”,這句的意思就是将mouthScaleBaseChange的負值-mouthScaleBaseChange指派給它自己。

于是,變量mouthScaleBase 就會在0~5之間反複變化。由于其指派給mouthScale 并用于繪制懵逼臉 drawConfuseFace()時指定嘴巴的比例,于是嘴巴就實作了随時間開合的動态。

互動性的實作

現在再觀察藍色代碼,了解滑鼠互動的實作機制。

其基本思路是,在繪制每一個懵逼臉時,都要計算出滑鼠位置和它的距離,然後,根據距離計算臉部尺寸和五官比例,再按計算結果繪制。

要點1. 計算滑鼠位置和懵逼臉的距離。 這裡采取了一種簡單方法,即球的滑鼠和臉部中心點的橫縱坐标差的絕對值dx和dy,這裡用到了p5.js提供的計算絕對值的函數abs(): var dx = abs(x-mouseX); 

var dy = abs(y-mouseY);

并将二者相加,即代碼: var distance = dx + dy; // 距離

此外,為了讓後續計算時,尺寸變化不至于過大,是以限制distance的取值再200以内,即代碼: // 讓距離不超過200

if(distance>200)

{

distance = 200;

}

1.9 開始第一幅“碼繪”——掌握大殺器”循環“,一招搞定百千萬個懵逼臉引言:重複與美用while循環來繪制懵逼臉的頭發循環的另一招:do-while用for循環繪制一排懵逼臉用for循環繪制一個懵逼臉方陣讓懵逼臉能與滑鼠互動總結知識點參考:

要點2. 根據距離計算臉部尺寸和五官比例。 在計算臉部尺寸和五官比例時,均用到了距離distance,即:

// 3 根據距離來計算臉部和五官尺寸

var faceSize = 40 + distance*0.2;

var mouthScale = mouthScaleBase;

var lEyeScale = distance * 0.001;

var rEyeScale = 0.4 - distance * 0.001;

這裡采取的計算公式和數值其實都是按照一定的經驗直覺來設定的, 而且均需要根據顯示效果反複調整以達到最佳效果。

總結

到此為止,我們已經掌握的用JS配p5.js來進行“過程式程式設計“的基本方法。 從下一章開始,我們将深入p5.js這個寶庫,并逐漸探讨程式設計語言的進階技能。

知識點參考:

while循環和do-while循環: http://www.runoob.com/js/js-loop-while.html

for循環:http://www.runoob.com/js/js-loop-for.html