├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── css └── main.css ├── index.html ├── js └── main.js └── screenshot.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: victorqribeiro 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Victor Ribeiro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ripple Effect 2 | 3 | A JavaScript ripple effect implemented using canvas 4 | 5 | ![screenshot](screenshot.png) 6 | 7 | Live version [here](https://victorribeiro.com/rippleEffect) 8 | 9 | ## Algorithm 10 | 11 | [https://web.archive.org/web/20160418004149/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm](https://web.archive.org/web/20160418004149/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm) 12 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | height: 100%; 5 | background: url('https://images.unsplash.com/photo-1516528387618-afa90b13e000?ixlib=rb-1.2.1&w=1366&q=90') center bottom; 6 | background-size: cover; 7 | } 8 | 9 | canvas { 10 | display: block; 11 | mix-blend-mode: screen; 12 | } 13 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Waves 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | const s = 1; /* size - the bigger the faster (lower quality) */ 2 | const canvas = document.getElementById("canvas"); 3 | canvas.width = w = Math.floor(innerWidth/s); 4 | canvas.height = h = Math.floor(innerHeight/s); 5 | canvas.style.width = '100%'; 6 | canvas.style.height = '100%'; 7 | const c = canvas.getContext("2d"); 8 | 9 | let buffer1 = Array(w).fill().map(_=>Array(h).fill(0)); 10 | let buffer2 = Array(w).fill().map(_=>Array(h).fill(0)); 11 | 12 | const damping = 0.999; 13 | let temp; 14 | 15 | function animation(){ 16 | 17 | for(let i = 1; i < w-1; i++){ 18 | for(let j = 1; j < h-1; j++){ 19 | buffer2[i][j] = ((buffer1[i-1][j] + 20 | buffer1[i+1][j] + 21 | buffer1[i][j-1] + 22 | buffer1[i][j+1]) / 2 - buffer2[i][j]) * damping; 23 | } 24 | } 25 | 26 | let img = new ImageData(w, h) 27 | 28 | for(let i = 0; i < buffer1.length; i++){ 29 | for(let j = 0; j < buffer1[0].length; j++){ 30 | let index = (j * buffer1.length + i) * 4 31 | img.data[index] = buffer2[i][j] 32 | img.data[index+1] = buffer2[i][j] 33 | img.data[index+2] = buffer2[i][j] 34 | img.data[index+3] = 255 35 | } 36 | } 37 | 38 | c.putImageData(img,0,0) 39 | 40 | temp = buffer2; 41 | buffer2 = buffer1; 42 | buffer1 = temp; 43 | requestAnimationFrame(animation); 44 | } 45 | 46 | animation(); 47 | 48 | function ripple(e){ 49 | let x = Math.floor(e.clientX/s); 50 | let y = Math.floor(e.clientY/s); 51 | buffer1[x][y] = 500; 52 | } 53 | 54 | document.addEventListener("click", ripple ); 55 | 56 | document.addEventListener("mousemove", ripple ); 57 | 58 | window.addEventListener("resize",function(){ 59 | location.reload(); 60 | }); 61 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorqribeiro/rippleEffect/29cd69acd6c275e0f5af2ace2c67bf139e9ea53b/screenshot.png --------------------------------------------------------------------------------