js实现canvas窗外玻璃雨滴下滑落下下雨动画效果代码

代码语言:html

所属分类:动画

代码描述:js实现canvas窗外玻璃雨滴下滑落下下雨动画效果代码

代码标签: 窗外 玻璃 雨滴 下滑 落下 下雨 动画 效果

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">

    <style media="screen" type="text/css">
        img {
        	display: none;
        }
        body {
        	overflow: hidden;
        }
        #canvas {
        	position: absolute;
        	top: 0px;
        	left: 0px;
        }
    </style>
    <script type="text/javascript">
        /**
 * Defines a new instance of the rainyday.js.
 * @param canvasid DOM id of the canvas used for rendering
 * @param sourceid DOM id of the image element used as background image
 * @param width width of the rendering
 * @param height height of the rendering
 * @param opacity opacity attribute value of the glass canvas (default: 1)
 * @param blur blur radius (default: 20)
 */

function RainyDay(canvasid, sourceid, width, height, opacity, blur) {
	this.canvasid = canvasid;
	this.canvas = document.getElementById(canvasid);

	this.sourceid = sourceid;
	this.img = document.getElementById(sourceid);

	// draw and blur the backgroiund image
	this.prepareBackground(blur ? blur : 20, width, height);
	this.w = this.canvas.width;
	this.h = this.canvas.height;

	// create the glass canvas
	this.prepareGlass(opacity ? opacity : 1);

	// assume default reflection mechanism
	this.reflection = this.REFLECTION_MINIATURE;

	// assume default trail mechanism
	this.trail = this.TRAIL_DROPS;

	// assume default gravity
	this.gravity = this.GRAVITY_NON_LINEAR;

	// drop size threshold for the gravity algorhitm
	this.VARIABLE_GRAVITY_THRESHOLD = 3;

	// gravity angle
	this.VARIABLE_GRAVITY_ANGLE = Math.PI / 2;

	// frames per second animation speed
	this.VARIABLE_FPS = 25;

	// context fill style when no REFLECTION_NONE is used
	this.VARIABLE_FILL_STYLE = '#8ED6FF';

	// collisions enabled by default
	this.VARIABLE_COLLISIONS = false;

	// assume default collision algorhitm
	this.collision = this.COLLISION_SIMPLE;
}

/**
 * Create the helper canvas for rendering raindrop reflections.
 */
RainyDay.prototype.prepareReflections = function() {
	// new canvas
	this.reflected = document.createElement('canvas');
	this.reflected.width = this.canvas.width;
	this.reflected.height = this.canvas.height;

	var ctx = this.reflected.getContext('2d');

	// rotate by 180 degress
	ctx.translate(this.reflected.width / 2, this.reflected.height / 2);
	ctx.rotate(Math.PI);

	ctx.drawImage(this.img, -this.reflected.width / 2, -this.reflected.height / 2, this.reflected.width, this.reflected.height);
};

/**
 * Create the glass canvas and position it directly over the main one.
 * @param opacity opacity attribute value of the glass canvas
 */
RainyDay.prototype.prepareGlass = function(opacity) {
	this.glass = document.createElement('canvas');
	this.glass.width = this.canvas.width;
	this.glass.height = this.canvas.height;
	this.glass.style.position = "absolute";
	this.glass.style.top = this.canvas.offsetTop;
	this.glass.style.left = this.canvas.offsetLeft;
	this.glass.style.zIndex = this.canvas.style.zIndex + 100;
	this.canvas.parentNode.appendChild(this.glass);
	this.context = this.glass.getContext('2d');
	this.glass.style.opacity = opacity;
};

/**
 * Creates a new preset object with given attributes.
 * @param min minimum size of a drop
 * @param base base value for randomizing drop size
 * @param quan probability of selecting this preset (must be between 0 and 1)
 * @returns present object with given attributes
 */
RainyDay.prototype.preset = function(min, base, quan) {
	return {
		"min": min,
		"base": base,
		"quan": quan
	}
};

/**
 * Main function for starting rain rendering.
 * @param presets list of presets to be applied
 * @param speed speed of the animation (if not provided or 0 static image will be generated)
 */
