一個多彩泡泡屏保特效(JS+CSS版) https://my.oschina.net/darkness/blog/360394 java
package screensaver; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; import com.abigdreamer.infinity.common.util.IntegerValue; import com.sun.awt.AWTUtilities; @SuppressWarnings("restriction") public class BubblesScreensaver { // 常量 static int D = 222; // 泡泡直徑 static double K = 0.999; static double POW_RATE = 0.0001; // 補償機率 static double POW_RANGE = 0.8; // 補償範圍(基於誕生速度) public static double SPEED_X() { return 8 + (RND() * 4); } public static double SPEED_Y() { return 6 + (RND() * 2); } static List<Bubble> arrBubs = new ArrayList<>(); static double iBottom; static double iRight; public static double SQRT(double a) { return Math.sqrt(a); } public static double RND() { return Math.random(); } static int clientWidth; static int clientHeight; public static void main(String[] args) { JFrame frame = new JFrame(); frame.setAlwaysOnTop(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setUndecorated(true); frame.setExtendedState(JFrame.MAXIMIZED_BOTH); AWTUtilities.setWindowOpaque(frame, false); final JPanel pane = new JPanel() { private static final long serialVersionUID = 1L; @Override public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D) g; int n = arrBubs.size(); for (int j = 0; j < n; j++) { Bubble bub = arrBubs.get(j); float v; g2d.translate(bub.x, bub.y); g2d.drawImage(bub.images[3], 0, 0, null); Composite old = g2d.getComposite(); for (int i = 0; i < 3; i++) { v = (float) Math.abs(Math.sin(bub.kOpa[i] += bub.kStp[i] * Math.random())); v *= bub.POW[i]; // v = ((v * 1e4) >> 0) / 1e4; AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, v); g2d.setComposite(alphaComposite);// 透明度 g2d.drawImage(bub.images[i], 0, 0, null);// 背景 } g2d.setComposite(old); g2d.translate(-bub.x, -bub.y); } } }; pane.setOpaque(false); frame.setContentPane(pane); frame.setVisible(true); clientHeight = frame.getHeight(); clientWidth = frame.getWidth(); int MAX = 5; IntegerValue integerValue = new IntegerValue(); Timer timer1 = new Timer(); timer1.schedule(new TimerTask() { public void run() { if (integerValue.get() < MAX) { integerValue.add(); CreateBubble(frame); } else { timer1.cancel(); } } }, 10, 1000);// delay=2000毫秒 後執行該任務 Timer timer = new Timer(); timer.schedule(new TimerTask() { public void run() { update(); pane.repaint(); } }, 10, 17);// delay=2000毫秒 後執行該任務 } public static void CreateBubble(JFrame frame) { Bubble bub = new Bubble(); bub.setX(0); bub.setY(0); bub.vx = SPEED_X(); bub.vy = SPEED_Y(); arrBubs.add(bub); }; public static void update() { int n = arrBubs.size(); int i, j; updateWall(); for (i = 0; i < n; i++) { Bubble bub = arrBubs.get(i); bub.vx *= K; bub.vy *= K; if (RND() < POW_RATE) { bub.vx = SPEED_X() * (1 + RND() * POW_RANGE); bub.vy = SPEED_Y() * (1 + RND() * POW_RANGE); } bub.setX(bub.x + bub.vx); bub.setY(bub.y + bub.vy); checkWalls(bub); } Bubble bub, bub2; for (i = 0; i < n - 1; i++) { bub = arrBubs.get(i); for (j = i + 1; j < n; j++) { bub2 = arrBubs.get(j); checkCollision(bub, bub2); } } } public static void updateWall() { iRight = clientWidth - D; iBottom = clientHeight - D; } public static void checkWalls(Bubble bub) { if (bub.x < 0) { bub.setX(0); bub.vx *= -1; } else if (bub.x > iRight) { bub.setX(iRight); bub.vx *= -1; } if (bub.y < 0) { bub.setY(0); bub.vy *= -1; } else if (bub.y > iBottom) { bub.setY(iBottom); bub.vy *= -1; } } static class Position { public double x; public double y; public Position(double x, double y) { this.x = x; this.y = y; } } public static Position rotate(double x, double y, double sin, double cos, boolean reverse) { if (reverse) return new Position((x * cos + y * sin), (y * cos - x * sin)); else return new Position((x * cos - y * sin), (y * cos + x * sin)); } public static void checkCollision(Bubble bub0, Bubble bub1) { double dx = bub1.x - bub0.x; double dy = bub1.y - bub0.y; double dist = SQRT(dx * dx + dy * dy); if (dist < D) { // 計算角度和正餘弦值 double angle = Math.atan2(dy, dx); double sin = Math.sin(angle); double cos = Math.cos(angle); // 旋轉 bub0 的位置 Position pos0 = new Position(0, 0); // 旋轉 bub1 的速度 Position pos1 = rotate(dx, dy, sin, cos, true); // 旋轉 bub0 的速度 Position vel0 = rotate(bub0.vx, bub0.vy, sin, cos, true); // 旋轉 bub1 的速度 Position vel1 = rotate(bub1.vx, bub1.vy, sin, cos, true); // 碰撞的做用力 double vxTotal = vel0.x - vel1.x; vel0.x = vel1.x; vel1.x = vxTotal + vel0.x; // 更新位置 double absV = Math.abs(vel0.x) + Math.abs(vel1.x); double overlap = D - Math.abs(pos0.x - pos1.x); pos0.x += vel0.x / absV * overlap; pos1.x += vel1.x / absV * overlap; // 將位置旋轉回來 Position pos0F = rotate(pos0.x, pos0.y, sin, cos, false); Position pos1F = rotate(pos1.x, pos1.y, sin, cos, false); // 將位置調整爲屏幕的實際位置 bub1.setX(bub0.x + pos1F.x); bub1.setY(bub0.y + pos1F.y); bub0.setX(bub0.x + pos0F.x); bub0.setY(bub0.y + pos0F.y); // 將速度旋轉回來 Position vel0F = rotate(vel0.x, vel0.y, sin, cos, false); Position vel1F = rotate(vel1.x, vel1.y, sin, cos, false); bub0.vx = vel0F.x; bub0.vy = vel0F.y; bub1.vx = vel1F.x; bub1.vy = vel1F.y; } } } class Bubble { static int D = 222; // 泡泡直徑 double APLHA = 0.8; double[] POW = new double[] { 1.0, APLHA, APLHA * APLHA }; double vx; double vy; double x; double y; double[] kOpa; double[] kStp; Image[] images = new Image[4]; public Bubble() { double[] kOpa = new double[4]; double[] kStp = new double[4]; for (int i = 0; i < 4; i++) { // 泡泡頂層 if (i == 3) { ImageIcon imageIcon = new ImageIcon(getClass().getResource("heart.png")); images[i] = imageIcon.getImage(); } else { ImageIcon imageIcon = new ImageIcon(getClass().getResource("ch" + i + ".png")); images[i] = imageIcon.getImage(); } kOpa[i] = 3 * Math.random(); kStp[i] = 0.02 * Math.random(); } this.kOpa = kOpa; this.kStp = kStp; } public void setX(double x) { this.x = x; } public void setY(double y) { this.y = y; } }