├── .babelrc
├── .gitignore
├── .jshintrc
├── LICENSE
├── README.md
├── demo
└── index.html
├── dist
└── vue-waves.js
├── index.js
├── package-lock.json
├── package.json
├── waves.css
├── waves.js
└── webpack.config.babel.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "undef": true,
3 | "unused": true,
4 | "esversion": 6,
5 | "asi": true,
6 | "browser": true,
7 | "node": true,
8 | "devel": true,
9 | "predef": ["Vue", "VueWaves"]
10 | }
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License (MIT)
2 |
3 | Copyright (c) 2016 TeddyZhu
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 | # vue-waves
2 |
3 | A Vue.js version of waves based on https://github.com/fians/Waves.
4 |
5 | ## How to Use
6 |
7 | ### SetUp
8 |
9 | ```bash
10 | npm i -S vue-waves
11 | ```
12 |
13 | then in the js file
14 |
15 | ```javascript
16 | import Vue from 'vue';
17 | import VueWaves from 'vue-waves';
18 |
19 | Vue.use(VueWaves);
20 | ```
21 |
22 | ### Config
23 |
24 | ```javascript
25 | Vue.use(VueWaves, {
26 | name: 'waves' // Vue指令名称
27 | duration: 500, // 涟漪效果持续时间
28 | delay: 200 // 延时显示涟漪效果
29 | })
30 | ```
31 |
32 | ### Usage
33 |
34 | ```html
35 |
36 |
37 | ```
38 |
39 | Vue directive:
40 | ```
41 | v-waves.button 按钮
42 | v-waves.circle 圆形
43 | v-waves.block 块
44 | v-waves.float 阴影效果
45 | v-waves.light 亮色涟漪
46 | v-waves.classic ??
47 | ```
48 |
49 | Demo:
50 |
51 | git clone https://github.com/Teddy-Zhu/vue-waves.git
52 | open vue-waves/demo/index.html
53 |
54 | # Contributes
55 |
56 | ```
57 | $ npm i
58 | $ npm run build
59 | ```
60 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vue-waves
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{ message }}
15 |
16 |
19 |
22 |
25 |
28 |
31 |
32 |
33 |
34 |
35 |
60 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/dist/vue-waves.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.VueWaves=e():t.VueWaves=e()}(window,function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:r})},n.r=function(t){Object.defineProperty(t,"__esModule",{value:!0})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=7)}([function(t,e){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(t){"object"==typeof window&&(n=window)}t.exports=n},function(t,e,n){"use strict";(function(n){var r,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};!function(n,o){void 0===(r=function(){return o.apply(n)}.apply(e,[]))||(t.exports=r)}("object"===(void 0===n?"undefined":o(n))?n:void 0,function(){var t=t||{},e=document.querySelectorAll.bind(document),n=Object.prototype.toString,r="ontouchstart"in window;function a(t){var e=void 0===t?"undefined":o(t);return"function"===e||"object"===e&&!!t}function i(t){var r,o=n.call(t);return"[object String]"===o?e(t):a(t)&&/^\[object (Array|HTMLCollection|NodeList|Object)\]$/.test(o)&&t.hasOwnProperty("length")?t:a(r=t)&&r.nodeType>0?[t]:[]}function s(t){var e,n,r={top:0,left:0},a=t&&t.ownerDocument;return e=a.documentElement,"undefined"!==o(t.getBoundingClientRect)&&(r=t.getBoundingClientRect()),n=function(t){return null!==(e=t)&&e===e.window?t:9===t.nodeType&&t.defaultView;var e}(a),{top:r.top+n.pageYOffset-e.clientTop,left:r.left+n.pageXOffset-e.clientLeft}}function u(t){var e="";for(var n in t)t.hasOwnProperty(n)&&(e+=n+":"+t[n]+";");return e}var l={duration:750,delay:200,show:function(t,e,n){if(2===t.button)return!1;e=e||this;var r=document.createElement("div");r.className="waves-ripple waves-rippling",e.appendChild(r);var o=s(e),a=0,i=0;"touches"in t&&t.touches.length?(a=t.touches[0].pageY-o.top,i=t.touches[0].pageX-o.left):(a=t.pageY-o.top,i=t.pageX-o.left),i=i>=0?i:0,a=a>=0?a:0;var c="scale("+e.clientWidth/100*3+")",f="translate(0,0)";n&&(f="translate("+n.x+"px, "+n.y+"px)"),r.setAttribute("data-hold",Date.now()),r.setAttribute("data-x",i),r.setAttribute("data-y",a),r.setAttribute("data-scale",c),r.setAttribute("data-translate",f);var d={top:a+"px",left:i+"px"};r.classList.add("waves-notransition"),r.setAttribute("style",u(d)),r.classList.remove("waves-notransition"),d["-webkit-transform"]=c+" "+f,d["-moz-transform"]=c+" "+f,d["-ms-transform"]=c+" "+f,d["-o-transform"]=c+" "+f,d.transform=c+" "+f,d.opacity="1";var p="mousemove"===t.type?2500:l.duration;d["-webkit-transition-duration"]=p+"ms",d["-moz-transition-duration"]=p+"ms",d["-o-transition-duration"]=p+"ms",d["transition-duration"]=p+"ms",r.setAttribute("style",u(d))},hide:function(t,e){for(var n=(e=e||this).getElementsByClassName("waves-rippling"),o=0,a=n.length;o=0&&null!==e.wait){setTimeout(d({type:"mouseup",button:1},r),e.wait)}},t.calm=function(t){for(var e={type:"mouseup",button:1},n=0,r=(t=i(t)).length;n=0&&c.splice(e,1)}function h(t){var e=document.createElement("style");return t.attrs.type="text/css",b(e,t.attrs),v(t,e),e}function b(t,e){Object.keys(e).forEach(function(n){t.setAttribute(n,e[n])})}function g(t,e){var n,r,o,a;if(e.transform&&t.css){if(!(a=e.transform(t.css)))return function(){};t.css=a}if(e.singleton){var i=l++;n=u||(u=h(e)),r=x.bind(null,n,i,!1),o=x.bind(null,n,i,!0)}else t.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(t){var e=document.createElement("link");return t.attrs.type="text/css",t.attrs.rel="stylesheet",b(e,t.attrs),v(t,e),e}(e),r=function(t,e,n){var r=n.css,o=n.sourceMap,a=void 0===e.convertToAbsoluteUrls&&o;(e.convertToAbsoluteUrls||a)&&(r=f(r));o&&(r+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */");var i=new Blob([r],{type:"text/css"}),s=t.href;t.href=URL.createObjectURL(i),s&&URL.revokeObjectURL(s)}.bind(null,n,e),o=function(){m(n),n.href&&URL.revokeObjectURL(n.href)}):(n=h(e),r=function(t,e){var n=e.css,r=e.media;r&&t.setAttribute("media",r);if(t.styleSheet)t.styleSheet.cssText=n;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(n))}}.bind(null,n),o=function(){m(n)});return r(t),function(e){if(e){if(e.css===t.css&&e.media===t.media&&e.sourceMap===t.sourceMap)return;r(t=e)}else o()}}t.exports=function(t,e){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(e=e||{}).attrs="object"==typeof e.attrs?e.attrs:{},e.singleton||"boolean"==typeof e.singleton||(e.singleton=i()),e.insertInto||(e.insertInto="head"),e.insertAt||(e.insertAt="bottom");var n=p(t,e);return d(n,e),function(t){for(var r=[],o=0;o1&&void 0!==arguments[1]?arguments[1]:{name:"waves"};t.directive(e.name,{inserted:function(t,e){var n=["button","circle","block","float","light","classic"].filter(function(t){return e.modifiers[t]}).map(function(t){return"waves-"+t});a.default.attach(t,n)}}),t.mixin({created:function(){a.default.init(e)}})}}}])});
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import './waves.css'
2 | import Waves from './waves.js'
3 |
4 | export default {
5 | install(Vue, options = { name: 'waves' }) {
6 | Vue.directive(options.name, {
7 | inserted(el, binding) {
8 | let classes = ['button', 'circle', 'block', 'float', 'light', 'classic']
9 | .filter(cls => binding.modifiers[cls])
10 | .map(cls => `waves-${cls}`)
11 | Waves.attach(el, classes)
12 | }
13 | })
14 | Vue.mixin({
15 | created: function() {
16 | Waves.init(options);
17 | }
18 | })
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-waves",
3 | "homepage": "https://github.com/Teddy-Zhu/vue-waves",
4 | "version": "0.1.1",
5 | "description": "A Vue.js version of waves",
6 | "author": "teddyzhu",
7 | "scripts": {
8 | "build": "./node_modules/webpack/bin/webpack.js -p"
9 | },
10 | "dependencies": {},
11 | "readme": "README.md",
12 | "main": "index.js",
13 | "repository": {
14 | "type": "git",
15 | "url": "git@github.com:Teddy-Zhu/vue-waves.git"
16 | },
17 | "license": "MIT",
18 | "devDependencies": {
19 | "babel-core": "^6.17.0",
20 | "babel-loader": "^7.1.4",
21 | "babel-preset-es2015": "^6.16.0",
22 | "css-loader": "^0.28.11",
23 | "style-loader": "^0.20.3",
24 | "vue": "^2.5.16",
25 | "webpack": "^4.4.1"
26 | },
27 | "files": [
28 | "dist",
29 | "waves.css",
30 | "waves.js"
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/waves.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Waves v0.7.5
3 | * http://fian.my.id/Waves
4 | *
5 | * Copyright 2014-2016 Alfiana E. Sibuea and other contributors
6 | * Released under the MIT license
7 | * https://github.com/fians/Waves/blob/master/LICENSE
8 | */
9 | .waves-effect {
10 | position: relative;
11 | cursor: pointer;
12 | display: inline-block;
13 | overflow: hidden;
14 | -webkit-user-select: none;
15 | -moz-user-select: none;
16 | -ms-user-select: none;
17 | user-select: none;
18 | -webkit-tap-highlight-color: transparent;
19 | }
20 | .waves-effect .waves-ripple {
21 | position: absolute;
22 | border-radius: 50%;
23 | width: 100px;
24 | height: 100px;
25 | margin-top: -50px;
26 | margin-left: -50px;
27 | opacity: 0;
28 | background: rgba(0, 0, 0, 0.2);
29 | background: -webkit-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
30 | background: -o-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
31 | background: -moz-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
32 | background: radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
33 | -webkit-transition: all 0.5s ease-out;
34 | -moz-transition: all 0.5s ease-out;
35 | -o-transition: all 0.5s ease-out;
36 | transition: all 0.5s ease-out;
37 | -webkit-transition-property: -webkit-transform, opacity;
38 | -moz-transition-property: -moz-transform, opacity;
39 | -o-transition-property: -o-transform, opacity;
40 | transition-property: transform, opacity;
41 | -webkit-transform: scale(0) translate(0, 0);
42 | -moz-transform: scale(0) translate(0, 0);
43 | -ms-transform: scale(0) translate(0, 0);
44 | -o-transform: scale(0) translate(0, 0);
45 | transform: scale(0) translate(0, 0);
46 | pointer-events: none;
47 | }
48 | .waves-effect.waves-light .waves-ripple {
49 | background: rgba(255, 255, 255, 0.4);
50 | background: -webkit-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
51 | background: -o-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
52 | background: -moz-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
53 | background: radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
54 | }
55 | .waves-effect.waves-classic .waves-ripple {
56 | background: rgba(0, 0, 0, 0.2);
57 | }
58 | .waves-effect.waves-classic.waves-light .waves-ripple {
59 | background: rgba(255, 255, 255, 0.4);
60 | }
61 | .waves-notransition {
62 | -webkit-transition: none !important;
63 | -moz-transition: none !important;
64 | -o-transition: none !important;
65 | transition: none !important;
66 | }
67 | .waves-button,
68 | .waves-circle {
69 | -webkit-transform: translateZ(0);
70 | -moz-transform: translateZ(0);
71 | -ms-transform: translateZ(0);
72 | -o-transform: translateZ(0);
73 | transform: translateZ(0);
74 | -webkit-mask-image: -webkit-radial-gradient(circle, #ffffff 100%, #000000 100%);
75 | }
76 | .waves-button,
77 | .waves-button:hover,
78 | .waves-button:visited,
79 | .waves-button-input {
80 | white-space: nowrap;
81 | vertical-align: middle;
82 | cursor: pointer;
83 | border: none;
84 | outline: none;
85 | color: inherit;
86 | background-color: rgba(0, 0, 0, 0);
87 | font-size: 1em;
88 | line-height: 1em;
89 | text-align: center;
90 | text-decoration: none;
91 | z-index: 1;
92 | }
93 | .waves-button {
94 | padding: 0.85em 1.1em;
95 | border-radius: 0.2em;
96 | }
97 | .waves-button-input {
98 | margin: 0;
99 | padding: 0.85em 1.1em;
100 | }
101 | .waves-input-wrapper {
102 | border-radius: 0.2em;
103 | vertical-align: bottom;
104 | }
105 | .waves-input-wrapper.waves-button {
106 | padding: 0;
107 | }
108 | .waves-input-wrapper .waves-button-input {
109 | position: relative;
110 | top: 0;
111 | left: 0;
112 | z-index: 1;
113 | }
114 | .waves-circle {
115 | text-align: center;
116 | width: 2.5em;
117 | height: 2.5em;
118 | line-height: 2.5em;
119 | border-radius: 50%;
120 | }
121 | .waves-float {
122 | -webkit-mask-image: none;
123 | -webkit-box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12);
124 | box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12);
125 | -webkit-transition: all 300ms;
126 | -moz-transition: all 300ms;
127 | -o-transition: all 300ms;
128 | transition: all 300ms;
129 | }
130 | .waves-float:active {
131 | -webkit-box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3);
132 | box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3);
133 | }
134 | .waves-block {
135 | display: block;
136 | }
137 |
--------------------------------------------------------------------------------
/waves.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Waves v0.7.5
3 | * http://fian.my.id/Waves
4 | *
5 | * Copyright 2014-2016 Alfiana E. Sibuea and other contributors
6 | * Released under the MIT license
7 | * https://github.com/fians/Waves/blob/master/LICENSE
8 | */
9 |
10 | ;(function(window, factory) {
11 | 'use strict';
12 |
13 | // AMD. Register as an anonymous module. Wrap in function so we have access
14 | // to root via `this`.
15 | if (typeof define === 'function' && define.amd) {
16 | define([], function() {
17 | return factory.apply(window);
18 | });
19 | }
20 |
21 | // Node. Does not work with strict CommonJS, but only CommonJS-like
22 | // environments that support module.exports, like Node.
23 | else if (typeof exports === 'object') {
24 | module.exports = factory.call(window);
25 | }
26 |
27 | // Browser globals.
28 | else {
29 | window.Waves = factory.call(window);
30 | }
31 | })(typeof global === 'object' ? global : this, function() {
32 | 'use strict';
33 |
34 | var Waves = Waves || {};
35 | var $$ = document.querySelectorAll.bind(document);
36 | var toString = Object.prototype.toString;
37 | var isTouchAvailable = 'ontouchstart' in window;
38 |
39 |
40 | // Find exact position of element
41 | function isWindow(obj) {
42 | return obj !== null && obj === obj.window;
43 | }
44 |
45 | function getWindow(elem) {
46 | return isWindow(elem) ? elem : elem.nodeType === 9 && elem.defaultView;
47 | }
48 |
49 | function isObject(value) {
50 | var type = typeof value;
51 | return type === 'function' || type === 'object' && !!value;
52 | }
53 |
54 | function isDOMNode(obj) {
55 | return isObject(obj) && obj.nodeType > 0;
56 | }
57 |
58 | function getWavesElements(nodes) {
59 | var stringRepr = toString.call(nodes);
60 |
61 | if (stringRepr === '[object String]') {
62 | return $$(nodes);
63 | } else if (isObject(nodes) && /^\[object (Array|HTMLCollection|NodeList|Object)\]$/.test(stringRepr) && nodes.hasOwnProperty('length')) {
64 | return nodes;
65 | } else if (isDOMNode(nodes)) {
66 | return [nodes];
67 | }
68 |
69 | return [];
70 | }
71 |
72 | function offset(elem) {
73 | var docElem, win,
74 | box = { top: 0, left: 0 },
75 | doc = elem && elem.ownerDocument;
76 |
77 | docElem = doc.documentElement;
78 |
79 | if (typeof elem.getBoundingClientRect !== typeof undefined) {
80 | box = elem.getBoundingClientRect();
81 | }
82 | win = getWindow(doc);
83 | return {
84 | top: box.top + win.pageYOffset - docElem.clientTop,
85 | left: box.left + win.pageXOffset - docElem.clientLeft
86 | };
87 | }
88 |
89 | function convertStyle(styleObj) {
90 | var style = '';
91 |
92 | for (var prop in styleObj) {
93 | if (styleObj.hasOwnProperty(prop)) {
94 | style += (prop + ':' + styleObj[prop] + ';');
95 | }
96 | }
97 |
98 | return style;
99 | }
100 |
101 | var Effect = {
102 |
103 | // Effect duration
104 | duration: 750,
105 |
106 | // Effect delay (check for scroll before showing effect)
107 | delay: 200,
108 |
109 | show: function(e, element, velocity) {
110 |
111 | // Disable right click
112 | if (e.button === 2) {
113 | return false;
114 | }
115 |
116 | element = element || this;
117 |
118 | // Create ripple
119 | var ripple = document.createElement('div');
120 | ripple.className = 'waves-ripple waves-rippling';
121 | element.appendChild(ripple);
122 |
123 | // Get click coordinate and element width
124 | var pos = offset(element);
125 | var relativeY = 0;
126 | var relativeX = 0;
127 | // Support for touch devices
128 | if('touches' in e && e.touches.length) {
129 | relativeY = (e.touches[0].pageY - pos.top);
130 | relativeX = (e.touches[0].pageX - pos.left);
131 | }
132 | //Normal case
133 | else {
134 | relativeY = (e.pageY - pos.top);
135 | relativeX = (e.pageX - pos.left);
136 | }
137 | // Support for synthetic events
138 | relativeX = relativeX >= 0 ? relativeX : 0;
139 | relativeY = relativeY >= 0 ? relativeY : 0;
140 |
141 | var scale = 'scale(' + ((element.clientWidth / 100) * 3) + ')';
142 | var translate = 'translate(0,0)';
143 |
144 | if (velocity) {
145 | translate = 'translate(' + (velocity.x) + 'px, ' + (velocity.y) + 'px)';
146 | }
147 |
148 | // Attach data to element
149 | ripple.setAttribute('data-hold', Date.now());
150 | ripple.setAttribute('data-x', relativeX);
151 | ripple.setAttribute('data-y', relativeY);
152 | ripple.setAttribute('data-scale', scale);
153 | ripple.setAttribute('data-translate', translate);
154 |
155 | // Set ripple position
156 | var rippleStyle = {
157 | top: relativeY + 'px',
158 | left: relativeX + 'px'
159 | };
160 |
161 | ripple.classList.add('waves-notransition');
162 | ripple.setAttribute('style', convertStyle(rippleStyle));
163 | ripple.classList.remove('waves-notransition');
164 |
165 | // Scale the ripple
166 | rippleStyle['-webkit-transform'] = scale + ' ' + translate;
167 | rippleStyle['-moz-transform'] = scale + ' ' + translate;
168 | rippleStyle['-ms-transform'] = scale + ' ' + translate;
169 | rippleStyle['-o-transform'] = scale + ' ' + translate;
170 | rippleStyle.transform = scale + ' ' + translate;
171 | rippleStyle.opacity = '1';
172 |
173 | var duration = e.type === 'mousemove' ? 2500 : Effect.duration;
174 | rippleStyle['-webkit-transition-duration'] = duration + 'ms';
175 | rippleStyle['-moz-transition-duration'] = duration + 'ms';
176 | rippleStyle['-o-transition-duration'] = duration + 'ms';
177 | rippleStyle['transition-duration'] = duration + 'ms';
178 |
179 | ripple.setAttribute('style', convertStyle(rippleStyle));
180 | },
181 |
182 | hide: function(e, element) {
183 | element = element || this;
184 |
185 | var ripples = element.getElementsByClassName('waves-rippling');
186 |
187 | for (var i = 0, len = ripples.length; i < len; i++) {
188 | removeRipple(e, element, ripples[i]);
189 | }
190 |
191 | if (isTouchAvailable) {
192 | element.removeEventListener('touchend', Effect.hide);
193 | element.removeEventListener('touchcancel', Effect.hide);
194 | }
195 |
196 | element.removeEventListener('mouseup', Effect.hide);
197 | element.removeEventListener('mouseleave', Effect.hide);
198 | }
199 | };
200 |
201 | /**
202 | * Collection of wrapper for HTML element that only have single tag
203 | * like and
204 | */
205 | var TagWrapper = {
206 |
207 | // Wrap tag so it can perform the effect
208 | input: function(element) {
209 |
210 | var parent = element.parentNode;
211 |
212 | // If input already have parent just pass through
213 | if (parent.tagName.toLowerCase() === 'i' && parent.classList.contains('waves-effect')) {
214 | return;
215 | }
216 |
217 | // Put element class and style to the specified parent
218 | var wrapper = document.createElement('i');
219 | wrapper.className = element.className + ' waves-input-wrapper';
220 | element.className = 'waves-button-input';
221 |
222 | // Put element as child
223 | parent.replaceChild(wrapper, element);
224 | wrapper.appendChild(element);
225 |
226 | // Apply element color and background color to wrapper
227 | var elementStyle = window.getComputedStyle(element, null);
228 | var color = elementStyle.color;
229 | var backgroundColor = elementStyle.backgroundColor;
230 |
231 | wrapper.setAttribute('style', 'color:' + color + ';background:' + backgroundColor);
232 | element.setAttribute('style', 'background-color:rgba(0,0,0,0);');
233 |
234 | },
235 |
236 | // Wrap
tag so it can perform the effect
237 | img: function(element) {
238 |
239 | var parent = element.parentNode;
240 |
241 | // If input already have parent just pass through
242 | if (parent.tagName.toLowerCase() === 'i' && parent.classList.contains('waves-effect')) {
243 | return;
244 | }
245 |
246 | // Put element as child
247 | var wrapper = document.createElement('i');
248 | parent.replaceChild(wrapper, element);
249 | wrapper.appendChild(element);
250 |
251 | }
252 | };
253 |
254 | /**
255 | * Hide the effect and remove the ripple. Must be
256 | * a separate function to pass the JSLint...
257 | */
258 | function removeRipple(e, el, ripple) {
259 |
260 | // Check if the ripple still exist
261 | if (!ripple) {
262 | return;
263 | }
264 |
265 | ripple.classList.remove('waves-rippling');
266 |
267 | var relativeX = ripple.getAttribute('data-x');
268 | var relativeY = ripple.getAttribute('data-y');
269 | var scale = ripple.getAttribute('data-scale');
270 | var translate = ripple.getAttribute('data-translate');
271 |
272 | // Get delay beetween mousedown and mouse leave
273 | var diff = Date.now() - Number(ripple.getAttribute('data-hold'));
274 | var delay = 350 - diff;
275 |
276 | if (delay < 0) {
277 | delay = 0;
278 | }
279 |
280 | if (e.type === 'mousemove') {
281 | delay = 150;
282 | }
283 |
284 | // Fade out ripple after delay
285 | var duration = e.type === 'mousemove' ? 2500 : Effect.duration;
286 |
287 | setTimeout(function() {
288 |
289 | var style = {
290 | top: relativeY + 'px',
291 | left: relativeX + 'px',
292 | opacity: '0',
293 |
294 | // Duration
295 | '-webkit-transition-duration': duration + 'ms',
296 | '-moz-transition-duration': duration + 'ms',
297 | '-o-transition-duration': duration + 'ms',
298 | 'transition-duration': duration + 'ms',
299 | '-webkit-transform': scale + ' ' + translate,
300 | '-moz-transform': scale + ' ' + translate,
301 | '-ms-transform': scale + ' ' + translate,
302 | '-o-transform': scale + ' ' + translate,
303 | 'transform': scale + ' ' + translate
304 | };
305 |
306 | ripple.setAttribute('style', convertStyle(style));
307 |
308 | setTimeout(function() {
309 | try {
310 | el.removeChild(ripple);
311 | } catch (e) {
312 | return false;
313 | }
314 | }, duration);
315 |
316 | }, delay);
317 | }
318 |
319 |
320 | /**
321 | * Disable mousedown event for 500ms during and after touch
322 | */
323 | var TouchHandler = {
324 |
325 | /* uses an integer rather than bool so there's no issues with
326 | * needing to clear timeouts if another touch event occurred
327 | * within the 500ms. Cannot mouseup between touchstart and
328 | * touchend, nor in the 500ms after touchend. */
329 | touches: 0,
330 |
331 | allowEvent: function(e) {
332 |
333 | var allow = true;
334 |
335 | if (/^(mousedown|mousemove)$/.test(e.type) && TouchHandler.touches) {
336 | allow = false;
337 | }
338 |
339 | return allow;
340 | },
341 | registerEvent: function(e) {
342 | var eType = e.type;
343 |
344 | if (eType === 'touchstart') {
345 |
346 | TouchHandler.touches += 1; // push
347 |
348 | } else if (/^(touchend|touchcancel)$/.test(eType)) {
349 |
350 | setTimeout(function() {
351 | if (TouchHandler.touches) {
352 | TouchHandler.touches -= 1; // pop after 500ms
353 | }
354 | }, 500);
355 |
356 | }
357 | }
358 | };
359 |
360 |
361 | /**
362 | * Delegated click handler for .waves-effect element.
363 | * returns null when .waves-effect element not in "click tree"
364 | */
365 | function getWavesEffectElement(e) {
366 |
367 | if (TouchHandler.allowEvent(e) === false) {
368 | return null;
369 | }
370 |
371 | var element = null;
372 | var target = e.target || e.srcElement;
373 |
374 | while (target.parentElement) {
375 | if ( (!(target instanceof SVGElement)) && target.classList.contains('waves-effect')) {
376 | element = target;
377 | break;
378 | }
379 | target = target.parentElement;
380 | }
381 |
382 | return element;
383 | }
384 |
385 | /**
386 | * Bubble the click and show effect if .waves-effect elem was found
387 | */
388 | function showEffect(e) {
389 |
390 | // Disable effect if element has "disabled" property on it
391 | // In some cases, the event is not triggered by the current element
392 | // if (e.target.getAttribute('disabled') !== null) {
393 | // return;
394 | // }
395 |
396 | var element = getWavesEffectElement(e);
397 |
398 | if (element !== null) {
399 |
400 | // Make it sure the element has either disabled property, disabled attribute or 'disabled' class
401 | if (element.disabled || element.getAttribute('disabled') || element.classList.contains('disabled')) {
402 | return;
403 | }
404 |
405 | TouchHandler.registerEvent(e);
406 |
407 | if (e.type === 'touchstart' && Effect.delay) {
408 |
409 | var hidden = false;
410 |
411 | var timer = setTimeout(function () {
412 | timer = null;
413 | Effect.show(e, element);
414 | }, Effect.delay);
415 |
416 | var hideEffect = function(hideEvent) {
417 |
418 | // if touch hasn't moved, and effect not yet started: start effect now
419 | if (timer) {
420 | clearTimeout(timer);
421 | timer = null;
422 | Effect.show(e, element);
423 | }
424 | if (!hidden) {
425 | hidden = true;
426 | Effect.hide(hideEvent, element);
427 | }
428 |
429 | removeListeners();
430 | };
431 |
432 | var touchMove = function(moveEvent) {
433 | if (timer) {
434 | clearTimeout(timer);
435 | timer = null;
436 | }
437 | hideEffect(moveEvent);
438 |
439 | removeListeners();
440 | };
441 |
442 | element.addEventListener('touchmove', touchMove, false);
443 | element.addEventListener('touchend', hideEffect, false);
444 | element.addEventListener('touchcancel', hideEffect, false);
445 |
446 | var removeListeners = function() {
447 | element.removeEventListener('touchmove', touchMove);
448 | element.removeEventListener('touchend', hideEffect);
449 | element.removeEventListener('touchcancel', hideEffect);
450 | };
451 | } else {
452 |
453 | Effect.show(e, element);
454 |
455 | if (isTouchAvailable) {
456 | element.addEventListener('touchend', Effect.hide, false);
457 | element.addEventListener('touchcancel', Effect.hide, false);
458 | }
459 |
460 | element.addEventListener('mouseup', Effect.hide, false);
461 | element.addEventListener('mouseleave', Effect.hide, false);
462 | }
463 | }
464 | }
465 |
466 | Waves.init = function(options) {
467 | var body = document.body;
468 |
469 | options = options || {};
470 |
471 | if ('duration' in options) {
472 | Effect.duration = options.duration;
473 | }
474 |
475 | if ('delay' in options) {
476 | Effect.delay = options.delay;
477 | }
478 |
479 | if (isTouchAvailable) {
480 | body.addEventListener('touchstart', showEffect, false);
481 | body.addEventListener('touchcancel', TouchHandler.registerEvent, false);
482 | body.addEventListener('touchend', TouchHandler.registerEvent, false);
483 | }
484 |
485 | body.addEventListener('mousedown', showEffect, false);
486 | };
487 |
488 |
489 | /**
490 | * Attach Waves to dynamically loaded inputs, or add .waves-effect and other
491 | * waves classes to a set of elements. Set drag to true if the ripple mouseover
492 | * or skimming effect should be applied to the elements.
493 | */
494 | Waves.attach = function(elements, classes) {
495 |
496 | elements = getWavesElements(elements);
497 |
498 | if (toString.call(classes) === '[object Array]') {
499 | classes = classes.join(' ');
500 | }
501 |
502 | classes = classes ? ' ' + classes : '';
503 |
504 | var element, tagName;
505 |
506 | for (var i = 0, len = elements.length; i < len; i++) {
507 |
508 | element = elements[i];
509 | tagName = element.tagName.toLowerCase();
510 |
511 | if (['input', 'img'].indexOf(tagName) !== -1) {
512 | TagWrapper[tagName](element);
513 | element = element.parentElement;
514 | }
515 |
516 | if (element.className.indexOf('waves-effect') === -1) {
517 | element.className += ' waves-effect' + classes;
518 | }
519 | }
520 | };
521 |
522 |
523 | /**
524 | * Cause a ripple to appear in an element via code.
525 | */
526 | Waves.ripple = function(elements, options) {
527 | elements = getWavesElements(elements);
528 | var elementsLen = elements.length;
529 |
530 | options = options || {};
531 | options.wait = options.wait || 0;
532 | options.position = options.position || null; // default = centre of element
533 |
534 |
535 | if (elementsLen) {
536 | var element, pos, off, centre = {}, i = 0;
537 | var mousedown = {
538 | type: 'mousedown',
539 | button: 1
540 | };
541 | var hideRipple = function(mouseup, element) {
542 | return function() {
543 | Effect.hide(mouseup, element);
544 | };
545 | };
546 |
547 | for (; i < elementsLen; i++) {
548 | element = elements[i];
549 | pos = options.position || {
550 | x: element.clientWidth / 2,
551 | y: element.clientHeight / 2
552 | };
553 |
554 | off = offset(element);
555 | centre.x = off.left + pos.x;
556 | centre.y = off.top + pos.y;
557 |
558 | mousedown.pageX = centre.x;
559 | mousedown.pageY = centre.y;
560 |
561 | Effect.show(mousedown, element);
562 |
563 | if (options.wait >= 0 && options.wait !== null) {
564 | var mouseup = {
565 | type: 'mouseup',
566 | button: 1
567 | };
568 |
569 | setTimeout(hideRipple(mouseup, element), options.wait);
570 | }
571 | }
572 | }
573 | };
574 |
575 | /**
576 | * Remove all ripples from an element.
577 | */
578 | Waves.calm = function(elements) {
579 | elements = getWavesElements(elements);
580 | var mouseup = {
581 | type: 'mouseup',
582 | button: 1
583 | };
584 |
585 | for (var i = 0, len = elements.length; i < len; i++) {
586 | Effect.hide(mouseup, elements[i]);
587 | }
588 | };
589 |
590 | /**
591 | * Deprecated API fallback
592 | */
593 | Waves.displayEffect = function(options) {
594 | console.error('Waves.displayEffect() has been deprecated and will be removed in future version. Please use Waves.init() to initialize Waves effect');
595 | Waves.init(options);
596 | };
597 |
598 | return Waves;
599 | });
600 |
--------------------------------------------------------------------------------
/webpack.config.babel.js:
--------------------------------------------------------------------------------
1 | export default {
2 | entry: './index.js',
3 | output: {
4 | path: __dirname + '/dist',
5 | filename: 'vue-waves.js',
6 | library: 'VueWaves',
7 | libraryTarget: 'umd',
8 | },
9 | module: {
10 | rules: [
11 | { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" },
12 | { test: /\.css$/, loader: "style-loader!css-loader" },
13 | ]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------