RainyDay.prototype.rain = function(presets, speed) {
	// prepare canvas for drop reflections
	if (this.reflection != this.REFLECTION_NONE) {
		this.prepareReflections();
	}

	if (speed > 0) {
		// animation
		this.presets = presets;

		this.PRIVATE_GRAVITY_FORCE_FACTOR_Y = (this.VARIABLE_FPS * 0.005) / 25;
		this.PRIVATE_GRAVITY_FORCE_FACTOR_X = ((Math.PI / 2) - this.VARIABLE_GRAVITY_ANGLE) * (this.VARIABLE_FPS * 0.005) / 50;

		// prepare gravity matrix
		if (this.VARIABLE_COLLISIONS) {

			// calculate max radius of a drop to establish gravity matrix resolution
			var maxDropRadius = 0;
			for (var i = 0; i < presets.length; i++) {
				if (presets[i].base + presets[i].min > maxDropRadius) {
					maxDropRadius = Math.floor(presets[i].base + presets[i].min);
				}
			}

			if (maxDropRadius > 0) {
				// initialize the gravity matrix
				var mwi = Math.ceil(this.w / maxDropRadius);
				var mhi = Math.ceil(this.h / maxDropRadius);
				this.matrix = new CollisionMatrix(mwi, mhi, maxDropRadius);
			} else {
				this.VARIABLE_COLLISIONS = false;
			}
		}

		setInterval(
			(function(self) {
				return function() {
					var random = Math.random();
					// select matching preset
					var preset;
					for (var i = 0; i < presets.length; i++) {
						if (random < presets[i].quan) {
							preset = presets[i];
							break;
						}
					}
					if (preset) {
						self.putDrop(new Drop(self, Math.random() * self.w, Math.random() * self.h, preset.min, preset.base));
					}
				}
			})(this),
			speed
		);
	} else {
		// static picture
		for (var i = 0; i < presets.length; i++) {
			var preset = presets[i];
			for (var c = 0; c < preset.quan; ++c) {
				this.putDrop(new Drop(this, Math.random() * this.w, Math.random() * this.h, preset.min, preset.base));
			}
		}
	}
};
/**
 * Adds a new raindrop to the animation.
 * @param drop drop object to be added to the animation
 */
RainyDay.prototype.putDrop = function(drop) {
	drop.draw();
	if (this.gravity && drop.r1 > this.VARIABLE_GRAVITY_THRESHOLD) {

		if (this.VARIABLE_COLLISIONS) {
			// put on the gravity matrix
			this.matrix.update(drop);
		}

		drop.animate();
	}
};
/**
 * Imperfectly approximates shape of a circle.
 * @param iterations number of iterations applied to the size approximation algorithm
 * @returns list of points approximating a circle shape
 */
RainyDay.prototype.getLinepoints = function(iterations) {
	var pointList = {};
	pointList.first = {
		x: 0,
		y: 1
	};
	var lastPoint = {
		x: 1,
		y: 1
	}
	var minY = 1;
	var maxY = 1;
	var point;
	var nextPoint;
	var dx, newX, newY;

	pointList.first.next = lastPoint;
	for (var i = 0; i < iterations; i++) {
		point = pointList.first;
		while (point.next != null) {
			nextPoint = point.next;

			dx = nextPoint.x - point.x;
			newX = 0.5 * (point.x + nextPoint.x);
			newY = 0.5 * (point.y + nextPoint.y);
			newY += dx * (Math.random() * 2 - 1);

			var newPoint = {
				x: newX,
				y: newY
			};

			//min, max
			if (newY < minY) {
				minY = newY;
			} else if (newY > maxY) {
				maxY = newY;
			}

			//put between points
			newPoint.next = nextPoint;
			point.next = newPoint;

			point = nextPoint;
		}
	}

	//normalize to values between 0 and 1
	if (maxY != minY) {
		var normalizeRate = 1 / (maxY - minY);
		point = pointList.first;
		while (point != null) {
			point.y = normalizeRate * (point.y - minY);
			point = point.next;
		}
	} else {
		point = pointList.first;
		while (point != null) {
			point.y = 1;
			point = point.next;
		}
	}

	return pointList;
};

/**
 * Defines a new raindrop object.
 * @param rainyday reference to the parent object
 * @param centerX x position of the center of this drop
 * @param centerY y position of the center of this drop
 * @param min minimum size of a drop
 * @param base base value for randomizing drop size
 */

function Drop(rainyday, centerX, centerY, min, base) {
	this.x = Math.floor(centerX);
	this.y = Math.floor(centerY);
	this.r1 = (Math.random() * base) + min;
	this.rainyday = rainyday;
	var iterations = 4;
	this.r2 = 0.8 * this.r1;
	this.linepoints = rainyday.getLinepoints(iterations);
	this.context = rainyday.context;
	this.reflection = rainyday.reflected;
}

/**
 * Draws a raindrop on canvas at the current position.
 */
