├── .babelrc
├── .env
├── .gitignore
├── LICENSE
├── README.md
├── dist
├── softripple.js
└── softripple.min.js
├── package-lock.json
├── package.json
├── src
├── Ripple.js
├── main.js
└── utils.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"]
3 | }
4 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | NAME=SoftRipple
2 | NODE_ENV=production
3 | TARGET=window
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Sikriti Dakua
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 | # Soft Ripple
2 |
3 | 
4 |
5 | A small Javascript library to create soft ripple interaction
6 |
7 | Check the demos: [here](https://codepen.io/dev_loop/full/KKVKqrq)
8 |
9 | ## Installation
10 |
11 | You can install softripple-js in your project via npm
12 |
13 | ```
14 | npm install --save softripple-js
15 | ```
16 |
17 | Or load the ES module directly
18 |
19 | ```html
20 |
21 | ```
22 |
23 | ## Usage
24 |
25 | It's pretty easy to use. Just import the package an declare SoftRipple with the element you want the ripple effect to take place.
26 |
27 | ```javascript
28 | const rippleEffect = new SoftRipple(el);
29 | ```
30 |
31 | Also you can add effect to multiple elements at once.
32 |
33 | ```javascript
34 | const el1 = document.getElementById("el1");
35 | const el2 = document.getElementById("el2");
36 | const rippleEffect = new SoftRipple([el1, el2]);
37 | ```
38 |
39 | You can also pass in options as the second argument which needs to be an object.
40 |
41 | ```javascript
42 | const rippleEffect = new SoftRipple(el, { options });
43 | ```
44 |
45 | ## Options
46 |
47 | To customize the ripple effect you can use the following properties, and then pass them as an object as the second argument of the constructor.
48 |
49 | #### rippleColor
50 |
51 | A String representing the color of the ripple. Defaults to the `background-color` of the element.
52 |
53 | #### transitionDuration
54 |
55 | Duration of the transition in seconds. Default is `0.8s`.
56 | minimum is `0.4` seconds and max is `2` seconds.
57 |
58 | #### rippleWidth
59 |
60 | Width of the ripple. Defaults to `4`.
61 | minimum is `2` and max is `8`.
62 |
63 | #### rippleMaxSize
64 |
65 | Maximum size of the ripple. Defaults to `100`.
66 | minimum is `50` and max is `200`.
67 |
68 | #### randomSize
69 |
70 | Boolean property to set random size of the ripples. Default value is `false`.
71 |
72 | #### randomColor
73 |
74 | Boolean property to set random color of the ripples. Default value is `false`.
75 |
76 | #### overrideDefaults
77 |
78 | Boolean property to override the defaults. Default value is `false`.
79 |
--------------------------------------------------------------------------------
/dist/softripple.js:
--------------------------------------------------------------------------------
1 | window["SoftRipple"] =
2 | /******/ (function(modules) { // webpackBootstrap
3 | /******/ // The module cache
4 | /******/ var installedModules = {};
5 | /******/
6 | /******/ // The require function
7 | /******/ function __webpack_require__(moduleId) {
8 | /******/
9 | /******/ // Check if module is in cache
10 | /******/ if(installedModules[moduleId]) {
11 | /******/ return installedModules[moduleId].exports;
12 | /******/ }
13 | /******/ // Create a new module (and put it into the cache)
14 | /******/ var module = installedModules[moduleId] = {
15 | /******/ i: moduleId,
16 | /******/ l: false,
17 | /******/ exports: {}
18 | /******/ };
19 | /******/
20 | /******/ // Execute the module function
21 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22 | /******/
23 | /******/ // Flag the module as loaded
24 | /******/ module.l = true;
25 | /******/
26 | /******/ // Return the exports of the module
27 | /******/ return module.exports;
28 | /******/ }
29 | /******/
30 | /******/
31 | /******/ // expose the modules object (__webpack_modules__)
32 | /******/ __webpack_require__.m = modules;
33 | /******/
34 | /******/ // expose the module cache
35 | /******/ __webpack_require__.c = installedModules;
36 | /******/
37 | /******/ // define getter function for harmony exports
38 | /******/ __webpack_require__.d = function(exports, name, getter) {
39 | /******/ if(!__webpack_require__.o(exports, name)) {
40 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
41 | /******/ }
42 | /******/ };
43 | /******/
44 | /******/ // define __esModule on exports
45 | /******/ __webpack_require__.r = function(exports) {
46 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
47 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
48 | /******/ }
49 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
50 | /******/ };
51 | /******/
52 | /******/ // create a fake namespace object
53 | /******/ // mode & 1: value is a module id, require it
54 | /******/ // mode & 2: merge all properties of value into the ns
55 | /******/ // mode & 4: return value when already ns object
56 | /******/ // mode & 8|1: behave like require
57 | /******/ __webpack_require__.t = function(value, mode) {
58 | /******/ if(mode & 1) value = __webpack_require__(value);
59 | /******/ if(mode & 8) return value;
60 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
61 | /******/ var ns = Object.create(null);
62 | /******/ __webpack_require__.r(ns);
63 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
64 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
65 | /******/ return ns;
66 | /******/ };
67 | /******/
68 | /******/ // getDefaultExport function for compatibility with non-harmony modules
69 | /******/ __webpack_require__.n = function(module) {
70 | /******/ var getter = module && module.__esModule ?
71 | /******/ function getDefault() { return module['default']; } :
72 | /******/ function getModuleExports() { return module; };
73 | /******/ __webpack_require__.d(getter, 'a', getter);
74 | /******/ return getter;
75 | /******/ };
76 | /******/
77 | /******/ // Object.prototype.hasOwnProperty.call
78 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
79 | /******/
80 | /******/ // __webpack_public_path__
81 | /******/ __webpack_require__.p = "";
82 | /******/
83 | /******/
84 | /******/ // Load entry module and return exports
85 | /******/ return __webpack_require__(__webpack_require__.s = "./src/main.js");
86 | /******/ })
87 | /************************************************************************/
88 | /******/ ({
89 |
90 | /***/ "./src/Ripple.js":
91 | /*!***********************!*\
92 | !*** ./src/Ripple.js ***!
93 | \***********************/
94 | /*! no static exports found */
95 | /***/ (function(module, exports, __webpack_require__) {
96 |
97 | "use strict";
98 |
99 |
100 | Object.defineProperty(exports, "__esModule", {
101 | value: true
102 | });
103 |
104 | 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; }; }();
105 |
106 | var _utils = __webpack_require__(/*! ./utils.js */ "./src/utils.js");
107 |
108 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
109 |
110 | var Ripple = function () {
111 | function Ripple(el) {
112 | var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
113 |
114 | _classCallCheck(this, Ripple);
115 |
116 | this.el = el;
117 | this.props = props;
118 |
119 | this.init();
120 | }
121 |
122 | _createClass(Ripple, [{
123 | key: "init",
124 | value: function init() {
125 | this.elBox = this.el.getBoundingClientRect();
126 | this.ripples = [];
127 |
128 | this.rippleSizeMin = 50;
129 | this.rippleSizeMax = 200;
130 | this.rippleSizeDefault = 100;
131 |
132 | this.transitionDurationMin = 0.4;
133 | this.transitionDurationMax = 2;
134 | this.transitionDurationDefault = 0.8;
135 |
136 | this.rippleWidthMin = 2;
137 | this.rippleWidthMax = 10;
138 | this.rippleWidthDefault = 4;
139 |
140 | this.overrideDefaults = this.props.overrideDefaults || false;
141 |
142 | if (this.overrideDefaults) this.overrideRipplePropsDefaults();else {
143 | this.rippleProps = {
144 | rippleColor: this.props.rippleColor || window.getComputedStyle(this.el).getPropertyValue("background-color"),
145 | transitionDuration: (0, _utils.clampValue)(this.props.transitionDuration, this.transitionDurationMin, this.transitionDurationMax) || this.transitionDurationDefault,
146 | rippleWidth: (0, _utils.clampValue)(this.props.rippleWidth, this.rippleWidthMin, this.rippleWidthMax) || this.rippleWidthDefault,
147 | rippleMaxSize: (0, _utils.clampValue)(this.props.rippleMaxSize, this.rippleSizeMin, this.rippleSizeMax) || this.rippleSizeDefault,
148 | randomSize: this.props.randomSize || false,
149 | randomColor: this.props.randomColor || false
150 | };
151 | }
152 |
153 | this.el.style.position = "relative";
154 | this.el.style.overflow = "hidden";
155 |
156 | this.addRippleStyles();
157 | this.addListeners();
158 | }
159 | }, {
160 | key: "addRipple",
161 | value: function addRipple(e) {
162 | var x = e.x - this.elBox.left;
163 | var y = e.y - this.elBox.top;
164 | var rippleSize = this.rippleProps.randomSize != true ? this.rippleProps.rippleMaxSize : (0, _utils.getRandomIntFromRange)(this.rippleSizeMin, this.rippleProps.rippleMaxSize);
165 | var rippleEl = document.createElement("div");
166 | rippleEl.id = "ripple";
167 | rippleEl.style.left = x + "px";
168 | rippleEl.style.top = y + "px";
169 | rippleEl.style.width = rippleSize + "px";
170 | rippleEl.style.height = rippleSize + "px";
171 | rippleEl.style.pointerEvents = "none";
172 |
173 | rippleEl.innerHTML = this.returnCompleteSVG();
174 |
175 | this.el.appendChild(rippleEl);
176 | this.ripples.push(rippleEl);
177 | this.removeRipple(rippleEl, this.rippleProps.transitionDuration * 1000);
178 | }
179 | }, {
180 | key: "returnCompleteSVG",
181 | value: function returnCompleteSVG() {
182 | var rippleWidth = this.rippleProps.randomSize != true ? this.rippleProps.rippleWidth : (0, _utils.getRandomIntFromRange)(this.rippleWidthMin, this.rippleWidthMax);
183 | var rippleColor = this.rippleProps.randomColor != true ? this.rippleProps.rippleColor : (0, _utils.getRandomHex)();
184 |
185 | return "";
186 | }
187 | }, {
188 | key: "removeRipple",
189 | value: function removeRipple(ripple, delay) {
190 | var _this = this;
191 |
192 | var t = setTimeout(function () {
193 | _this.el.removeChild(ripple);
194 | _this.ripples.splice(_this.ripples.indexOf(ripple), 1);
195 | clearTimeout(t);
196 | }, delay);
197 | }
198 | }, {
199 | key: "addRippleStyles",
200 | value: function addRippleStyles() {
201 | this.style = document.createElement("style");
202 | document.head.appendChild(this.style);
203 | this.style.sheet.insertRule("\n\t\t\t#ripple { position: absolute; transform: translate(-50%, -50%); width: 100%; height: 100%; border-radius: 50%; overflow: hidden; animation: scale-up " + this.rippleProps.transitionDuration + "s ease forwards;}\n\t\t");
204 | this.style.sheet.insertRule("\n\t\t\t#ripple svg { width: 100%; height: 100%;}\n\t\t");
205 |
206 | this.style.sheet.insertRule("\n\t\t\t@keyframes scale-up {\n\t\t\t\tfrom {\n\t\t\t\t\topacity: 1;\n\t\t\t\t\ttransform: translate(-50%, -50%) scale(0);\n\t\t\t\t}\n\t\t\t\tto {\n\t\t\t\t\topacity: 0;\n\t\t\t\t\ttransform: translate(-50%, -50%) scale(1);\n\t\t\t\t}\n\t\t\t}\t\t\n\t\t\t");
207 | }
208 | }, {
209 | key: "overrideRipplePropsDefaults",
210 | value: function overrideRipplePropsDefaults() {
211 | this.rippleProps = {
212 | rippleColor: this.props.rippleColor || window.getComputedStyle(this.el).getPropertyValue("background-color"),
213 | transitionDuration: this.props.transitionDuration || this.transitionDurationDefault,
214 | rippleWidth: this.props.rippleWidth || this.rippleWidthDefault,
215 | rippleMaxSize: this.props.rippleMaxSize || this.rippleSizeDefault,
216 | randomSize: this.props.randomSize || false,
217 | randomColor: this.props.randomColor || false
218 | };
219 | }
220 | }, {
221 | key: "addListeners",
222 | value: function addListeners() {
223 | var _this2 = this;
224 |
225 | this.el.addEventListener("pointerdown", this.addRipple.bind(this));
226 | window.addEventListener("resize", function () {
227 | _this2.elBox = _this2.el.getBoundingClientRect();
228 | });
229 | }
230 | }]);
231 |
232 | return Ripple;
233 | }();
234 |
235 | exports.default = Ripple;
236 |
237 | /***/ }),
238 |
239 | /***/ "./src/main.js":
240 | /*!*********************!*\
241 | !*** ./src/main.js ***!
242 | \*********************/
243 | /*! no static exports found */
244 | /***/ (function(module, exports, __webpack_require__) {
245 |
246 | "use strict";
247 |
248 |
249 | 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; }; }();
250 |
251 | var _Ripple = __webpack_require__(/*! ./Ripple.js */ "./src/Ripple.js");
252 |
253 | var _Ripple2 = _interopRequireDefault(_Ripple);
254 |
255 | var _utils = __webpack_require__(/*! ./utils.js */ "./src/utils.js");
256 |
257 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
258 |
259 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
260 |
261 | var SoftRipple = function () {
262 | function SoftRipple(els) {
263 | var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
264 |
265 | _classCallCheck(this, SoftRipple);
266 |
267 | this.els = (0, _utils.convertToArray)(els);
268 | this.props = props;
269 |
270 | this.init();
271 | }
272 |
273 | _createClass(SoftRipple, [{
274 | key: "init",
275 | value: function init() {
276 | for (var i = 0; i < this.els.length; i++) {
277 | var el = this.els[i];
278 | new _Ripple2.default(el, this.props);
279 | }
280 | }
281 | }]);
282 |
283 | return SoftRipple;
284 | }();
285 |
286 | module.exports = function (els, props) {
287 | return new SoftRipple(els, props);
288 | };
289 |
290 | /***/ }),
291 |
292 | /***/ "./src/utils.js":
293 | /*!**********************!*\
294 | !*** ./src/utils.js ***!
295 | \**********************/
296 | /*! no static exports found */
297 | /***/ (function(module, exports, __webpack_require__) {
298 |
299 | "use strict";
300 |
301 |
302 | Object.defineProperty(exports, "__esModule", {
303 | value: true
304 | });
305 | exports.convertToArray = convertToArray;
306 | exports.lightenColor = lightenColor;
307 |
308 | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
309 |
310 | var clampValue = exports.clampValue = function clampValue(val, min, max) {
311 | return val > max ? max : val < min ? min : val;
312 | };
313 |
314 | var getRandomIntFromRange = exports.getRandomIntFromRange = function getRandomIntFromRange(min, max) {
315 | return Math.floor(Math.random() * (max - min + 1)) + min;
316 | };
317 |
318 | var getRandomHex = exports.getRandomHex = function getRandomHex() {
319 | return "#" + Math.floor(Math.random() * 16777215).toString(16);
320 | };
321 |
322 | function convertToArray(input) {
323 | if (NodeList.prototype.isPrototypeOf(input)) return [].concat(_toConsumableArray(input));else if (Array.isArray(input)) return input;else return [input];
324 | }
325 |
326 | function lightenColor(color, percent) {
327 | var num = parseInt(color.replace("#", ""), 16),
328 | amt = Math.round(2.55 * percent),
329 | R = (num >> 16) + amt,
330 | B = (num >> 8 & 0x00ff) + amt,
331 | G = (num & 0x0000ff) + amt;
332 | return "#" + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 + (B < 255 ? B < 1 ? 0 : B : 255) * 0x100 + (G < 255 ? G < 1 ? 0 : G : 255)).toString(16).slice(1);
333 | }
334 |
335 | /***/ })
336 |
337 | /******/ });
338 | //# sourceMappingURL=softripple.js.map
--------------------------------------------------------------------------------
/dist/softripple.min.js:
--------------------------------------------------------------------------------
1 | window.SoftRipple=function(t){function e(r){if(i[r])return i[r].exports;var o=i[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var i={};return e.m=t,e.c=i,e.d=function(t,i,r){e.o(t,i)||Object.defineProperty(t,i,{enumerable:!0,get:r})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,i){if(1&i&&(t=e(t)),8&i)return t;if(4&i&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&i&&"string"!=typeof t)for(var o in t)e.d(r,o,function(e){return t[e]}.bind(null,o));return r},e.n=function(t){var i=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(i,"a",i),i},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=1)}([function(t,e){"use strict";var i=Math.floor;Object.defineProperty(e,"__esModule",{value:!0}),e.convertToArray=function(t){return NodeList.prototype.isPrototypeOf(t)?[].concat(function(t){if(Array.isArray(t)){for(var e=0,i=Array(t.length);e>16)+r,n=(255&i>>8)+r,l=(255&i)+r;return"#"+(16777216+65536*(255>o?1>o?0:o:255)+256*(255>n?1>n?0:n:255)+(255>l?1>l?0:l:255)).toString(16).slice(1)};e.clampValue=function(t,e,i){return t>i?i:t '}},{key:"removeRipple",value:function(t,e){var i=this,r=setTimeout((function(){i.el.removeChild(t),i.ripples.splice(i.ripples.indexOf(t),1),clearTimeout(r)}),e)}},{key:"addRippleStyles",value:function(){this.style=document.createElement("style"),document.head.appendChild(this.style),this.style.sheet.insertRule("\n\t\t\t#ripple { position: absolute; transform: translate(-50%, -50%); width: 100%; height: 100%; border-radius: 50%; overflow: hidden; animation: scale-up "+this.rippleProps.transitionDuration+"s ease forwards;}\n\t\t"),this.style.sheet.insertRule("\n\t\t\t#ripple svg { width: 100%; height: 100%;}\n\t\t"),this.style.sheet.insertRule("\n\t\t\t@keyframes scale-up {\n\t\t\t\tfrom {\n\t\t\t\t\topacity: 1;\n\t\t\t\t\ttransform: translate(-50%, -50%) scale(0);\n\t\t\t\t}\n\t\t\t\tto {\n\t\t\t\t\topacity: 0;\n\t\t\t\t\ttransform: translate(-50%, -50%) scale(1);\n\t\t\t\t}\n\t\t\t}\t\t\n\t\t\t")}},{key:"overrideRipplePropsDefaults",value:function(){this.rippleProps={rippleColor:this.props.rippleColor||window.getComputedStyle(this.el).getPropertyValue("background-color"),transitionDuration:this.props.transitionDuration||this.transitionDurationDefault,rippleWidth:this.props.rippleWidth||this.rippleWidthDefault,rippleMaxSize:this.props.rippleMaxSize||this.rippleSizeDefault,randomSize:this.props.randomSize||!1,randomColor:this.props.randomColor||!1}}},{key:"addListeners",value:function(){var t=this;this.el.addEventListener("pointerdown",this.addRipple.bind(this)),window.addEventListener("resize",(function(){t.elBox=t.el.getBoundingClientRect()}))}}]),t}();e.default=l}]);
2 | //# sourceMappingURL=softripple.min.js.map
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "softripple-js",
3 | "version": "1.2.1",
4 | "description": "A small library to for creating soft ripples",
5 | "main": "src/main.js",
6 | "scripts": {
7 | "build:dev": "webpack && NODE_ENV=development webpack",
8 | "build:prod": "webpack && NODE_ENV=production webpack"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/devloop01/soft-ripple-js.git"
13 | },
14 | "keywords": [
15 | "ripples",
16 | "library",
17 | "interaction"
18 | ],
19 | "author": "Sikriti Dakua",
20 | "license": "MIT",
21 | "bugs": {
22 | "url": "https://github.com/devloop01/soft-ripple-js/issues"
23 | },
24 | "homepage": "https://github.com/devloop01/soft-ripple-js#readme",
25 | "devDependencies": {
26 | "babel-core": "^6.26.3",
27 | "babel-loader": "^7.1.5",
28 | "babel-minify-webpack-plugin": "^0.3.1",
29 | "babel-preset-es2015": "^6.24.1",
30 | "dotenv": "^8.2.0",
31 | "webpack": "^4.43.0",
32 | "webpack-cli": "^3.3.11",
33 | "webpack-dev-server": "^3.11.0"
34 | },
35 | "dependencies": {}
36 | }
37 |
--------------------------------------------------------------------------------
/src/Ripple.js:
--------------------------------------------------------------------------------
1 | import { clampValue, lightenColor, getRandomIntFromRange, getRandomHex } from "./utils.js";
2 |
3 | class Ripple {
4 | constructor(el, props = {}) {
5 | this.el = el;
6 | this.props = props;
7 |
8 | this.init();
9 | }
10 |
11 | init() {
12 | this.elBox = this.el.getBoundingClientRect();
13 | this.ripples = [];
14 |
15 | this.rippleSizeMin = 50;
16 | this.rippleSizeMax = 200;
17 | this.rippleSizeDefault = 100;
18 |
19 | this.transitionDurationMin = 0.4;
20 | this.transitionDurationMax = 2;
21 | this.transitionDurationDefault = 0.8;
22 |
23 | this.rippleWidthMin = 2;
24 | this.rippleWidthMax = 10;
25 | this.rippleWidthDefault = 4;
26 |
27 | this.overrideDefaults = this.props.overrideDefaults || false;
28 |
29 | if (this.overrideDefaults) this.overrideRipplePropsDefaults();
30 | else {
31 | this.rippleProps = {
32 | rippleColor:
33 | this.props.rippleColor ||
34 | window.getComputedStyle(this.el).getPropertyValue("background-color"),
35 | transitionDuration:
36 | clampValue(
37 | this.props.transitionDuration,
38 | this.transitionDurationMin,
39 | this.transitionDurationMax
40 | ) || this.transitionDurationDefault,
41 | rippleWidth:
42 | clampValue(this.props.rippleWidth, this.rippleWidthMin, this.rippleWidthMax) ||
43 | this.rippleWidthDefault,
44 | rippleMaxSize:
45 | clampValue(this.props.rippleMaxSize, this.rippleSizeMin, this.rippleSizeMax) ||
46 | this.rippleSizeDefault,
47 | randomSize: this.props.randomSize || false,
48 | randomColor: this.props.randomColor || false,
49 | };
50 | }
51 |
52 | this.el.style.position = "relative";
53 | this.el.style.overflow = "hidden";
54 |
55 | this.addRippleStyles();
56 | this.addListeners();
57 | }
58 |
59 | addRipple(e) {
60 | const x = e.x - this.elBox.left;
61 | const y = e.y - this.elBox.top;
62 | const rippleSize =
63 | this.rippleProps.randomSize != true
64 | ? this.rippleProps.rippleMaxSize
65 | : getRandomIntFromRange(this.rippleSizeMin, this.rippleProps.rippleMaxSize);
66 | const rippleEl = document.createElement("div");
67 | rippleEl.id = "ripple";
68 | rippleEl.style.left = `${x}px`;
69 | rippleEl.style.top = `${y}px`;
70 | rippleEl.style.width = `${rippleSize}px`;
71 | rippleEl.style.height = `${rippleSize}px`;
72 | rippleEl.style.pointerEvents = "none";
73 |
74 | rippleEl.innerHTML = this.returnCompleteSVG();
75 |
76 | this.el.appendChild(rippleEl);
77 | this.ripples.push(rippleEl);
78 | this.removeRipple(rippleEl, this.rippleProps.transitionDuration * 1000);
79 | }
80 |
81 | returnCompleteSVG() {
82 | const rippleWidth =
83 | this.rippleProps.randomSize != true
84 | ? this.rippleProps.rippleWidth
85 | : getRandomIntFromRange(this.rippleWidthMin, this.rippleWidthMax);
86 | const rippleColor =
87 | this.rippleProps.randomColor != true ? this.rippleProps.rippleColor : getRandomHex();
88 |
89 | return ``;
102 | }
103 |
104 | removeRipple(ripple, delay) {
105 | let t = setTimeout(() => {
106 | this.el.removeChild(ripple);
107 | this.ripples.splice(this.ripples.indexOf(ripple), 1);
108 | clearTimeout(t);
109 | }, delay);
110 | }
111 |
112 | addRippleStyles() {
113 | this.style = document.createElement("style");
114 | document.head.appendChild(this.style);
115 | this.style.sheet.insertRule(`
116 | #ripple { position: absolute; transform: translate(-50%, -50%); width: 100%; height: 100%; border-radius: 50%; overflow: hidden; animation: scale-up ${this.rippleProps.transitionDuration}s ease forwards;}
117 | `);
118 | this.style.sheet.insertRule(`
119 | #ripple svg { width: 100%; height: 100%;}
120 | `);
121 |
122 | this.style.sheet.insertRule(
123 | `
124 | @keyframes scale-up {
125 | from {
126 | opacity: 1;
127 | transform: translate(-50%, -50%) scale(0);
128 | }
129 | to {
130 | opacity: 0;
131 | transform: translate(-50%, -50%) scale(1);
132 | }
133 | }
134 | `
135 | );
136 | }
137 |
138 | overrideRipplePropsDefaults() {
139 | this.rippleProps = {
140 | rippleColor:
141 | this.props.rippleColor ||
142 | window.getComputedStyle(this.el).getPropertyValue("background-color"),
143 | transitionDuration: this.props.transitionDuration || this.transitionDurationDefault,
144 | rippleWidth: this.props.rippleWidth || this.rippleWidthDefault,
145 | rippleMaxSize: this.props.rippleMaxSize || this.rippleSizeDefault,
146 | randomSize: this.props.randomSize || false,
147 | randomColor: this.props.randomColor || false,
148 | };
149 | }
150 |
151 | addListeners() {
152 | this.el.addEventListener("pointerdown", this.addRipple.bind(this));
153 | window.addEventListener("resize", () => {
154 | this.elBox = this.el.getBoundingClientRect();
155 | });
156 | }
157 | }
158 |
159 | export default Ripple;
160 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import Ripple from "./Ripple.js";
4 | import { convertToArray } from "./utils.js";
5 |
6 | class SoftRipple {
7 | constructor(els, props = {}) {
8 | this.els = convertToArray(els);
9 | this.props = props;
10 |
11 | this.init();
12 | }
13 |
14 | init() {
15 | for (let i = 0; i < this.els.length; i++) {
16 | const el = this.els[i];
17 | new Ripple(el, this.props);
18 | }
19 | }
20 | }
21 |
22 | module.exports = (els, props) => {
23 | return new SoftRipple(els, props);
24 | };
25 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export const clampValue = (val, min, max) => (val > max ? max : val < min ? min : val);
2 |
3 | export const getRandomIntFromRange = (min, max) =>
4 | Math.floor(Math.random() * (max - min + 1)) + min;
5 |
6 | export const getRandomHex = () => `#${Math.floor(Math.random() * 16777215).toString(16)}`;
7 |
8 | export function convertToArray(input) {
9 | if (NodeList.prototype.isPrototypeOf(input)) return [...input];
10 | else if (Array.isArray(input)) return input;
11 | else return [input];
12 | }
13 |
14 | export function lightenColor(color, percent) {
15 | var num = parseInt(color.replace("#", ""), 16),
16 | amt = Math.round(2.55 * percent),
17 | R = (num >> 16) + amt,
18 | B = ((num >> 8) & 0x00ff) + amt,
19 | G = (num & 0x0000ff) + amt;
20 | return (
21 | "#" +
22 | (
23 | 0x1000000 +
24 | (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
25 | (B < 255 ? (B < 1 ? 0 : B) : 255) * 0x100 +
26 | (G < 255 ? (G < 1 ? 0 : G) : 255)
27 | )
28 | .toString(16)
29 | .slice(1)
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const config = require("./package.json");
3 |
4 | require("dotenv").config();
5 |
6 | const MinifyPlugin = require("babel-minify-webpack-plugin");
7 |
8 | const PROD = process.env.NODE_ENV === "production";
9 |
10 | let plugins = [];
11 |
12 | PROD ? [plugins.push(new MinifyPlugin())] : "";
13 |
14 | module.exports = {
15 | entry: path.resolve(__dirname, config.main),
16 | devtool: "source-map",
17 | output: {
18 | library: process.env.NAME,
19 | libraryTarget: process.env.TARGET,
20 | path: __dirname,
21 | filename: PROD ? "dist/softripple.min.js" : "dist/softripple.js",
22 | },
23 | module: {
24 | rules: [
25 | {
26 | test: /\.(js)$/,
27 | exclude: /node_modules/,
28 | use: ["babel-loader"],
29 | },
30 | ],
31 | },
32 | plugins: plugins,
33 | mode: process.env.NODE_ENV,
34 | };
35 |
--------------------------------------------------------------------------------