【分享】翻出過去的一個多彩泡泡屏保特效(JS+CSS版)

  整理文件時翻出一個很久前作的泡泡屏保的特效,純JS+CSS作的。回想了下,是去年年初時看見XP下那個流行的泡泡屏保,忽然想移植到JS版原本。但有作着才發現有很多麻煩的問題解決很差,因而沒繼續。css

   

  DEMO: http://www.etherdream.com/funnyscript/bubbles/   html

  

  和XP系統自帶的那個屏保同樣,從屏幕一個角落裏冒出不少泡泡,而後在屏幕裏碰撞反彈。泡泡有着半透明的漸變色,而且顏色也是在不停的變換。web

   

  當時這個效果分析了很多時間。若是是用flash那就再簡單不過了,把泡泡蒙板的灰色通道複製到一個純色的背景層的Alpha通道就能夠了。可是網頁裏除非用HTML5的canvas,單憑純粹的CSS還沒那麼強大的位圖處理能力。在CSS裏,和透明度有關的道具也只有這幾個:png圖片,css alpha值,rgba(),chroma濾鏡,mask濾鏡,AlphaImageLoader濾鏡,以及CSS3的漸變。canvas

  

  

 

  你也許會說,這不是很簡單,給png圖片層設置各類background-color,不就能夠實現顏色的變換了嗎。事實上,背景色不但混合到了半透明像素中,連泡泡外的四個邊角也給填充了,這樣就成了方塊,而不是泡泡了,而且顏色也不正確。顯然沒有這麼簡單。瀏覽器

  

  由於泡泡是半透明漸變的材質,chroma和mask這些過濾單色的濾鏡都派不上用場。而rgba的背景色一樣也會出現多餘的背景。 AlphaImageLoader濾鏡經測試,實際顯示出來的圖片在background之上, 與<img>載入png效果同樣。而CSS3的漸變和本例的蒙板配合起來比較困難,並且兼容性也有問題。app

   

  本例的困難之處在於:圖片自己不只是半透明漸變的,而且這些漸變點的顏色還能經過腳本改變。考慮了好久,既然沒有一個簡便的方法,那不如就用複雜的吧~dom

 

  你們都知道,顏色都是RGB組成的,調整3種原色的比例,就能夠變出各類顏色。咱們不妨把灰色蒙板事先填入R,G,B三種純色,保存爲3張圖片。這樣就有了100%的紅色泡泡,綠色泡泡,藍色泡泡。把他們疊在同個位置,而後給3張圖片設置不一樣的css alpha值,因而就有了各類顏色的泡泡,而且半透明的像素仍然保留!測試

  

  

 

   因而這個彩色的問題就解決了。不過值得注意的是,藍色位於最頂層,而紅色則是最底層。即便是紅色層100%的不透明,也會被藍色和綠色層的PNG層層剝削,很明顯的減淡。因此實際顯示時,還需給藍和綠層分別加個權值,以保證紅色通道不會那麼的微弱,而藍的那麼的明顯。不過在IE裏的顯示效果還是藍色很明顯,不知道IE的透明度計算方式和標準瀏覽器有什麼不一樣。。。 (2010/2/1)ui

 

  後續:當時對ie的mask濾鏡理解不對,mask濾鏡並不是是單色的過濾,而是:用指定的RGB顏色替換容器內全部點的RGB,而且Alpha'=255 - Alpha。因此ie下只要把蒙板圖片的Alpha通道取反,而後同時用AlphaImageLoader和Mask濾鏡,便可達到效果,Mask濾鏡的color參數就是泡泡的顏色。另在Webkit內核的瀏覽器下,可使用-webkit-mask-image直接應用一個圖片蒙板!(2011/11/10)this


圖片資源

html代碼

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<title>CSS Bubbles</title>
<style>
html, body
{
	border: none;
	overflow: hidden;
	height: 100%;
}

body {background: url(BG.jpg) bottom}
</style>
</head>

<body onload="Demo()">
<script src="Bubbles.js"></script>
<script>
var MAX = 5;
var i = 0;

function Demo()
{
	CreateBubble();

	if(++i < MAX)
		setTimeout(Demo, 1000);
}
</script>
</body>
</html>

Bubbles代碼

/**
 * JavaScript Bubbles
 *  By EtherDream 2010
 */
