src/animations/ThreeDotsBouncing.js
/**
* @file ThreeDotsBouncing.js
* @author Liqueur de Toile <contact@liqueurdetoile.com>
* @licence AGPL-3.0 {@link https://github.com/liqueurdetoile/beloader-animations/blob/master/LICENSE}
*/
const anime = window.anime;
const Q = window.elementify.Q;
import AbstractAnimation from 'core/AbstractAnimation';
/**
* ThreeDotsBouncing provides a block of
* three dots bouncing from left to right and reverse.
*
* @version 1.0.0
* @since 1.0.0
* @author Liqueur de Toile <contact@liqueurdetoile.com>
* @extends {AbstractAnimation}
*
* @example
* var dots = new ThreeDotsBouncing();
*
* document.body.appendChild(dots.block);
* dots.start();
*
* @see https://cdn.rawgit.com/beloader/beloader-animations/94619414/demos/threedotsbouncing.html
*/
export default class ThreeDotsBouncing extends AbstractAnimation {
/**
* Creates an instance of ThreeDotsBouncing with given
* options
*
* @version 1.0.0
* @since 1.0.0
* @author Liqueur de Toile <contact@liqueurdetoile.com>
*
* @param {Object} options Options for the animation
* @param {boolean} [options.loop=true]
* @param {number} [options.duration=3000] Duration of the animation
* @param {number} [options.offset=1000] Offset duration between each dots animation
* @param {number} [options.width=25%] Width of the container (any CSS compatible value)
* @param {Object} [options.dots] Specific options for dots
* @param {number} [options.dots.width=30] Width of a dot (in pixels)
* @param {number} [options.dots.height=30] Height of a dot (in pixels)
* @param {string|Function} [options.dots.color='#fff'] color of a dot (any CSS compatible form)
*/
constructor(options = {}) {
super(options);
this.options.define('loop', true);
this.options.define('duration', 3000);
this.options.define('offset', 1000);
this.options.define('width', '25%');
this.options.define('dots.width', 30);
this.options.define('dots.height', 30);
this.options.define('dots.color', '#fff');
}
/**
* Get the HTMLElement for the block
*
* @version 1.0.0
* @since 1.0.0
* @author Liqueur de Toile <contact@liqueurdetoile.com>
* @type {HTMLElement}
*/
get block() {
/* istanbul ignore else */
if (!this._block) {
let dd = [
this.options.pull('duration') + this.options.pull('offset'),
this.options.pull('duration') / 2,
this.options.pull('duration') - this.options.pull('offset')
];
let ddr = dd.reverse();
this._block = Q('+div', {
id: this.id,
class: 'tbd',
style: {
margin: '0 auto',
width: this.options.pull('width')
}
});
dd.forEach((d, i) => {
let line = Q('+div', {
class: 'tdb-line',
style: 'margin: 1em 0'
});
line.append(Q('+div', {
class: 'tdb-dot',
style: {
width: this.options.pull('dots.width'),
height: this.options.pull('dots.height'),
backgroundColor: this.options.pull('dots.color')
},
data: {
duration: d,
'duration-rev': ddr[i]
}
}));
this._block.append(line);
});
}
return this._block;
}
/**
* Starts the animation
*
* @version 1.0.0
* @since 1.0.0
* @author Liqueur de Toile <contact@liqueurdetoile.com>
*
* @returns {this} Chainable
*/
start() {
let timeline = anime.timeline({
loop: this.options.pull('loop'),
autoplay: true
});
timeline.add({
targets: `#${this.id} .tbd-dot`,
translateX: Q(`#${this.id}`).width - this.options.pull('dots.width'),
rotate: 180,
duration: function (target) {
return target.getAttribute('data-duration');
},
delay: function (target, index) {
return index * 100;
},
elasticity: function (target, index, totalTargets) {
return 200 + ((totalTargets - index) * 200);
}
});
timeline.add({
targets: `*#${this.id} .tdb-dot`,
translateX: 0,
rotate: 0,
duration: function (target) {
return target.getAttribute('data-duration-rev');
},
delay: function (target, index, totalTargets) {
return 100 + ((totalTargets - index) * 100);
},
elasticity: function (target, index, totalTargets) {
return 200 + ((index - totalTargets) * 200);
}
});
this.animation = timeline;
return this;
}
}