1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | import onsElements from '../ons/elements.js';
|
19 | import util from '../ons/util.js';
|
20 | import internal from '../ons/internal/index.js';
|
21 | import BaseElement from './base/base-element.js';
|
22 | import Animator from './ons-ripple/animator-css.js';
|
23 | import contentReady from '../ons/content-ready.js';
|
24 | import ModifierUtil from '../ons/internal/modifier-util.js';
|
25 |
|
26 | const defaultClassName = 'ripple';
|
27 | const scheme = {
|
28 | '': 'ripple--*',
|
29 | '.ripple__wave': 'ripple--*__wave',
|
30 | '.ripple__background': 'ripple--*__background',
|
31 | };
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 | export default class RippleElement extends BaseElement {
|
60 |
|
61 | |
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | |
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 | |
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 | |
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | |
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | |
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | |
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 | constructor() {
|
117 | super();
|
118 |
|
119 | this._onTap = this._onTap.bind(this);
|
120 | this._onHold = this._onHold.bind(this);
|
121 | this._onDragStart = this._onDragStart.bind(this);
|
122 | this._onRelease = this._onRelease.bind(this);
|
123 |
|
124 | contentReady(this, () => this._compile());
|
125 |
|
126 | this._animator = new Animator();
|
127 |
|
128 | ['color', 'center', 'start-radius', 'background', 'modifier'].forEach(e => {
|
129 | this.attributeChangedCallback(e, null, this.getAttribute(e));
|
130 | });
|
131 | }
|
132 |
|
133 | _compile() {
|
134 | this.classList.add(defaultClassName);
|
135 |
|
136 | this._wave = this.getElementsByClassName('ripple__wave')[0];
|
137 | this._background = this.getElementsByClassName('ripple__background')[0];
|
138 |
|
139 | if (!(this._background && this._wave)) {
|
140 | this._wave = util.create('.ripple__wave');
|
141 | this._background = util.create('.ripple__background');
|
142 |
|
143 | this.appendChild(this._wave);
|
144 | this.appendChild(this._background);
|
145 | }
|
146 |
|
147 | ModifierUtil.initModifier(this, scheme);
|
148 | }
|
149 |
|
150 | _getEffectSize() {
|
151 | const sizes = ['cover', 'contain'];
|
152 | if (this.hasAttribute('size')) {
|
153 | const size = this.getAttribute('size');
|
154 | if (sizes.indexOf(size) !== -1) {
|
155 | return size;
|
156 | }
|
157 | }
|
158 |
|
159 | return 'cover';
|
160 | }
|
161 |
|
162 | _calculateCoords(e) {
|
163 | let x, y, h, w, r;
|
164 | const b = this.getBoundingClientRect();
|
165 | const size = this._getEffectSize();
|
166 | const error = () => util.throw('Ripple invalid state');
|
167 |
|
168 | if (this._center) {
|
169 | x = b.width / 2;
|
170 | y = b.height / 2;
|
171 |
|
172 | if (size === 'cover') {
|
173 | r = Math.sqrt(x * x + y * y);
|
174 | } else if (size === 'contain') {
|
175 | r = Math.min(x, y);
|
176 | } else {
|
177 | error();
|
178 | }
|
179 | } else {
|
180 | x = (typeof e.clientX === 'number' ? e.clientX : e.changedTouches[0].clientX) - b.left;
|
181 | y = (typeof e.clientY === 'number' ? e.clientY : e.changedTouches[0].clientY) - b.top;
|
182 | h = Math.max(y, b.height - y);
|
183 | w = Math.max(x, b.width - x);
|
184 |
|
185 | if (size === 'cover') {
|
186 | r = Math.sqrt(h * h + w * w);
|
187 | } else if (size === 'contain') {
|
188 | r = Math.min(Math.round(h / 2), Math.round(w / 2));
|
189 | } else {
|
190 | error();
|
191 | }
|
192 | }
|
193 |
|
194 | return {x, y, r};
|
195 | }
|
196 |
|
197 | _rippleAnimation(e, duration = 300) {
|
198 | const {_animator, _wave, _background, _minR} = this;
|
199 | const {x, y, r} = this._calculateCoords(e);
|
200 |
|
201 | _animator.stopAll({stopNext: 1});
|
202 | _animator.animate(_background, {opacity: 1}, duration);
|
203 |
|
204 | util.extend(_wave.style, {
|
205 | opacity: 1,
|
206 | top: y - _minR + 'px',
|
207 | left: x - _minR + 'px',
|
208 | width: 2 * _minR + 'px',
|
209 | height: 2 * _minR + 'px'
|
210 | });
|
211 |
|
212 | return _animator.animate(_wave, {
|
213 | top: y - r,
|
214 | left: x - r,
|
215 | height: 2 * r,
|
216 | width: 2 * r
|
217 | }, duration);
|
218 | }
|
219 |
|
220 | _updateParent() {
|
221 | if (!this._parentUpdated && this.parentNode) {
|
222 | const computedStyle = window.getComputedStyle(this.parentNode);
|
223 | if (computedStyle.getPropertyValue('position') === 'static') {
|
224 | this.parentNode.style.position = 'relative';
|
225 | }
|
226 | this._parentUpdated = true;
|
227 | }
|
228 | }
|
229 |
|
230 | _onTap(e) {
|
231 | if (!this.disabled && !e.ripple) {
|
232 | e.ripple = true;
|
233 | this._updateParent();
|
234 | this._rippleAnimation(e.gesture.srcEvent).then(() => {
|
235 | this._animator.fade(this._wave);
|
236 | this._animator.fade(this._background);
|
237 | });
|
238 | }
|
239 | }
|
240 |
|
241 | _onHold(e) {
|
242 | if (!this.disabled && !e.ripple) {
|
243 | e.ripple = true;
|
244 | this._updateParent();
|
245 | this._holding = this._rippleAnimation(e.gesture.srcEvent, 2000);
|
246 | document.addEventListener('release', this._onRelease);
|
247 | }
|
248 | }
|
249 |
|
250 | _onRelease(e) {
|
251 | if (this._holding && !e.ripple) {
|
252 | e.ripple = true;
|
253 | this._holding.speed(300).then(() => {
|
254 | this._animator.stopAll({stopNext: true});
|
255 | this._animator.fade(this._wave);
|
256 | this._animator.fade(this._background);
|
257 | });
|
258 |
|
259 | this._holding = false;
|
260 | }
|
261 |
|
262 | document.removeEventListener('release', this._onRelease);
|
263 | }
|
264 |
|
265 | _onDragStart(e) {
|
266 | if (this._holding) {
|
267 | return this._onRelease(e);
|
268 | }
|
269 | if (['left', 'right'].indexOf(e.gesture.direction) != -1) {
|
270 | this._onTap(e);
|
271 | }
|
272 | }
|
273 |
|
274 | connectedCallback() {
|
275 | this._parentNode = this.parentNode;
|
276 |
|
277 | if (internal.config.animationsDisabled) {
|
278 | this.disabled = true;
|
279 | } else {
|
280 | this._parentNode.addEventListener('tap', this._onTap);
|
281 | this._parentNode.addEventListener('hold', this._onHold);
|
282 | this._parentNode.addEventListener('dragstart', this._onDragStart);
|
283 | }
|
284 | }
|
285 |
|
286 | disconnectedCallback() {
|
287 | const pn = this._parentNode || this.parentNode;
|
288 | pn.removeEventListener('tap', this._onTap);
|
289 | pn.removeEventListener('hold', this._onHold);
|
290 | pn.removeEventListener('dragstart', this._onDragStart);
|
291 | }
|
292 |
|
293 | static get observedAttributes() {
|
294 | return ['start-radius', 'color', 'background', 'center', 'class', 'modifier'];
|
295 | }
|
296 |
|
297 | attributeChangedCallback(name, last, current) {
|
298 | switch (name) {
|
299 |
|
300 | case 'class':
|
301 | util.restoreClass(this, defaultClassName, scheme);
|
302 | break;
|
303 |
|
304 | case 'modifier':
|
305 | ModifierUtil.onModifierChanged(last, current, this, scheme);
|
306 | break;
|
307 |
|
308 | case 'start-radius':
|
309 | this._minR = Math.max(0, parseFloat(current) || 0);
|
310 | break;
|
311 |
|
312 | case 'color':
|
313 | if (current) {
|
314 | contentReady(this, () => {
|
315 | this._wave.style.background = current;
|
316 | if (!this.hasAttribute('background')) {
|
317 | this._background.style.background = current;
|
318 | }
|
319 | });
|
320 | }
|
321 | break;
|
322 |
|
323 | case 'background':
|
324 | if (current || last) {
|
325 | if (current === 'none') {
|
326 | contentReady(this, () => {
|
327 | this._background.setAttribute('disabled', 'disabled');
|
328 | this._background.style.background = 'transparent';
|
329 | });
|
330 | } else {
|
331 | contentReady(this, () => {
|
332 | if (this._background.hasAttribute('disabled')) {
|
333 | this._background.removeAttribute('disabled');
|
334 | }
|
335 | this._background.style.background = current;
|
336 | });
|
337 | }
|
338 | }
|
339 | break;
|
340 |
|
341 | case 'center':
|
342 | if (name === 'center') {
|
343 | this._center = current != null && current != 'false';
|
344 | }
|
345 | break;
|
346 |
|
347 | }
|
348 | }
|
349 |
|
350 | |
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 | }
|
358 |
|
359 | util.defineBooleanProperties(RippleElement, ['disabled', 'center']);
|
360 |
|
361 | onsElements.Ripple = RippleElement;
|
362 | customElements.define('ons-ripple', RippleElement);
|