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