+function()
{
	//
	// 瀏覽器輔助
	//
	var _VER_ = navigator.userAgent;
	var _IE6_ = /IE 6/.test(_VER_);

	var STD = !!window.addEventListener;
	var de = document.documentElement;

	_IE6_ && document.execCommand("BackgroundImageCache", false, true);


	//
	// 常量
	//
	var D = 222;				//泡泡直徑
	var K = 0.999;

	var POW_RATE = 0.0001;		//補償機率
	var POW_RANGE = 0.8;		//補償範圍(基於誕生速度)

	function SPEED_X(){return 8 + RND() * 4}
	function SPEED_Y(){return 6 + RND() * 2}


	var arrBubs = [];
	var iBottom;
	var iRight;


	var SQRT = Math.sqrt;
	var ATAN2 = Math.atan2;
	var SIN = Math.sin;
	var COS = Math.cos;
	var ABS = Math.abs;
	var RND = Math.random;
	var ROUND = Math.round;


	function Timer(call, time)
	{
		var last = +new Date;
		var delay = 0;

		return setInterval(function()
		{
			// 時間差累計
			var cur = +new Date;
			delay += (cur - last);
			last = cur;

			// 計算幀數
			if(delay >= time)
			{
				call();
				delay %= time;
			}
		}, 1);
	}

	Timer(update, 17);

	CreateBubble = function()
	{
		var bub = new Bubble();

		bub.setX(0);
		bub.setY(0);
		bub.vx = SPEED_X();
		bub.vy = SPEED_Y();

		arrBubs.push(bub);
	};



	function update()
	{
		var n = arrBubs.length;
		var bub, bub2;
		var i, j;


		updateWall();

		for(i=0; i<n; i++)
		{
			bub = arrBubs[i];

			bub.paint();

			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);
		}

		for(i=0; i<n-1; i++)
		{
			bub = arrBubs[i];

			for(j=i+1; j<n; j++)
			{
				bub2 = arrBubs[j];
				checkCollision(bub, bub2);
			}
		}
	}

	function updateWall()
	{
		iRight = de.clientWidth - D;
		iBottom = de.clientHeight - D;
	}

	function checkWalls(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;
		}
	}

	function rotate(x, y, sin, cos, reverse)
	{
		if(reverse)
			return {x: x * cos + y * sin, y: y * cos - x * sin};
		else
			return {x: x * cos - y * sin, y: y * cos + x * sin};
	}

	function checkCollision(bub0, bub1)
	{
		var dx = bub1.x - bub0.x;
		var dy = bub1.y - bub0.y;
		var dist = SQRT(dx*dx + dy*dy);
	
		if(dist < D)
		{
			// 計算角度和正餘弦值
			var angle = ATAN2(dy,dx);
			var sin = SIN(angle);
			var cos = COS(angle);

			// 旋轉 bub0 的位置
			var pos0 = {x:0, y:0};

			// 旋轉 bub1 的速度
			var pos1 = rotate(dx, dy, sin, cos, true);

			// 旋轉 bub0 的速度
			var vel0 = rotate(bub0.vx, bub0.vy, sin, cos, true);

			// 旋轉 bub1 的速度
			var vel1 = rotate(bub1.vx, bub1.vy, sin, cos, true);

			// 碰撞的做用力
			var vxTotal = vel0.x - vel1.x;
			vel0.x = vel1.x;
			vel1.x = vxTotal + vel0.x;

			// 更新位置
			var absV = ABS(vel0.x) + ABS(vel1.x);
			var overlap = D - ABS(pos0.x - pos1.x);

			pos0.x += vel0.x / absV * overlap;
			pos1.x += vel1.x / absV * overlap;

			// 將位置旋轉回來
			var pos0F = rotate(pos0.x, pos0.y, sin, cos, false);
			var 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);

			// 將速度旋轉回來
			var vel0F = rotate(vel0.x, vel0.y, sin, cos, false);
			var 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;
		}
	}



	var APLHA = 0.8;
	var POW = [1, APLHA, APLHA*APLHA];

	/******************************
	 * Class Bubble
	 ******************************/
	function Bubble()
	{
		var kOpa = [], kStp = [];
		var arrFlt = [];
		var oBox = document.body.appendChild(document.createElement("div"));


		styBox = oBox.style;
		styBox.position = "absolute";
		styBox.width = D + "px";
		styBox.height = D + "px";

		for(var i=0; i<4; i++)
		{
			var div = document.createElement("div");
			var sty = div.style;

			sty.position = "absolute";
			sty.width = "222px";
			sty.height = "222px";

			oBox.appendChild(div);

			// 泡泡頂層
			if(i == 3)
			{
				if(_IE6_)
					sty.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=heart.png)";
				else
					sty.backgroundImage = "url(heart.png)";
				break;
			}

			kOpa[i] = 3 * RND();
			kStp[i] = 0.02 * RND();

			if(STD)
			{
				sty.backgroundImage = "url(ch" + i + ".png)";
				arrFlt[i] = sty;
			}
			else
			{
				sty.filter = "alpha progid:DXImageTransform.Microsoft.AlphaImageLoader(src=ch" + i + ".png)";
				arrFlt[i] = div.filters.alpha;
			}
		}

		this.styBox = styBox;
		this.kOpa = kOpa;
		this.kStp = kStp;
		this.arrFlt = arrFlt;
	}

	Bubble.prototype.setX = function(x)
	{
		this.x = x;
		this.styBox.left = ROUND(x) + "px";
	};

	Bubble.prototype.setY = function(y)
	{
		this.y = y;
		this.styBox.top = ROUND(y) + "px";
	};

	Bubble.prototype.paint = function()
	{
		var i, v;

		for(i=0; i<3; i++)
		{
			v = ABS(SIN(this.kOpa[i] += this.kStp[i] * RND()));
			v *= POW[i];

			v = ((v * 1e4) >> 0) / 1e4;
			this.arrFlt[i].opacity = STD? v : v*100;
		}
	};

}();
相關文章
相關標籤/搜索