UNPKG

5.17 kBJavaScriptView Raw
1/*
2Copyright 2013-2016 ASIAL CORPORATION
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15
16*/
17
18import internal from '../../ons/internal/index.js';
19
20
21var raf = (window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame)
22 || (cb => { setTimeout(() => { cb(new Date().getTime()); }, 1000 / 60); });
23
24raf = raf.bind(window);
25
26/**
27 * @class AnimatorJS - implementation of Animator class using javascript
28 */
29class AnimatorJS {
30
31 /**
32 * @method animate
33 * @desc main animation function
34 * @param {Element} element
35 * @param {Object} finalCSS
36 * @param {number} [duration=200] - duration in milliseconds
37 * @return {Object} result
38 * @return {Function} result.then(callback) - sets a callback to be executed after the animation has stopped
39 * @return {Function} result.stop(options) - stops the animation; if options.stopNext is true then it doesn't call the callback
40 * @return {Function} result.finish(ms) - finishes the animation in the specified time in milliseconds
41 * @return {Function} result.speed(ms) - sets the animation speed so that it finishes as if the original duration was the one specified here
42 * @example
43 * ````
44 * var result = animator.animate(el, {opacity: 0.5}, 1000);
45 *
46 * el.addEventListener('click', function(e){
47 * result.speed(200).then(function(){
48 * console.log('done');
49 * });
50 * }, 300);
51 * ````
52 */
53 animate(el, final, duration = 200) {
54 var start,
55 initial = {},
56 stopped = false,
57 next,
58 elapsed,
59 properties = Object.keys(final);
60
61 var result = {
62 stop: (options = {}) => {
63 if (options.stopNext) {
64 next = false;
65 }
66 if (!stopped) {
67 stopped = true;
68 next && next();
69 }
70 return result;
71 },
72 then: (cb) => {
73 next = cb;
74 if (stopped) {
75 next && next();
76 }
77 return result;
78 },
79 finish: (milliseconds = 50) => {
80 var k = milliseconds / (duration - elapsed);
81 if (internal.config.animationsDisabled) {
82 k = 0;
83 }
84 if (k < 1) {
85 start += elapsed - elapsed * k;
86 duration *= k;
87 }
88 return result;
89 },
90 speed: (newDuration) => {
91 return result.finish(newDuration * (1 - elapsed / duration));
92 }
93 };
94
95 if (el.hasAttribute('disabled') || internal.config.animationsDisabled) {
96 return result;
97 }
98
99 var cs = window.getComputedStyle(el);
100 properties.forEach(i => {
101 initial[i] = parseFloat(el.style[i] || cs.getPropertyValue(i));
102 });
103 this._onStopAnimations(el, result.stop);
104
105 var step = (timestamp) => {
106 start = start || timestamp;
107 elapsed = timestamp - start;
108 if (!stopped) {
109 properties.forEach(i => {
110 el.style[i] = initial[i] + (final[i] - initial[i]) * Math.min(1, elapsed / duration) + (i == 'opacity' ? 0 : 'px');
111 });
112 stopped = stopped || elapsed >= duration;
113 if (!stopped) {
114 return raf(step);
115 }
116 }
117 return next && next();
118 };
119 step(0);
120
121 return result;
122 }
123
124 constructor () {
125 this._queue = [];
126 this._index = 0;
127 }
128
129 _onStopAnimations(el, listener) {
130 var queue = this._queue;
131 var i = this._index++;
132 queue[el] = queue[el] || [];
133 queue[el][i] = (options) => {
134 delete queue[el][i];
135 if (queue[el] && queue[el].length == 0) {
136 delete queue[el];
137 }
138 return listener(options);
139 };
140 }
141
142 /**
143 * @method stopAnimations
144 * @desc stops active animations on a specified element
145 * @param {Element|Array} element - element or array of elements
146 * @param {Object} [options={}]
147 * @param {Boolean} [options.stopNext] - the callbacks after the animations won't be called if this option is true
148 */
149 stopAnimations(el, options = {}) {
150 if (Array.isArray(el)) {
151 return el.forEach(el => {
152 this.stopAnimations(el, options);
153 });
154 }
155
156 (this._queue[el] || []).forEach(e => { e(options || {}); });
157 }
158
159 /**
160 * @method stopAll
161 * @desc stops all active animations
162 * @param {Object} [options={}]
163 * @param {Boolean} [options.stopNext] - the callbacks after the animations won't be called if this option is true
164 */
165 stopAll(options = {}) {
166 this.stopAnimations(Object.keys(this._queue), options);
167 }
168
169 /**
170 * @method fade
171 * @desc fades the element (short version for animate(el, {opacity: 0}))
172 * @param {Element} element
173 * @param {number} [duration=200]
174 */
175 fade(el, duration = 200) {
176 return this.animate(el, {opacity: 0}, duration);
177 }
178
179}
180
181export default AnimatorJS;
182