【06】processing-交互(中文)
互動性
Casey Reas 和 Ben Fry
?
屏幕在我們的身體和計算機內部的電路和電之間形成了一座橋梁。我們通過觸摸板、軌跡球和操縱桿等多種設備控制屏幕上的元素,但鍵盤和鼠標仍然是臺式計算機最常用的輸入設備。計算機鼠標可以追溯到20世紀60年代末,當時道格拉斯·恩格爾巴特將該設備作為在線系統(NLS)的一個組成部分,NLS是最早配備視頻顯示器的計算機系統之一。鼠標的概念在施樂帕洛阿爾托研究中心(PARC)得到了進一步的發展,但它在1984年與蘋果Macintosh(蘋果Macintosh)一起推出,是它目前無處不在的催化劑。在過去的四十年里,鼠標的設計經歷了多次修改,但其功能保持不變。在恩格爾巴特1970年的原始專利申請中,他把鼠標稱為“X-Y位置指示器”,這仍然準確,但枯燥地定義了它的當代用途。
?
物理鼠標對象用于控制光標在屏幕上的位置并選擇界面元素。光標位置由計算機程序讀取為兩個數字,即x坐標和y坐標。這些數字可以用來控制屏幕上元素的屬性。如果收集和分析這些坐標,它們可以用來提取更高層次的信息,如鼠標的速度和方向。這些數據可以依次用于手勢和模式識別。
?
鍵盤通常用于輸入字符,用于編寫文檔、電子郵件和即時消息,但鍵盤有可能超出其原始用途。鍵盤從打字機到計算機的遷移擴展了它的功能,使它能夠啟動軟件,在軟件應用程序的菜單中移動,并在游戲中導航三維環境。在編寫自己的軟件時,您可以隨心所欲地使用鍵盤數據。例如,手指的速度和節奏等基本信息可以由按鍵的速度來確定。這些信息可以控制事件的速度或運動質量。也可以忽略鍵盤上打印的字符,使用每個鍵相對于鍵盤網格的位置作為數字位置。
?
現代電腦鍵盤是打字機的直接后代。英語鍵盤上鍵的位置是從早期打字機繼承來的。這種布局之所以稱為QWERTY,是因為最上面一行字母鍵的順序。這一百多年的機械遺產仍然影響著我們今天如何編寫軟件。
?
鼠標數據
處理變量mouseX和mouseY(注意大寫的X和Y)將光標相對于原點的X坐標和Y坐標存儲在顯示窗口的左上角。要查看移動鼠標時產生的實際值,請運行此程序將值打印到控制臺:
?
void draw() {frameRate(12);println(mouseX + " : " + mouseY); }?
?
?
當程序啟動時,mouseX和mouseY值為0。如果光標移動到顯示窗口中,則將值設置為光標的當前位置。如果光標在左側,則mouseX值為0,并且該值隨著光標向右移動而增加。如果光標位于頂部,則mouseY值為0,并且該值隨著光標向下移動而增加。如果在沒有draw()的程序中使用mouseX和mouseY,或者在setup()中運行noLoop(),則值始終為0。
?
鼠標位置最常用于控制屏幕上視覺元素的位置。當視覺元素與鼠標值的關系不同時,會創建更有趣的關系,而不是簡單地模擬當前位置。從鼠標位置添加和減去值會創建保持不變的關系,而將這些值相乘和相除會在鼠標位置和屏幕上的元素之間創建更改的視覺關系。在下面的第一個示例中,圓直接映射到光標,在第二個示例中,數字從光標位置相加和減去以創建偏移,在第三個示例中,乘法和除法用于縮放偏移。
?
| void setup() {size(100, 100);noStroke(); }void draw() {background(126);ellipse(mouseX, mouseY, 33, 33); } |
?
| void setup() {size(100, 100);noStroke(); }void draw() {background(126);ellipse(mouseX, 16, 33, 33); // 頂圓ellipse(mouseX+20, 50, 33, 33); // 中間圓ellipse(mouseX-20, 84, 33, 33); // 底圓 } |
?
| void setup() {size(100, 100);noStroke(); }void draw() {background(126);ellipse(mouseX, 16, 33, 33); // 頂圓ellipse(mouseX/2, 50, 33, 33); // 中間圓ellipse(mouseX*2, 84, 33, 33); // 底圓 } |
?
要反轉鼠標值,請從窗口寬度中減去mouseX值,然后從屏幕高度中減去mouseY值。
?
| void setup() {size(100, 100);noStroke(); }void draw() {float x = mouseX;float y = mouseY;float ix = width - mouseX; // Inverse Xfloat iy = height - mouseY; // Inverse Ybackground(126);fill(255, 150);ellipse(x, height/2, y, y);fill(0, 159);ellipse(ix, height/2, iy, iy); } |
?
處理變量pmouseX和pmouseY存儲前一幀中的鼠標值。如果鼠標不移動,則值將相同,但如果鼠標移動很快,則值之間可能存在較大差異。若要查看差異,請運行以下程序并交替緩慢快速移動鼠標。觀察值打印到控制臺。
?
void draw() {frameRate(12);println(pmouseX - mouseX); }?
?
?
從上一個鼠標位置到當前位置畫一條線,在一幀中顯示更改的位置,并顯示鼠標的速度和方向。當鼠標不移動時,會繪制一個點,但是快速的鼠標移動會創建長線。
?
| void setup() {size(100, 100);strokeWeight(8); }void draw() {background(204);line(mouseX, mouseY, pmouseX, pmouseY); } |
?
將mouseX和mouseY變量與if結構一起使用,以允許光標選擇屏幕區域。以下示例演示光標在顯示窗口的不同區域之間進行選擇。第一個把屏幕分成兩半,第二個把屏幕分成三分。
?
| void setup() {size(100, 100);noStroke();fill(0); }void draw() {background(204);if (mouseX < 50) {rect(0, 0, 50, 100); // Left} else {rect(50, 0, 50, 100); // Right} } |
?
| void setup() {size(100, 100);noStroke();fill(0); }void draw() {background(204);if (mouseX < 33) {rect(0, 0, 33, 100); // Left} else if (mouseX < 66) {rect(33, 0, 33, 100); // Middle} else {rect(66, 0, 33, 100); // Right} } |
?
使用邏輯運算符和if結構選擇屏幕的矩形區域。如下面的示例所示,當創建一個關系表達式來測試矩形的每條邊(左、右、上、下)并且這些邊與邏輯“和”連接時,只有當光標位于矩形內時,整個關系表達式才為真。
?
| void setup() {size(100, 100);noStroke();fill(0); }void draw() {background(204);if ((mouseX > 40) && (mouseX < 80) &&(mouseY > 20) && (mouseY < 80)) {fill(255);} else {fill(0);}rect(40, 20, 40, 60); } |
?
這段代碼詢問:“光標是否在左邊緣的右側,光標是否在右邊緣的左側,光標是否在上邊緣之外,光標是否在下邊緣之上?”?“下一個示例的代碼將詢問一組類似的問題,并將它們與關鍵字else結合起來,以確定哪個定義的區域包含光標。
?
| void setup() {size(100, 100);noStroke();fill(0); }void draw() {background(204);if ((mouseX <= 50) && (mouseY <= 50)) {rect(0, 0, 50, 50); // Upper-left} else if ((mouseX <= 50) && (mouseY > 50)) {rect(0, 50, 50, 50); // Lower-left} else if ((mouseX > 50) && (mouseY <= 50)) {rect(50, 0, 50, 50); // Upper-right} else {rect(50, 50, 50, 50); // Lower-right} } |
?
鼠標按鈕
計算機鼠標和其他相關的輸入設備通常有一個到三個按鈕;處理過程可以檢測這些按鈕是用mousePressed和mouseButton變量按下的。與按鈕狀態一起使用時,光標位置使鼠標能夠執行不同的操作。例如,當鼠標在圖標上時按下按鈕可以選擇它,這樣圖標可以移動到屏幕上的其他位置。如果按下任何鼠標按鈕,則mouse pressed變量為true;如果未按下鼠標按鈕,則為false。變量mouse button是左、中或右,具體取決于最近按下的鼠標按鈕。釋放按鈕后,mousePressed變量將立即恢復為false,但mouseButton變量將保留其值,直到按下其他按鈕。這些變量可以單獨使用,也可以組合使用來控制軟件。運行這些程序以查看軟件如何響應您的手指。
?
| void setup() {size(100, 100); } void draw() {background(204);if (mousePressed == true) {fill(255); // White} else {fill(0); // Black}rect(25, 25, 50, 50); } |
?
| void setup() {size(100, 100); }void draw() { if (mouseButton == LEFT) {fill(0); // Black} else if (mouseButton == RIGHT) {fill(255); // White} else { fill(126); // Gray}rect(25, 25, 50, 50); } |
?
| void setup() {size(100, 100); } void draw() {if (mousePressed == true) {if (mouseButton == LEFT) {fill(0); // Black} else if (mouseButton == RIGHT) { fill(255); // White}} else {fill(126); // Gray}rect(25, 25, 50, 50); } |
?
并非所有的鼠標都有多個按鈕,如果軟件分布廣泛,交互就不應該依賴于檢測哪個按鈕被按下。
?
鍵盤數據
?
處理記錄最近按下的鍵以及當前是否按下某個鍵。如果按鍵,則布爾變量key pressed為true,否則為false。在if結構的測試中包含此變量,以便僅在按下鍵時才允許代碼行運行。按住鍵時,keyPressed變量保持為true,只有在釋放鍵時才變為false。
?
| void setup() {size(100, 100);strokeWeight(4); }void draw() {background(204);if (keyPressed == true) { // 如果按鍵,line(20, 20, 80, 80); // 畫一條線} else { // 否則,rect(40, 40, 20, 20); // 繪制矩形} } |
?
| int x = 20; void setup() {size(100, 100);strokeWeight(4); } void draw() {background(204);if (keyPressed == true) { // 如果按鍵x++; // 每次遞增 1 給 x} line(x, 20, x-60, 80); } |
?
key變量存儲單個字母數字字符。具體來說,它保存最近按下的鍵。這個鍵可以用text()函數顯示在屏幕上(第150頁)。
?
| void setup() {size(100, 100);textSize(60); }void draw() {background(0);text(key, 20, 75); // 在坐標(20,75)處繪制 } |
?
key變量可用于確定是否按下了特定的鍵。下面的示例使用表達式key='A'來測試是否按下了A鍵。單引號表示A是char數據類型(第144頁)。表達式key=“A”將導致錯誤,因為雙引號將A表示為字符串,并且無法將字符串與char進行比較。邏輯和符號(&&operator)用于將表達式與key pressed變量連接,以確定按下的鍵是大寫的A。
前面的示例使用大寫字母A,但如果按小寫字母,則不適用。要檢查大寫和小寫字母,請使用邏輯或關系運算符擴展關系表達式。上一個程序中的第9行將更改為:
if((keyPressed==true)&&((key==a')| |(key==a')){因為每個字符都有一個由ASCII表(第605頁)定義的數值,所以鍵變量的值可以像任何其他數字一樣用來控制視覺屬性,例如形狀元素的位置和顏色。例如,ASCII表將大寫字母A定義為數字65,將數字1定義為49。
?
| void setup() {size(100, 100);strokeWeight(4); }void draw() {background(204);// 如果按下“A”鍵,畫一條線if ((keyPressed == true) && (key == 'A')) {line(50, 25, 50, 75);} else { //否則,畫一個橢圓ellipse(50, 50, 50, 50);} } |
?
前面的示例使用大寫字母A,但如果按小寫字母,則不適用。要檢查大寫和小寫字母,請使用邏輯或關系運算符擴展關系表達式。上一個程序中的第9行將更改為:
if((keyPressed==true)&&((key==a')||(key==a')){?
?
?
因為每個字符都有一個由ASCII表(第605頁)定義的數值,所以鍵變量的值可以像任何其他數字一樣用來控制視覺屬性,例如形狀元素的位置和顏色。例如,ASCII表將大寫字母A定義為數字65,將數字1定義為49。
?
| void setup() {size(100, 100);stroke(0); }void draw() {if (keyPressed == true) {int x = key - 32;line(x, 0, x, height);} } |
| float angle = 0;void setup() {size(100, 100);fill(0); } void draw() {background(204);if (keyPressed == true) {if ((key >= 32) && (key <= 126)) {// 如果鍵是字母數字,//將其值用作角度angle = (key - 32) * 3;}}arc(50, 50, 66, 66, 0, radians(angle)); } |
?
編碼密鑰
除了讀取數字、字母和符號的鍵值外,處理還可以從其他鍵(包括箭頭鍵和Alt、Control、Shift、Backspace、Tab、Enter、Return、Escape和Delete鍵)讀取值。變量keyCode將ALT、CONTROL、SHIFT、UP、DOWN、LEFT和RIGHT鍵存儲為常量。在確定按下哪個編碼鍵之前,必須先檢查該鍵是否已編碼。如果對鍵進行了編碼,則表達式key==CODED為true,否則為false。即使不是字母數字,ASCII規范中包含的鍵(BACKSPACE、TAB、ENTER、RETURN、ESC和DELETE)也不會被標識為編碼鍵。如果您正在進行跨平臺項目,請注意,Enter鍵通常在pc和UNIX上使用,Return鍵在Macintosh上使用。檢查Enter和Return以確保您的程序適用于所有平臺(請參見代碼12-17)。
?
| int y = 35;void setup() {size(100, 100); }void draw() {background(204);line(10, 50, 90, 50);if (key == CODED) { if (keyCode == UP) {y = 20;} else if (keyCode == DOWN) {y = 50;}} else {y = 35;}rect(25, y, 50, 30); } |
?
事件
當按鍵或鼠標移動等動作發生時,一類稱為事件的函數改變程序的正常流程。事件是程序正常流程的禮貌中斷。按鍵和鼠標移動將一直存儲到draw()結束,在這里它們可以執行不會干擾當前正在進行的繪圖的操作。事件函數中的代碼在每次相應事件發生時運行一次。例如,如果按下鼠標按鈕,mousePressed()函數中的代碼將運行一次,并且在再次按下按鈕之前不會再次運行。這使得鼠標和鍵盤產生的數據可以獨立于程序的其他部分進行讀取。
?
鼠標事件
鼠標事件函數有mousePressed()、mouseReleased()、mouseMoved()和mouseDragged():
?
?
| mousePressed() | 當按下鼠標按鈕時,此塊中的代碼將運行一次 |
| mouseReleased() | 此塊中的代碼在釋放鼠標按鈕時運行一次 |
| mouseMoved() | 當鼠標移動時,此塊中的代碼將運行一次 |
| mouseDragged() | 當按下鼠標按鈕移動鼠標時,此塊中的代碼將運行一次 |
?
mousePressed()函數的工作方式與mousePressed變量不同。在釋放鼠標按鈕之前,mousePressed變量的值為true。因此,可以在draw()中使用它在按下鼠標時運行一行代碼。相反,mousePressed()函數中的代碼只在按下按鈕時運行一次。這使得當鼠標單擊用于觸發操作(如清除屏幕)時非常有用。在下面的示例中,每次按下鼠標按鈕時,背景值都會變亮。在您的計算機上運行該示例以查看響應于您的手指的更改。
?
| int gray = 0;void setup() {size(100, 100); }void draw() {background(gray); } void mousePressed() {gray += 20; } |
?
下面的示例與上面的示例相同,但灰色變量是在mouserereled()事件函數中設置的,每次釋放按鈕時都會調用一次。只有運行程序并單擊鼠標按鈕才能看到這種差異。長時間按住鼠標按鈕,注意只有松開按鈕時背景值才會更改。
?
| int gray = 0;void setup() {size(100, 100); }void draw() {background(gray); } void mouseReleased() {gray += 20; } |
?
在事件函數內部繪制通常不是一個好主意,但是可以在某些條件下完成。在繪制這些函數之前,必須考慮程序的流程。在本例中,正方形是在mousePressed()中繪制的,由于draw()中沒有background(),因此它們仍保留在屏幕上。但是,如果使用background(),在鼠標事件函數中繪制的可視元素將僅顯示在屏幕上一幀,或者默認情況下顯示為1/60秒。事實上,您會注意到這個示例在draw()中沒有任何內容,但它需要在那里強制處理才能繼續監聽事件。如果在draw()中運行background()函數,矩形將閃爍到屏幕上并消失。
?
| void setup() {size(100, 100);fill(0, 102); } void draw() { } // Empty draw() 保持程序運行void mousePressed() {rect(mouseX, mouseY, 33, 33); } |
?
mouseMoved()和mouseDragged()事件函數中的代碼在鼠標位置發生更改時運行。當鼠標移動且未按下按鈕時,mouseMoved()塊中的代碼將在每幀的末尾運行。當按下鼠標按鈕時,mouseDragged()塊中的代碼也會執行相同的操作。如果鼠標在每幀之間保持相同的位置,則這些函數中的代碼不會運行。在本例中,未按下按鈕時灰色圓圈跟隨鼠標,按下鼠標按鈕時黑色圓圈跟隨鼠標。
?
| int dragX, dragY, moveX, moveY;void setup() {size(100, 100);noStroke(); }void draw() {background(204);fill(0); ellipse(dragX, dragY, 33, 33); // 黑色圓圈fill(153);ellipse(moveX, moveY, 33, 33); // 灰色圓圈 }void mouseMoved() { // 移動灰色圓圈moveX = mouseX;moveY = mouseY; }void mouseDragged() { // 移動黑色圓圈dragX = mouseX;dragY = mouseY; } |
?
關鍵事件
每次按鍵都通過鍵盤事件函數keyPressed()和keyReleased()進行注冊:
?
| keyPressed() | 當按下任何鍵時,此塊中的代碼都會運行一次 |
| keyReleased() | 當釋放任何鍵時,此塊中的代碼將運行一次 |
?
每次按下一個鍵,key pressed()塊中的代碼都會運行一次。在此塊中,可以測試按下了哪個鍵,并將此值用于任何目的。如果長時間按住某個鍵,則keyPressed()塊中的代碼可能會連續快速運行多次,因為大多數操作系統將接管并重復調用keyPressed()函數。開始重復所需的時間和重復的速度因計算機而異,具體取決于鍵盤的首選項設置。在本例中,當按下T鍵時,布爾變量drawT的值將從false設置為true;這將導致代碼行開始運行draw()中的矩形。
?
| boolean drawT = false;void setup() {size(100, 100);noStroke(); } void draw() {background(204);if (drawT == true) {rect(20, 20, 60, 20);rect(39, 40, 22, 45);} }void keyPressed() {if ((key == 'T') || (key == 't')) {drawT = true;} } |
?
每次釋放密鑰時,keyReleased()塊中的代碼都會運行一次。下面的示例基于前面的代碼;每次釋放鍵時,布爾變量drawT都被設置回false以阻止形狀在draw()中顯示。
?
| boolean drawT = false;void setup() {size(100, 100);noStroke(); } void draw() {background(204);if (drawT == true) { rect(20, 20, 60, 20);rect(39, 40, 22, 45);} }void keyPressed() {if ((key == 'T') || (key == 't')) {drawT = true;} }void keyReleased() {drawT = false; } |
?
事件流
如前所述,用draw()編寫的程序每秒向屏幕顯示60幀。frameRate()函數用于設置每秒顯示的幀數限制,noLoop()函數可用于停止draw()循環。當與鼠標和鍵盤事件函數結合使用時,附加函數loop()和redraw()提供了更多選項。如果程序已使用noLoop()暫停,則運行loop()將繼續其操作。由于事件函數是使用noLoop()暫停程序時繼續運行的唯一元素,因此可以在這些事件中使用loop()函數繼續在draw()中運行代碼。下面的示例在每次按下鼠標按鈕時運行draw()函數約兩秒鐘,然后在該時間過后暫停程序。
?
int frame = 0; void setup() {size(100, 100); } void draw() {if (frame > 120) { // 如果從鼠標開始120幀noLoop(); //被按下,停止程序background(0); // 把背景調黑。} else { // 否則,設置背景background(204); // 淺灰色并畫線line(mouseX, 0, mouseX, 100); // 在鼠標位置line(0, mouseY, 100, mouseY);frame++;} } void mousePressed() {loop();frame = 0; }?
函數的作用是:在draw()中運行一次代碼,然后停止執行。當顯示器不需要持續更新時,這很有幫助。下面的示例在每次按下鼠標按鈕時運行draw()中的代碼。
?
void setup() {size(100, 100);noLoop(); } void draw() {background(204);line(mouseX, 0, mouseX, 100);line(0, mouseY, 100, mouseY); } void mousePressed() {redraw(); // 在draw中運行代碼一次 }?
光標圖標
光標可以用noCursor()函數隱藏,也可以用cursor()函數設置為顯示為不同的圖標或圖像。當noCursor()函數運行時,光標圖標在移動到顯示窗口時消失。為了反饋光標在軟件中的位置,可以使用mouseX和mouseY變量繪制和控制自定義光標。
?
void setup() {size(100, 100);strokeWeight(7);noCursor(); } void draw() {background(204);ellipse(mouseX, mouseY, 10, 10); }//如果運行noCursor(),則當程序運行時光標將被隱藏,直到運行cursor()函數來顯示它為止。void setup() {size(100, 100);noCursor(); } void draw() {background(204);if (mousePressed == true) {cursor();} }?
?
?
向cursor()函數添加一個參數,將其更改為另一個圖標或圖像。加載并使用圖像,或使用自描述性選項:箭頭、十字、手、移動、文本和等待。
?
void setup() {size(100, 100); } void draw() {background(204);if (mousePressed == true) {cursor(HAND); // 手繪光標} else {cursor(CROSS);}line(mouseX, 0, mouseX, height);line(0, mouseY, width, mouseY); }?
?
?
這些光標圖標是計算機操作系統的一部分,在不同的計算機上會顯示不同的圖標。
總結
以上是生活随笔為你收集整理的【06】processing-交互(中文)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 当iPhone用上联发科,你还会爱上它吗
- 下一篇: 山东省职称英语及计算机考试报名,山东职称