臨近5月20的時候,想起之前做過一個用程序做出一個愛心的圖案,便突發奇想給它升級一下
愛心1.0
愛心2.0
原理
愛心1.0的原理 是根據公式 (x ^ 2 + y ^ 2 - 1) ^ 3 - x ^ 2 * y ^ 3 = 0畫出來的。使用雙重循環就可以實現,然後判斷最後的結果和0的關係。如果結果等於0則該座標就在愛心上,如果大於0則在愛心外,小於0則在愛心內。那麼就可以畫出不同樣子的愛心
- 畫邊框、愛心留白
- 只畫愛心內部
如果只是這樣的話,那和愛心1.0沒有多大區別,所以我打算讓它動起來,畫出不同的顏色
實現要點
- 怎麼畫出圖形: 使用java的圖形化界面,通過畫筆畫很多連續的矩形。使用到的是重寫的public void paintComponent(Graphics g)方法。 要使該方法能夠使用,就要在一個JPanel類中重寫,並且把這個類添加到一個JFrame類中,之後在裏面使用g.fillRect()方法畫矩形就好了
- 怎麼修改畫出矩形的顏色: 在使用g.fillRect()方法畫圖之前可以先通過g.setColor()方法設置顏色,在此方法中要放入一個Color對象作爲參數,那麼就可以通過RGB三原色設置要畫的顏色,之後不斷改變RBG數值就可以達到不斷改變顏色的效果
- 怎麼畫愛心: 根據上面說的公式來判斷當前座標的值是否在愛心外或愛心內來判斷該點是否要畫,如果要畫的話就調用repaint()方法,會直接執行paintComponent(Graphics g)方法
- 怎麼實現動畫效果: 最開始使用的是時間事件,每隔一定時間就畫一行。看似很理想但是會有一個問題,如果畫一行的操作是連續的,那麼在這一行中是需要循環判斷每一個點是否要畫,如果要畫就使用repaint(),否則就繼續循環。結果畫出來的只會有一個點。原因是Java有一個GUI (AWT) Thread來負責GUI事件的分發,這個線程一般是和主程序的線程是綁定的,如果當前線程休眠那麼畫圖的這個事件也會丟失。如果循環調用repaint()的話就會合併爲最後一個repaint(),所以永遠只會畫出一個點。那麼就需要使用到多線程,爲面板創建一個新的線程,每次調用repaint()時就讓面板線程休眠一段時間,那麼就會輪到GUI (AWT) Thread,就可以執行repaint(),從而就不會丟失事件。既然使用了線程休眠,那麼就不需要再額外增加一個時間事件了,只需要通過線程休眠達到時間間隔的效果就可以了
完整代碼
愛心1.0
#include<stdio.h>
#include<iostream>
using namespace std ;
int main() {
for(float y = 1.5f; y >= -1.5f; y -= 0.1f){
for(float x = -1.5f; x <= 1.5f; x += 0.05f){
float a = x*x + y*y - 1;
if(a*a*a - x*x*y*y*y <= 0.0f)
printf("*");
else
printf(" ");
}
printf("\n");
}
return 0 ;
}
愛心2.0
public class Main {
public static void main(String[] args) {
Window window = new Window() ;
}
}
public class Window extends JFrame {
Panel panel;
public Window() {
panel = new Panel() ;
add(panel) ; //只有將畫板添加到窗口才能畫圖
panel.setBounds(0, 0, 860, 750);
Thread t = new Thread(panel) ; //要使用線程才能實現動畫效果
t.start();
setLayout(null); //畫板要能調節大小,則窗口不能使用默認排版方式
setBounds(400, 50, 860, 750);
setVisible(true);
validate();
setDefaultCloseOperation(Window.EXIT_ON_CLOSE);
}
}
public class Panel extends JPanel implements Runnable {
int R ; //三原色red
int G ; //三原色green
int B ; //三原色blue
int tx; //畫圖座標
int ty ; //畫圖座標
float y ; //循環畫圖行數
boolean flag ; //畫邊框愛心還是實體愛心
boolean increaseOrDecrease = false ; //G、B增大或減小
boolean backRed = false ; //從黑色變回紅色
File file = new File("my lonely soul.wav") ; //背景音樂
URL url = null;
URI uri = null ;
AudioClip clip = null;
public Panel(){
try {
uri=file.toURI();
url = uri.toURL() ;
}
catch (MalformedURLException e1) {}
clip= Applet.newAudioClip(url);
clip.loop(); //播放背景音樂
R = 255 ; //初始三原色爲紅色
G = 0 ;
B = 0 ;
y = 1.5f ; //初始循環位置
tx = 30 ; //每一行畫圖的位置
ty = 10 ; //初始畫圖的列的位置
flag = false ; //最開始畫邊框愛心
setVisible(true);
}
public void paintComponent(Graphics g) {
if(!flag) { //畫邊框
Color color = new Color(R,G,B) ; //根據當前的RGB畫相應顏色的圖形
g.setColor(color);
g.fillRect(tx, ty, 13, 13);
g.fillRect(tx, ty+11, 13, 10); //多往下畫一點減小每行的間隔
}
else { //畫實體愛心
super.paintComponent(g); //將之前所有的邊框先清空
Color color = new Color(206, 40, 34) ; //最終的顏色
g.setColor(color);
for(float i = 1.5f; i >= -1.5f; i -= 0.1f){
for(float x = -1.5f; x <= 1.5f; x += 0.05f){
float a = x*x + i*i - 1;
if(a*a*a - x*x*i*i*i <= 0.0f) {
g.fillRect(tx, ty, 13, 13);
g.fillRect(tx, ty+11, 13, 16);
}
tx += 13 ;
}
tx = 30 ;
ty += 25 ;
}
}
}
public void run() {
while (true) {
try {
Thread.sleep(70);
}
catch(Exception e) {}
if(y>=-1.2f && !flag) { //畫邊框愛心
//根據公式(x^2+y^2-1)^3-x^2y^3=0畫
for(float x = -1.5f; x <= 1.5f; x += 0.05f) {
float a = x*x + y*y - 1;
if(a*a*a-x*x*y*y*y>0.0f && y!=1.5f) { //大於0是愛心外側,小於0是內側
this.repaint();
try { //要把線程休眠一會纔會輪到repaint()的線程
Thread.sleep(4); //可自定義事件,事件越小畫的速度越快但太小的話可能會漏畫
}
catch(InterruptedException e){}
}
tx += 13 ; //每行往右走一點
}
tx = 30 ; //畫完一行後要回到最左邊
ty += 25 ; //畫下一行
y -= 0.1f ; //循環次數減少
}
else { //畫完一個邊框後判斷繼續畫下一個邊框或畫實體
if(!increaseOrDecrease) { //G、B數值增加
G += 4 ;
B += 4 ;
}
else { //G、B數值減小
G -= 4 ;
B -= 4 ;
}
if(G >= 70) //G、B數值在0~70範圍內
increaseOrDecrease = true ;
if(!backRed) //紅變黑
R -= 10 ;
else //黑變紅
R += 10 ;
if(R <= 0) { //到黑色了準備變回紅色
R = 1 ; //重新初始化R
backRed = true ;
}
if(G<=0 && B<=0) {
G = 1 ;
B = 1 ;
backRed = true ;
}
if(R < 255) //R沒有再次變回255說明還在畫邊框
y = 1.5f ;
else { //畫實體愛心
flag = true ;
try {
Thread.sleep(500);
}
catch(Exception e) {}
this.repaint();
}
tx = 30 ; //每畫完一次都要重新初始化畫圖座標
ty = 10 ;
}
}
}
}
總結
- 這是繼聖誕樹之後第二個突發奇想做的東西,還是挺有意思的,也能學到一些新的東西,比如多線程實現循環repaint()。以後還會有更多突發奇想的東西做出來吧
- 做出來的只是一個模板,如果對你有用,完全可以在這個基礎上實現更多有趣浪漫的操作。不準覺得這個表白程序太直男!把“硬核”打在公屏上!