玻璃上的雨滴(动态特效)

动态变化中的静态截图

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD><TITLE> New Document </TITLE><META NAME="Generator" CONTENT="EditPlus"><META NAME="Author" CONTENT=""><META NAME="Keywords" CONTENT=""><META NAME="Description" CONTENT=""><style>html, body{margin:0;padding:0;background-color:#ffffff;
}canvas{display:block;position:absolute;top:0;left:0;
}</style></HEAD><BODY><script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.3.5/pixi.min.js"></script><script>var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }/*** Raindrop fragment shader, being used by PIXI.js in the EffectCanvas object* {{uniforms: {time: {type: string, value: number}, iResolution: {type: string, value: [*]}}, fragment: string}}*/
var shaderData = {uniforms: {iResolution: {type: 'v2',value: [window.innerWidth, window.innerHeight]},vTextureSize: {type: 'v2',value: [0, 0]},uTextureForeground: {type: 'sampler2D',value: null},uTextureBackground: {type: 'sampler2D',value: null},uTextureDropShine: {type: 'sampler2D',value: null}},fragment: '\n        precision mediump float;\n    \n        //Textures\n        uniform sampler2D uTextureForeground;\n        uniform sampler2D uTextureBackground;\n        uniform sampler2D uTextureDropShine;\n        \n        //Canvas image data\n        uniform sampler2D uSampler;\n    \n        //The resolution and coordinates of the current pixel\n        uniform vec2 iResolution;\n        uniform vec2 vTextureSize;\n        varying vec2 vTextureCoord;\n        \n        //Function to get the vec2 value of the current coordinate\n        vec2 texCoord(){\n            return vec2(gl_FragCoord.x, iResolution.y - gl_FragCoord.y) / iResolution;\n        }\n\n        //Scales the bg up and proportionally to fill the container\n        vec2 scaledTextureCoordinate(){\n            float ratioCanvas = iResolution.x / iResolution.y;\n            float ratioImage = vTextureSize.x / vTextureSize.y;\n            \n            vec2 scale = vec2(1, 1);\n            vec2 offset = vec2(0, 0);\n            float ratioDelta = ratioCanvas - ratioImage;\n\n            if(ratioDelta >= 0.0){\n                scale.y = (1.0 + ratioDelta);\n                offset.y = ratioDelta / 2.0;\n            }else{\n                scale.x = (1.0 - ratioDelta);\n                offset.x = -(ratioDelta / 2.0);\n            }\n\n            return (texCoord() + offset) / scale;\n        }\n        \n        //Alpha-blends two colors\n        vec4 blend(vec4 bg, vec4 fg){\n            vec3 bgm = bg.rgb * bg.a;\n            vec3 fgm = fg.rgb * fg.a;\n            float ia = 1.0 - fg.a;\n            float a = (fg.a + bg.a * ia);\n            \n            vec3 rgb;\n            \n            if(a != 0.0){\n                rgb = (fgm + bgm * ia) / a;\n            }else{\n                rgb = vec3(0.0,0.0,0.0);\n            }\n            \n            return vec4(rgb,a);\n        }\n        \n        vec2 pixel(){\n            return vec2(1.0, 1.0) / iResolution;\n        }\n        \n        //Get color from fg\n        vec4 fgColor(){\n            return texture2D(uSampler, vTextureCoord);\n        }\n                \n        void main(){\n            vec4 bg = texture2D(uTextureBackground, scaledTextureCoordinate());\n            vec4 cur = fgColor();\n\n            float d = cur.b; // "thickness"\n            float x = cur.g;\n            float y = cur.r;\n            float a = smoothstep(0.65, 0.7, cur.a);\n            \n            vec4 smoothstepped = vec4(y, x, d, a);\n\n            vec2 refraction = (vec2(x, y) - 0.5) * 2.0;\n            vec2 refractionPos = scaledTextureCoordinate() + (pixel() * refraction * (256.0 + (d * 512.0)));\n            vec4 tex = texture2D(uTextureForeground, refractionPos);\n            \n            float maxShine = 390.0;\n            float minShine = maxShine * 0.18;\n            vec2 shinePos = vec2(0.5, 0.5) + ((1.0 / 512.0) * refraction) * -(minShine + ((maxShine-minShine) * d));\n            vec4 shine = texture2D(uTextureDropShine, shinePos);\n            tex = blend(tex,shine);\n            \n            vec4 fg = vec4(tex.rgb, a);\n            gl_FragColor = blend(bg, fg);\n        }\n\t'
};/*** Application Class* Bootstraps the entire application and initializes all objects*/var Application = function () {/*** Application constructor*/function Application() {var _this = this;_classCallCheck(this, Application);this.width = window.innerWidth;this.height = window.innerHeight;// Define the assets that PIXI needs to preload to use later in the applicationthis.loader = PIXI.loader.add('https://stefanweck.nl/codepen/alpha.png').add('https://stefanweck.nl/codepen/shine.png').add('https://stefanweck.nl/codepen/background.jpg').add('https://stefanweck.nl/codepen/foreground.jpg').load(function () {return _this.initialize();});}/*** Initialize is ran when the image loader is done loading all resources* @return void*/_createClass(Application, [{key: 'initialize',value: function initialize() {var _this2 = this;// Create the Stats object and append it to the DOMthis.stats = new Stats();this.stats.domElement.style.position = 'absolute';this.stats.domElement.style.left = '0px';this.stats.domElement.style.top = '0px';this.stats.domElement.style.zIndex = '9000';document.body.appendChild(this.stats.domElement);// Create a new instance of the EffectCanvas which is going to produce all of the visualsthis.effectCanvas = new EffectCanvas(this.width, this.height, this.loader);// Resize listener for the canvas to fill browser window dynamicallywindow.addEventListener('resize', function () {return _this2.resizeCanvas();}, false);// Start the initial loop function for the first timethis.loop();}/*** Simple resize function. Reinitializing everything on the canvas while changing the width/height* @return {void}*/}, {key: 'resizeCanvas',value: function resizeCanvas() {this.width = window.innerWidth;this.height = window.innerHeight;this.effectCanvas.resize(this.width, this.height);}/*** Update and render the application at least 60 times a second* @return {void}*/}, {key: 'loop',value: function loop() {var _this3 = this;window.requestAnimationFrame(function () {return _this3.loop();});this.stats.begin();this.effectCanvas.update(this.width, this.height);this.effectCanvas.render();this.stats.end();}}]);return Application;
}();/*** EffectCanvas Class*/var EffectCanvas = function () {/*** EffectCanvas constructor*/function EffectCanvas(width, height, loader) {_classCallCheck(this, EffectCanvas);// Create and configure the rendererthis.renderer = new PIXI.autoDetectRenderer(width, height, {antialias: false,transparent: false});this.renderer.autoResize = true;document.body.appendChild(this.renderer.view);// Create a container object called the `stage`this.stage = new PIXI.Container();// Create a graphics object that is as big as the scene of the users window// Else the shader won't fill the entire screenthis.background = new PIXI.Graphics();this.background.fillAlphanumber = 0;this.background.beginFill('0xffffff');this.background.drawRect(0, 0, width, height);this.background.endFill();this.background.alpha = 0;this.stage.addChild(this.background);// Create the DropletManager and pass it the stage so it can insert the droplet containers into itthis.dropletManager = new DropletManager(this.stage, loader);// Send information about the textures and the size of the background texture through the uniforms to the shadershaderData.uniforms.uTextureDropShine.value = loader.resources['https://stefanweck.nl/codepen/shine.png'].texture;shaderData.uniforms.uTextureBackground.value = loader.resources['https://stefanweck.nl/codepen/background.jpg'].texture;shaderData.uniforms.uTextureForeground.value = loader.resources['https://stefanweck.nl/codepen/foreground.jpg'].texture;shaderData.uniforms.vTextureSize.value = [loader.resources['https://stefanweck.nl/codepen/background.jpg'].texture.width, loader.resources['https://stefanweck.nl/codepen/background.jpg'].texture.height];// Create our Pixi filter using our custom shader codethis.dropletShader = new PIXI.Filter('', shaderData.fragment, shaderData.uniforms);// Apply it to our objectthis.stage.filters = [this.dropletShader];}/*** Simple resize function which redraws our graphics object that should fill the entire screen* @return {void}*/_createClass(EffectCanvas, [{key: 'resize',value: function resize(width, height) {this.renderer.resize(width, height);this.background.clear();this.background.beginFill('0xffffff');this.background.drawRect(0, 0, width, height);this.background.endFill();}/*** Updates the application and every child of the application* @return {void}*/}, {key: 'update',value: function update(width, height) {this.updateShader(width, height);this.dropletManager.update(width, height);}/*** Updates the uniform values in the shader* @return {void}*/}, {key: 'updateShader',value: function updateShader(width, height) {this.dropletShader.uniforms.iResolution = [width, height];}/*** Renders the application and every child of the application* @return {void}*/}, {key: 'render',value: function render() {this.renderer.render(this.stage);}}]);return EffectCanvas;
}();/*** DropletManager class*/var DropletManager = function () {/*** EffectCanvas constructor*/function DropletManager(stage, loader) {_classCallCheck(this, DropletManager);var smallDropletAmount = 9000;var largeDropletAmount = 200;//Quick implementation to make sure there aren't out of this world thunderstorms on mobileif (stage.width < 700) {smallDropletAmount = 3000;largeDropletAmount = 150;}this.options = {spawnRate: {small: 0.6,large: 0.05},spawnsPerFrame: {small: 200,large: 5},spawnMass: {small: {min: 1,max: 2},large: {min: 7,max: 10}},poolDroplets: {small: {min: smallDropletAmount - 500,max: smallDropletAmount},large: {min: largeDropletAmount - 100,max: largeDropletAmount}},maximumMassGravity: 17,maximumMass: 21,dropletGrowSpeed: 1,dropletShrinkSpeed: 2,dropletContainerSize: 100};// Define a position matrix so we can calculate all the edges of a droplet in a single loopthis.positionMatrix = [[-1, -1], [1, -1], [-1, 1], [1, 1]];this.smallDroplets = [];this.largeDroplets = [];this.dropletSmallTexture = loader.resources['https://stefanweck.nl/codepen/alpha.png'].texture;this.dropletLargeTexture = loader.resources['https://stefanweck.nl/codepen/alpha.png'].texture;// Create a container for all the dropletsthis.smallDropletContainer = new DropletPool(Droplet, this.dropletSmallTexture, this.options.poolDroplets.small.min, this.options.poolDroplets.small.max);this.largeDropletContainer = new DropletPool(LargeDroplet, this.dropletLargeTexture, this.options.poolDroplets.large.min, this.options.poolDroplets.large.max);stage.addChild(this.largeDropletContainer);stage.addChild(this.smallDropletContainer);}/*** Updates the application and every child of the application* @return {void}*/_createClass(DropletManager, [{key: 'update',value: function update(width, height) {DropletManager.removeLargeOffscreenDroplets(width, height, this.largeDroplets, this.largeDropletContainer);// Trigger the spawn function for a small droplet as much times as is configured in the optionsfor (var i = 0; i < this.options.spawnsPerFrame.small; i++) {this.spawnNewSmallDroplet(width, height);}// Trigger the spawn function for a large droplet as much times as is configured in the optionsfor (var _i = 0; _i < this.options.spawnsPerFrame.large; _i++) {this.spawnNewLargeDroplet(width, height);}// Check if we need to do anything with a large Droplet// We don't process small droplets because they are 'dumb' objects that don't move after they've spawnedthis.checkLargeDropletLogic();}/*** Checks whether a big droplet hits a smaller droplet, if so, it grows by half of the smaller droplets size* @return {void}*/}, {key: 'checkLargeDropletLogic',value: function checkLargeDropletLogic() {// Store the length of the array so the for loop doesn't have to do that every runvar largeDropletsLength = this.largeDroplets.length;for (var i = largeDropletsLength - 1; i >= 0; i--) {this.updateLargeDropletSize(this.largeDroplets[i]);this.checkDropletMovement(this.largeDroplets[i]);this.checkLargeToSmallDropletCollision(this.largeDroplets[i]);this.checkLargeToLargeDropletCollision(this.largeDroplets[i]);this.removeLargeDroplets(i);}}/*** Function that checks if a single large Droplet should be removed* @param i - The current droplet that we are processing*/}, {key: 'removeLargeDroplets',value: function removeLargeDroplets(i) {if (this.largeDroplets[i].mass === 0 && this.largeDroplets[i].toBeRemoved === true) {this.largeDropletContainer.destroy(this.largeDroplets[i]);this.largeDroplets.splice(i, 1);}}/*** Function that updates the size of a single large Droplet* @param droplet*/}, {key: 'updateLargeDropletSize',value: function updateLargeDropletSize(droplet) {// If a droplet needs to be removed, we have to shrink it down to 0if (droplet.toBeRemoved === true) {this.shrinkDropletSize(droplet);} else {this.growDropletSize(droplet);}// Update the width and height of the droplet based on the new mass of the dropletdroplet.width = droplet.mass * 6;droplet.height = droplet.mass * 7;}/*** Shrink a droplet based on the configured shrink speed. If it will be too small, we set the mass to 0* @param {LargeDroplet} droplet*/}, {key: 'shrinkDropletSize',value: function shrinkDropletSize(droplet) {if (droplet.mass - this.options.dropletShrinkSpeed <= 0) {droplet.mass = 0;} else {droplet.mass -= this.options.dropletShrinkSpeed;}}/*** Grow a droplet based on the targetMass he has* @param {LargeDroplet} droplet*/}, {key: 'growDropletSize',value: function growDropletSize(droplet) {// If a droplet has already reached its target mass, exit hereif (droplet.mass === droplet.targetMass) {return;}// Check if we can grow the droplet based on the configured grow speedif (droplet.mass + this.options.dropletGrowSpeed >= droplet.targetMass) {droplet.mass = droplet.targetMass;} else {droplet.mass += this.options.dropletGrowSpeed;}}/*** Check whether a large droplet should be moving or not* @param {LargeDroplet} droplet* @return {void}*/}, {key: 'checkDropletMovement',value: function checkDropletMovement(droplet) {// If the droplet is going to be removed at the end of this loop, don't bother checking itif (droplet.toBeRemoved === true) {return;}// Check if the droplets mass is high enough to be moving, and if the droplet is not moving yetif (droplet.mass < this.options.maximumMassGravity && droplet.dropletVelocity.y === 0 && droplet.dropletVelocity.x === 0) {// There's a slight chance that the droplet starts movingif (Math.random() < 0.01) {droplet.dropletVelocity.y = Utils.getRandomInt(0.5, 3);}} else if (droplet.mass < this.options.maximumMassGravity && droplet.dropletVelocity.y !== 0) {// There's a slight chance that the droplet shifts to the left or the right, just like real droplets attach to droplets near themif (Math.random() < 0.1) {droplet.x += Utils.getRandomInt(-10, 10) / 10;}// There's a slight chance that the droplet stops movingif (Math.random() < 0.1) {droplet.dropletVelocity.y = 0;}} else if (droplet.mass >= this.options.maximumMassGravity && droplet.dropletVelocity.y < 10) {// The droplet is falling because it is too heavy, its speed and direction are now setdroplet.dropletVelocity.y = Utils.getRandomInt(10, 20);droplet.dropletVelocity.x = Utils.getRandomInt(-10, 10) / 10;}// Increase the x and y positions of the droplet based on its velocitydroplet.y += droplet.dropletVelocity.y;droplet.x += droplet.dropletVelocity.x;}/*** Checks in which small droplet arrays the large droplet is positioned* @param {Droplet} droplet*/}, {key: 'getDropletPresenceArray',value: function getDropletPresenceArray(droplet) {// Define a set of array indexes through which we hava to search for collisionvar arrayIndexes = [];var length = this.positionMatrix.length;// Loop through each positionMatrix to calculate the position of every edge of a dropletfor (var i = 0; i < length; i++) {var edgePosition = {x: Math.floor((droplet.x + droplet.width / 7 * this.positionMatrix[i][0]) / this.options.dropletContainerSize),y: Math.floor((droplet.y + droplet.height / 7 * this.positionMatrix[i][1]) / this.options.dropletContainerSize)};// Always push the first position in the arrayIndexes array, we use that value to compare the other edges toif (i === 0) {arrayIndexes.push(edgePosition);continue;}// If the current position differs from the first position, store the new value because that means that this is also an array we need to check for collisionif (arrayIndexes[0].x !== edgePosition.x || arrayIndexes[0].y !== edgePosition.y) {arrayIndexes.push(edgePosition);}}return arrayIndexes;}/*** Check the collision between one large Droplet and all the other Droplets* @param droplet*/}, {key: 'checkLargeToLargeDropletCollision',value: function checkLargeToLargeDropletCollision(droplet) {if (droplet.toBeRemoved === true) {return;}// Store the length of the droplets array so we have that valua cached in the for loopvar length = this.largeDroplets.length;for (var i = length - 1; i >= 0; i--) {// Don't bother checking this droplet against itselfif (droplet.x === this.largeDroplets[i].x && droplet.y === this.largeDroplets[i].y) {continue;}// Calculate the difference in position for the horizontal and the vertical axisvar dx = droplet.x - this.largeDroplets[i].x;var dy = droplet.y - this.largeDroplets[i].y;// Calculate the distance between the current droplet and the current other dropletvar distance = Math.sqrt(dx * dx + dy * dy);// If the distance between the droplets is close enough, the droplet colliding increases in sizeif (distance <= droplet.width / 7 + this.largeDroplets[i].width / 7) {if (droplet.mass + this.largeDroplets[i].mass <= this.options.maximumMass) {droplet.targetMass = droplet.mass + this.largeDroplets[i].mass;} else {droplet.targetMass = this.options.maximumMass;}// The other droplet should be removed at the end of this loopthis.largeDroplets[i].toBeRemoved = true;}}}/*** Checks whether a big droplet hits a smaller droplet, if so, it grows by half of the smaller droplets size* @param {LargeDroplet} droplet* @return {void}*/}, {key: 'checkLargeToSmallDropletCollision',value: function checkLargeToSmallDropletCollision(droplet) {if (droplet.toBeRemoved === true) {return;}// Define a set of array indexes through which we have to search for collisionvar arrayIndexes = this.getDropletPresenceArray(droplet);for (var i = 0; i < arrayIndexes.length; i++) {// If the small droplet doesn't exist anymore, we can continue to the next value in the loopif (typeof this.smallDroplets[arrayIndexes[i].x] === 'undefined' || typeof this.smallDroplets[arrayIndexes[i].x][arrayIndexes[i].y] === 'undefined') {continue;}// Store the length of the array so the for loop doesn't have to do that every runvar smallDropletsLength = this.smallDroplets[arrayIndexes[i].x][arrayIndexes[i].y].length;for (var c = smallDropletsLength - 1; c >= 0; c--) {// Calculate the difference in position for the horizontal and the vertical axisvar dx = droplet.x - this.smallDroplets[arrayIndexes[i].x][arrayIndexes[i].y][c].x;var dy = droplet.y - this.smallDroplets[arrayIndexes[i].x][arrayIndexes[i].y][c].y;// Calculate the distance between the current droplet and the current other dropletvar distance = Math.sqrt(dx * dx + dy * dy);// If the distance is small enough we can increase the size of the large droplet and remove the small dropletif (distance <= droplet.width / 7 + this.smallDroplets[arrayIndexes[i].x][arrayIndexes[i].y][c].width / 7) {if (droplet.mass + this.smallDroplets[arrayIndexes[i].x][arrayIndexes[i].y][c].mass / 3 <= this.options.maximumMass) {droplet.targetMass = droplet.mass + this.smallDroplets[arrayIndexes[i].x][arrayIndexes[i].y][c].mass / 3;}// Remove the small droplet and put it back in the object poolthis.smallDropletContainer.destroy(this.smallDroplets[arrayIndexes[i].x][arrayIndexes[i].y][c]);this.smallDroplets[arrayIndexes[i].x][arrayIndexes[i].y].splice(c, 1);}}}}/*** Spawns a new small droplet on the screen based on the spawn chance* @param {number} width* @param {number} height* @return {void}*/}, {key: 'spawnNewSmallDroplet',value: function spawnNewSmallDroplet(width, height) {// If our random value doesn't match the given spawn rate, we don't spawn a dropletif (Math.random() > this.options.spawnRate.small) {return;}// Get a new droplet object from the poolvar droplet = this.smallDropletContainer.get();// If the pool decided that we can't add more droplets, exit hereif (droplet === null) {return;}var position = {x: Utils.getRandomInt(0, width),y: Utils.getRandomInt(0, height)};var mass = Utils.getRandomInt(this.options.spawnMass.small.min, this.options.spawnMass.small.max);var arrayIndex = {x: Math.floor(position.x / this.options.dropletContainerSize),y: Math.floor(position.y / this.options.dropletContainerSize)};// Make sure the droplet updates with a new position and radiusdroplet.x = position.x;droplet.y = position.y;droplet.mass = mass;droplet.width = droplet.mass * 8;droplet.height = droplet.mass * 8;if (typeof this.smallDroplets[arrayIndex.x] === 'undefined') {this.smallDroplets[arrayIndex.x] = [];}if (typeof this.smallDroplets[arrayIndex.x][arrayIndex.y] === 'undefined') {this.smallDroplets[arrayIndex.x][arrayIndex.y] = [];}this.smallDroplets[arrayIndex.x][arrayIndex.y].push(droplet);}/*** Spawns a new large droplet on the screen based on the spawn chance* @param {number} width* @param {number} height* @return {void}*/}, {key: 'spawnNewLargeDroplet',value: function spawnNewLargeDroplet(width, height) {// If our random value doesn't match the given spawn rate, we don't spawn a dropletif (Math.random() > this.options.spawnRate.large) {return;}// Get a new droplet object from the poolvar droplet = this.largeDropletContainer.get();// If the pool decided that we can't add more droplets, exit hereif (droplet === null) {return;}// Make sure the droplet updates with a new position and radiusvar mass = Utils.getRandomInt(this.options.spawnMass.large.min, this.options.spawnMass.large.max);droplet.x = Utils.getRandomInt(0, width);droplet.y = Utils.getRandomInt(-100, height / 1.5);droplet.mass = mass / 2;droplet.targetMass = mass;droplet.width = droplet.mass * 6;droplet.height = droplet.mass * 7;droplet.dropletVelocity.x = 0;droplet.toBeRemoved = false;this.largeDroplets.push(droplet);}/*** Checks each droplet to see if it is positioned offscreen. If so, it's being pushed back into the object pool to be reused* @param {number} width* @param {number} height* @param {Array} dropletArray* @param {DropletPool} dropletContainer* @return {void}*/}], [{key: 'removeLargeOffscreenDroplets',value: function removeLargeOffscreenDroplets(width, height, dropletArray, dropletContainer) {// Store the length of the array so the for loop doesn't have to do that every runvar length = dropletArray.length;for (var i = length - 1; i >= 0; i--) {if (dropletArray[i].x > width + 10 || dropletArray[i].x < -10 || dropletArray[i].y > height + 10 || dropletArray[i].y < -100) {dropletContainer.destroy(dropletArray[i]);dropletArray.splice(i, 1);}}}}]);return DropletManager;
}();/*** DropletPool class* Functions as an object pool so we can re-use droplets over and over again*/var DropletPool = function (_PIXI$particles$Parti) {_inherits(DropletPool, _PIXI$particles$Parti);/*** DropletPool constructor*/function DropletPool(ObjectToCreate, objectTexture, startingSize, maximumSize) {_classCallCheck(this, DropletPool);var _this4 = _possibleConstructorReturn(this, (DropletPool.__proto__ || Object.getPrototypeOf(DropletPool)).call(this, maximumSize, {scale: true,position: true,rotation: false,uvs: false,alpha: false}));_this4.ObjectToCreate = ObjectToCreate;_this4.objectTexture = objectTexture;_this4.pool = [];_this4.inUse = 0;_this4.startingSize = startingSize;_this4.maximumSize = maximumSize;_this4.initialize();return _this4;}/*** Initialize the initial batch of objects that we are going to use throughout the application* @return {void}*/_createClass(DropletPool, [{key: 'initialize',value: function initialize() {for (var i = 0; i < this.startingSize; i += 1) {var droplet = new this.ObjectToCreate(this.objectTexture);droplet.x = -100;droplet.y = -100;droplet.anchor.set(0.5);// Add the object to the PIXI Container and store it in the poolthis.addChild(droplet);this.pool.push(droplet);}}/*** Get an object from the object pool, checks whether there is an object left or it if may create a new object otherwise* @returns {object}*/}, {key: 'get',value: function get() {// Check if we have reached the maximum number of objects, if so, return nullif (this.inUse >= this.maximumSize) {return null;}// We haven't reached the maximum number of objects yet, so we are going to reuse an objectthis.inUse++;// If there are still objects in the pool return the last item from the poolif (this.pool.length > 0) {return this.pool.pop();}// The pool was empty, but we are still allowed to create a new object and return thatvar droplet = new this.ObjectToCreate(this.objectTexture);droplet.x = -100;droplet.y = -100;droplet.anchor.set(0.5, 0.5);// Add the object to the PIXI Container and return itthis.addChild(droplet);return droplet;}/*** Put an element back into the object pool and reset it for later use* @param element - The object that should be pushed back into the object pool to be reused later on* @return {void}*/}, {key: 'destroy',value: function destroy(element) {if (this.inUse - 1 < 0) {console.error('Something went wrong, you cant remove more elements than there are in the total pool');return;}// Move the droplet offscreen, we cant't set visible or rendering to false because that doesn't matter in a PIXI.ParticleContainer// @see: https://github.com/pixijs/pixi.js/issues/1910element.x = -100;element.y = -100;// Push the element back into the object pool so it can be reused againthis.inUse -= 1;this.pool.push(element);}}]);return DropletPool;
}(PIXI.particles.ParticleContainer);/*** Droplet Class*/var Droplet = function (_PIXI$Sprite) {_inherits(Droplet, _PIXI$Sprite);/*** Droplet constructor*/function Droplet(texture) {_classCallCheck(this, Droplet);var _this5 = _possibleConstructorReturn(this, (Droplet.__proto__ || Object.getPrototypeOf(Droplet)).call(this, texture));_this5.mass = 0;return _this5;}return Droplet;
}(PIXI.Sprite);/*** LargeDroplet Class*/var LargeDroplet = function (_Droplet) {_inherits(LargeDroplet, _Droplet);/*** Droplet constructor*/function LargeDroplet(texture) {_classCallCheck(this, LargeDroplet);var _this6 = _possibleConstructorReturn(this, (LargeDroplet.__proto__ || Object.getPrototypeOf(LargeDroplet)).call(this, texture));_this6.dropletVelocity = new PIXI.Point(0, 0);_this6.toBeRemoved = false;_this6.targetMass = 0;return _this6;}return LargeDroplet;
}(Droplet);/*** Utilities Class has some functions that are needed throughout the entire application*/var Utils = function () {function Utils() {_classCallCheck(this, Utils);}_createClass(Utils, null, [{key: 'getRandomInt',/*** Returns a random integer between a given minimum and maximum value* @param {number} min - The minimum value, can be negative* @param {number} max - The maximum value, can be negative* @return {number}*/value: function getRandomInt(min, max) {return Math.floor(Math.random() * (max - min + 1)) + min;}}]);return Utils;
}();/*** Onload function is executed whenever the page is done loading, initializes the application*/window.onload = function () {// Create a new instance of the applicationvar application = new Application();
};</script></BODY>
</HTML>

玻璃上的雨滴(动态特效)相关推荐

  1. 【照片动态特效系列】旋转吧,照片!

    [照片动态特效系列]旋转吧,照片! 实现"旋转" 核心-二维仿射变换 基础旋转 分块旋转 多方向+彩图 核彩图处理心部分 主函数 其他 by 今天不飞了 突然就想做,然后就做了-- ...

  2. h5按钮css3动态特效,让页面静不下来

    h5按钮css3动态特效,让页面静不下来 在写一些h5页面的时候,难免会需要增加一些动态的效果.今天我来介绍一种,在按钮上添加动态效果的做法.保证让你瞒住,嘻嘻. 先看看效果图: 由于是动态而且颜色不 ...

  3. OpenCV图片动态特效显示(三)-- 平移显示及拉伸显示效果

    学更好的别人, 做更好的自己. --<微卡智享> 本文长度为2927字,预计阅读8分钟 前言 前两篇的特效已经实现了展开.渐显及马赛克的实现,今天来实现图像的平移效果及通过显示窗体的函数改 ...

  4. 玻璃上的编码喜悦(+ 10史诗般的Epigrams)

    by Den McHenry 丹·麦克亨利(Den McHenry) 玻璃上的编码喜悦(+ 10史诗般的Epigrams) (Perlis on Coding Joy (+ 10 Epic Epigr ...

  5. android 前后同时预览_用上这些官方动态壁纸,让你的 Android 主屏简洁又优雅

    近年来,越来越多的 Android 厂商都选择通过定制动态壁纸的方式,来为自家手机打造独特的使用体验.一些 OEM 厂商甚至能跳出常规框架.赋予其与众不同的视觉效果和交互方式,只是一张动态壁纸,就足以 ...

  6. Photoshop文字之——制作写在宣纸上的水彩字特效

    来源: 网页教学网 利用Photoshop可以简单制作好看的写在宣纸上的水彩字特效, 效果 不错吧!当然你可以改变颜色或者宣纸达到更好的效果. 先看效果: 下面我们开始讲制作步骤. 首先我们制作文字效 ...

  7. 电脑html动态桌面壁纸制作,电脑如何制作动态壁纸_电脑上怎么搞动态壁纸-win7之家...

    对于我们使用的电脑,常常会在第一时间将其设置成自己喜欢的风格,对此,许多用户都开始对系统桌面壁纸入手,然而有的用户想要将电脑中的壁纸设置成动态时却不知道如何制作了,那么电脑上怎么搞动态壁纸呢?接下来小 ...

  8. 使用GifCam工具上传GIF动态图至CSDN博客

    工具 我找了一个免费小巧的gif图制作工具, 官网下载链接:GifCam工具 博客下载地址:GifCam工具 使用方法 打开就是这样的一个界面,把你要录制的界面框起来,然后点击 Rec 就可以开始录制 ...

  9. 从计算机屏幕上抓取动态操作过程 也称为,计算机学业水平考试单项选择题综合训练一 答案复习过程...

    精品文档 计算机学业水平考试 单项选择题综合训练(一)[答案] 1.显示器最主要的性能指标是( D ). A.显示器品牌 B.显示器颜色 C.显示器大小 D.显示器分辨率 2.用"两画图&q ...

最新文章

  1. leetCode-删除排序数组中的重复项
  2. 在 k8s 中部署 Prometheus 和 Grafana
  3. express+handlebars 快速搭建网站前后台
  4. 面试中千万不要出现这些行为,很减分!
  5. MATLAB实现高斯-克吕格投影反算
  6. 基于(7,4 ) 线性分组码编码和 BPSK 调制
  7. sketch 52.2 中文破解版发布 附下载地址
  8. vr课设《梵高世界》第一人称的解谜游戏
  9. 如何选择深度学习的GPU
  10. 最新hexo+github搭建个人博客详细教程(二)——关于博客的美化
  11. swift生成二维码
  12. 【入门PCB】立创eda的学习
  13. 钉钉考勤接口调用与OA系统数据对接(多线程版)
  14. MOS 转载 ORA-1555 诊断和分析
  15. 抢抓双城发展机遇 新川代表团赴渝交流
  16. matlab中的四种取整函数的使用(fix, floor, ceil, round)
  17. Android 手机重启解决方案
  18. 三维点云数据处理软件供技术原理说明_基于三维点云处理技术的工件识别和匹配...
  19. 编写JavaScript程序实现:图像浏览器的功能
  20. 激活win7时报错,“很抱歉,程序无法在非MBR引导分区上进行激活”

热门文章

  1. WIN10/WIN7网络优化工具实现
  2. 闭式系统蒸汽管径推荐速度_几十种仪表的选型,值得收藏
  3. C语言100题练习计划 43——判断上三角矩阵
  4. 微信小程序wx:for 的使用以及wx:key绑定
  5. springboot + camel + jdbc Component 入门
  6. 总结计算机中十进制数二进制数,计算机中十进制转换为二进制的新方法
  7. 沙特SASO认证周期多少天出来,费用多少
  8. 解析SNS社区产品架构模型,互联网营销
  9. 小功率静电保护器与TVS管选型,看完醍醐灌顶
  10. C#下丢掉.asmx文件的WebService的实现