diff --git a/index.html b/index.html index 6546475..562f018 100644 --- a/index.html +++ b/index.html @@ -6,28 +6,75 @@ -
-
-

fadeIn

- -
-
-
-
-
-

move

- -
-
-
-
-
-

scale

- -
-
-
+
+
+

fadeIn

+ + +
+
+
+
+
+

fadeOut

+ + +
+
+
+
+
+

move

+ + +
+
+
+
+
+

scale

+ + +
+
+
+
+
+

moveAndHide

+ + +
+
+
+
+
+

showAndHide

+ + +
+
+
+
+
+

heartBeating

+ + +
+
+
+
+
+

worryAnimation

+
+
+
+
+
+

custom

+
+
+
- + \ No newline at end of file diff --git a/index.js b/index.js index 61e55f6..cd5f2cd 100644 --- a/index.js +++ b/index.js @@ -1,65 +1,207 @@ -addListeners(); +class Animaster { + constructor() { + this._steps = [] + } + + static #getTransform(translation = null, ratio = null, angle = null) { + const result = []; + if (translation) { + result.push(`translate(${translation.x}px,${translation.y}px)`); + } + if (ratio) { + result.push(`scale(${ratio})`); + } + if (angle) { + result.push(`rotate(${angle}deg)`); + } + + return result.join(' '); + } + + static #setFade(element, show) { + element.classList.remove(show ? 'hide' : 'show'); + element.classList.add(show ? 'show' : 'hide'); + } + + static #setCustom (element, scale = null, radius = null) { + element.style.transform = `scale(${scale})`; + element.style.borderRadius = `${radius}%`; + } + + play(element, cycled = false) { + let tick; + let timerId = setTimeout(tick = index => { + element.style.transitionDuration = `${this._steps[index].duration}ms`; + this._steps[index].action(element); + if (cycled === true || index < this._steps.length - 1) + timerId = setTimeout(tick, Math.round(this._steps[index].duration), + (index + 1) % this._steps.length); + }, 0, 0); + return { + stop: () => { + clearTimeout(timerId); + }, + + reset: () => { + element.style.transitionDuration = null; + this._steps.slice().reverse().forEach(s => s.undo(element)); + } + } + } + + buildHandler() { + let animasterObj = this; + return function () { + return animasterObj.play(this); + }; + } + + clone() { + let newAnimaster = new Animaster(); + newAnimaster._steps = this._steps.slice(); + return newAnimaster; + } -function addListeners() { - document.getElementById('fadeInPlay') - .addEventListener('click', function () { - const block = document.getElementById('fadeInBlock'); - fadeIn(block, 5000); + addFadeIn(duration) { + let clone = this.clone(); + clone._steps.push({ + duration, + action: element => Animaster.#setFade(element, true), + undo: element => Animaster.#setFade(element, false) }); + return clone; + } - document.getElementById('movePlay') - .addEventListener('click', function () { - const block = document.getElementById('moveBlock'); - move(block, 1000, {x: 100, y: 10}); + addFadeOut(duration) { + let clone = this.clone(); + clone._steps.push({ + duration, + action: element => Animaster.#setFade(element, false), + undo: element => Animaster.#setFade(element, true) }); + return clone; + } - document.getElementById('scalePlay') - .addEventListener('click', function () { - const block = document.getElementById('scaleBlock'); - scale(block, 1000, 1.25); + addMove(duration, translation) { + let clone = this.clone(); + clone._steps.push({ + duration, + action: element => element.style.transform = Animaster.#getTransform(translation), + undo: element => element.style.transform = Animaster.#getTransform() }); -} + return clone; + } -/** - * Блок плавно появляется из прозрачного. - * @param element — HTMLElement, который надо анимировать - * @param duration — Продолжительность анимации в миллисекундах - */ -function fadeIn(element, duration) { - element.style.transitionDuration = `${duration}ms`; - element.classList.remove('hide'); - element.classList.add('show'); -} + addScale(duration, ratio) { + let clone = this.clone(); + clone._steps.push({ + duration, + action: element => element.style.transform = Animaster.#getTransform(null, ratio), + undo: element => element.style.transform = Animaster.#getTransform() + }); + return clone; + } -/** - * Функция, передвигающая элемент - * @param element — HTMLElement, который надо анимировать - * @param duration — Продолжительность анимации в миллисекундах - * @param translation — объект с полями x и y, обозначающими смещение блока - */ -function move(element, duration, translation) { - element.style.transitionDuration = `${duration}ms`; - element.style.transform = getTransform(translation, null); -} + addDelay(duration) { + let clone = this.clone(); + clone._steps.push({ + duration, + action: () => null, + undo: () => null + }); + return clone; + } -/** - * Функция, увеличивающая/уменьшающая элемент - * @param element — HTMLElement, который надо анимировать - * @param duration — Продолжительность анимации в миллисекундах - * @param ratio — во сколько раз увеличить/уменьшить. Чтобы уменьшить, нужно передать значение меньше 1 - */ -function scale(element, duration, ratio) { - element.style.transitionDuration = `${duration}ms`; - element.style.transform = getTransform(null, ratio); -} + addCustom(duration, scale, radius) { + let clone = this.clone(); + clone._steps.push({ + duration, + action: element => Animaster.#setCustom(element, scale, radius), + undo: element => Animaster.#setCustom(element) + }); + return clone; + } + + fadeIn(duration) { + return this.addFadeIn(duration); + } + + fadeOut(duration) { + return this.addFadeOut(duration); + } + + move(duration, translation) { + return this.addMove(duration, translation); + } + + scale(duration, ratio) { + return this.addScale(duration, ratio); + } -function getTransform(translation, ratio) { - const result = []; - if (translation) { - result.push(`translate(${translation.x}px,${translation.y}px)`); + moveAndHide(duration, translation) { + return this + .addMove(duration, translation) + .addFadeOut(duration) } - if (ratio) { - result.push(`scale(${ratio})`); + + showAndHide(duration) { + return this + .addFadeIn(duration / 2) + .addDelay(duration / 2) + .addFadeOut(duration / 2) + } + + heartBeating() { + return this + .addScale(250, 1.4) + .addScale(250, 1) + .addDelay(25) + .addScale(250, 1.4) + .addScale(250, 1) + .addDelay(500) + } +} + +function addListeners(cycled, ...listeners) { + for (let listener of listeners) { + let block = document.getElementById(listener[0] + 'Block'); + let execute = document.getElementById(listener[0] + (cycled ? 'Stop' : 'Reset')); + let animasterObj = new Animaster()[listener[0]](...listener.slice(1)) + document.getElementById(listener[0] + 'Play') + .addEventListener('click', function () { + let executor = animasterObj.play(block, cycled); + execute.addEventListener('click', cycled ? executor.stop : executor.reset) + }); } - return result.join(' '); } + +addListeners(false, + ['fadeIn', 5000], + ['fadeOut', 5000], + ['move', 1000, {x: 100, y: 10}], + ['scale', 1000, 1.25], + ['moveAndHide', 1000, {x: 100, y: 10}], + ['showAndHide', 1000]); + +addListeners(true, ['heartBeating']); + +const worryAnimationHandler = new Animaster() + .addMove(200, {x: 80, y: 0}) + .addMove(200, {x: 0, y: 0}) + .addMove(200, {x: 80, y: 0}) + .addMove(200, {x: 0, y: 0}) + .buildHandler(); + +document + .getElementById('worryAnimationBlock') + .addEventListener('click', worryAnimationHandler); + +const customHandler = new Animaster() + .addCustom(500, 2, 50) + .addDelay(200) + .addCustom(500, 1, 0) + .buildHandler(); + +document + .getElementById('customBlock') + .addEventListener('click', customHandler);