Drop.prototype.draw = function() {
	var phase = 0;
	var point;
	var rad, theta;
	var x0, y0;

	this.context.save();
	this.context.beginPath();
	point = this.linepoints.first;
	theta = phase;
	rad = this.r2 + 0.5 * Math.random() * (this.r2 - this.r1);
	x0 = this.x + rad * Math.cos(theta);
	y0 = this.y + rad * Math.sin(theta);
	this.context.lineTo(x0, y0);
	while (point.next != null) {
		point = point.next;
		theta = (Math.PI * 2 * point.x) + phase;
		rad = this.r2 + 0.5 * Math.random() * (this.r2 - this.r1);
		x0 = this.x + rad * Math.cos(theta);
		y0 = this.y + rad * Math.sin(theta);
		this.context.lineTo(x0, y0);
	}

	this.context.clip();

	if (this.rainyday.reflection) {
		this.rainyday.reflection(this);
	}

	this.context.restore();
};

/**
 * Clears the raindrop region.
 * @param force force stop
 * @returns true if the animation is stopped
 */
Drop.prototype.clear = function(force) {
	this.context.clearRect(this.x - this.r1 - 1, this.y - this.r1 - 1, 2 * this.r1 + 2, 2 * this.r1 + 2);
	if (force) {
		// forced
		clearInterval(this.intid);
		return true;
	}
	if (this.y - this.r1 > this.rainyday.h) {
		// over the bottom edge, stop the thread
		clearInterval(this.intid);
		return true;
	}
	if ((this.x - this.r1 > this.rainyday.w) || (this.x + this.r1 < 0)) {
		// over the right or left edge, stop the thread
		clearInterval(this.intid);
		return true;
	}
	return false;
};

/**
 * Moves the raindrop to a new position according to the gravity.
 */
Drop.prototype.animate = function() {
	this.intid = setInterval(
		(function(self) {
			return function() {
				var stopped = self.rainyday.gravity(self);
				if (!stopped && self.rainyday.trail) {
					self.rainyday.trail(self);
				}
				if (self.rainyday.VARIABLE_COLLISIONS) {
					var collision = self.rainyday.matrix.update(self, stopped);
					if (collision) {
						self.rainyday.collision(self, collision.drop);
					}
				}
			}
		})(this),
		Math.floor(1000 / this.rainyday.VARIABLE_FPS)
	);
};

/**
 * TRAIL function: no trail at all
 * @param drop raindrop object
 */
RainyDay.prototype.TRAIL_NONE = function(drop) {
	// nothing going on here
};

/**
 * TRAIL function: trail of small drops (default)
 * @param drop raindrop object
 */
RainyDay.prototype.TRAIL_DROPS = function(drop) {
	if (!drop.trail_y || drop.y - drop.trail_y >= Math.random() * 10 * drop.r1) {
		drop.trail_y = drop.y;
		this.putDrop(new Drop(this, drop.x, drop.y - drop.r1 - 5, 0, Math.ceil(drop.r1 / 5)));
	}
};
/**
 * GRAVITY function: no gravity at all
 * @param drop raindrop object
 * @returns true if the animation is stopped
 */
RainyDay.prototype.GRAVITY_NONE = function(drop) {
	return true;
};

/**
 * GRAVITY function: linear gravity
 * @param drop raindrop object
 * @returns true if the animation is stopped
 */
RainyDay.prototype.GRAVITY_LINEAR = function(drop) {
	if (drop.clear()) {
		return true;
	}

	if (drop.yspeed) {
		drop.yspeed += this.PRIVATE_GRAVITY_FORCE_FACTOR_Y * Math.floor(drop.r1);
		drop.xspeed += this.PRIVATE_GRAVITY_FORCE_FACTOR_X * Math.floor(drop.r1);
	} else {
		drop.yspeed = this.PRIVATE_GRAVITY_FORCE_FACTOR_Y;
		drop.xspeed = this.PRIVATE_GRAVITY_FORCE_FACTOR_X;
	}

	drop.y += drop.yspeed;
	drop.draw();
	return false;
};

/**
 * GRAVITY function: non-linear gravity (default)
 * @param drop raindrop object
 * @returns true if the animation is stopped
 */
RainyDay.prototype.GRAVITY_NON_LINEAR = function(drop) {
	if (drop.clear()) {
		return true;
	}

	if (!drop.seed || drop.seed < 0) {
		drop.seed = Math.floor(Math.random() * this.VARIABLE_FPS);
		drop.skipping = drop.skipping ==.........完整代码请登录后点击上方下载按钮下载查看

网友评论0