From 285e3d8263f55291c025e4ff2de7cd09a58a46b4 Mon Sep 17 00:00:00 2001 From: Teelirium <74907594+Teelirium@users.noreply.github.com> Date: Thu, 21 Oct 2021 16:10:35 +0500 Subject: [PATCH 1/2] tasks 1-16 completed --- index.html | 37 ++++++- index.js | 281 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 275 insertions(+), 43 deletions(-) diff --git a/index.html b/index.html index 6546475..507e6b1 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@

fadeIn

+
@@ -17,6 +18,7 @@

fadeIn

move

+
@@ -24,10 +26,43 @@

move

scale

+
- +
+
+

moveAndHide

+ + +
+
+
+
+
+

showAndHide

+ + +
+
+
+
+
+

heartBeating

+ + + +
+
+
+
+
+

custom

+ + +
+
+
\ No newline at end of file diff --git a/index.js b/index.js index 61e55f6..8d6ce85 100644 --- a/index.js +++ b/index.js @@ -1,56 +1,232 @@ -addListeners(); +function animaster() { + return new Animaster; +} -function addListeners() { - document.getElementById('fadeInPlay') - .addEventListener('click', function () { - const block = document.getElementById('fadeInBlock'); - fadeIn(block, 5000); +class Animaster { + constructor () { + this._steps = []; + } + + play(element, cycled = false) { + const playSteps = (index) => { + const duration = this._steps[index].duration; + element.style.transitionDuration = `${duration}ms`; + this._steps[index].action(element); + if(cycled || index < this._steps.length - 1) + timer = setTimeout(playSteps, duration, (index + 1) % this._steps.length); + }; + let timer = setTimeout(playSteps, 0, 0); + const originalClassList = element.classList; + return { + stop: () => clearTimeout(timer), + reset: element => { + clearTimeout(timer); + element.style.transitionDuration = null; + this._steps.reverse().forEach(step => step.undo(element)); + element.classList = originalClassList; + } + } + } + + buildHandler() { + const animaster = this; + return function() { + animaster.play(this); + } + } + + clone() { + let animasterClone = animaster(); + animasterClone._steps = this._steps.slice(); + return animasterClone; + } + + addDelay(duration) { + const newAnimaster = this.clone(); + newAnimaster._steps.push({ + duration, + action: () => {}, + undo: () => {} }); + return newAnimaster; + } + + addMove(duration, translation) { + const newAnimaster = this.clone(); + newAnimaster._steps.push({ + duration, + action: element => element.style.transform = getTransform(translation, null), + undo: element => this.#resetMoveAndScale(element) + }); + return newAnimaster; + } - document.getElementById('movePlay') - .addEventListener('click', function () { - const block = document.getElementById('moveBlock'); - move(block, 1000, {x: 100, y: 10}); + addFadeOut(duration) { + const newAnimaster = this.clone(); + newAnimaster._steps.push({ + duration, + action: element => { + element.classList.remove('show'); + element.classList.add('hide'); + }, + undo: element => this.#resetFadeOut(element) }); + return newAnimaster; + } - document.getElementById('scalePlay') - .addEventListener('click', function () { - const block = document.getElementById('scaleBlock'); - scale(block, 1000, 1.25); + addFadeIn(duration) { + const newAnimaster = this.clone(); + newAnimaster._steps.push({ + duration, + action: element => { + element.classList.remove('hide'); + element.classList.add('show'); + }, + undo: element => this.#resetFadeIn(element) + }); + return newAnimaster; + } + + addScale(duration, ratio) { + const newAnimaster = this.clone(); + newAnimaster._steps.push({ + duration, + action: element => element.style.transform = getTransform(null, ratio), + undo: element => this.#resetMoveAndScale(element) }); -} + return newAnimaster; + } + + #resetFadeIn(element) { + element.classList.remove('show'); + element.classList.add('hide'); + } + + #resetFadeOut(element) { + element.classList.remove('hide'); + element.classList.add('show'); + } -/** - * Блок плавно появляется из прозрачного. - * @param element — HTMLElement, который надо анимировать - * @param duration — Продолжительность анимации в миллисекундах - */ -function fadeIn(element, duration) { - element.style.transitionDuration = `${duration}ms`; - element.classList.remove('hide'); - element.classList.add('show'); + #resetMoveAndScale(element) { + element.style.transform = getTransform(null, null); + } + + /** + * Блок плавно появляется из прозрачного. + * @param duration — Продолжительность анимации в миллисекундах + */ + fadeIn(duration) { + return this.addFadeIn(duration); + } + + /** + * Функция, передвигающая элемент + * @param duration — Продолжительность анимации в миллисекундах + * @param translation — объект с полями x и y, обозначающими смещение блока + */ + move(duration, translation) { + return this.addMove(duration, translation); + } + /** + * Функция, увеличивающая/уменьшающая элемент + * @param duration — Продолжительность анимации в миллисекундах + * @param ratio — во сколько раз увеличить/уменьшить. Чтобы уменьшить, нужно передать значение меньше 1 + */ + scale(duration, ratio) { + return this.addScale(duration, ratio); + } + + moveAndHide(duration, translation) { + return this.addMove(duration * 2 / 5, translation) + .addFadeOut(duration * 3 / 5); + } + + showAndHide(duration) { + return this.addFadeIn(duration / 3) + .addDelay(duration / 3) + .addFadeOut(duration / 3); + } + + heartBeating(duration) { + return this.addScale(duration / 2, 1.4) + .addDelay(duration / 6) + .addScale(duration / 2, 1) + .addDelay(duration / 6); + } + + custom(duration) { + const newAnimaster = this.clone(); + newAnimaster._steps.push({ + duration: duration / 4, + action: function() { + let blocks = document.getElementsByClassName('block'); + for (let block of blocks) { + block.style.transitionDuration = `${this.duration}ms`; + block.style.width = '300px'; + block.style.height = '300px'; + } + }, + undo: function() { + let blocks = document.getElementsByClassName('block'); + for (let block of blocks) { + block.style.transitionDuration = null; + block.style.width = null; + block.style.height = null; + } + } + }); + return newAnimaster; + } } -/** - * Функция, передвигающая элемент - * @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); +const animations = [{ + name: 'fadeIn', + duration: 5000 +}, { + name: 'move', + duration: 1000, + args: {x: 100, y: 10} +}, { + name: 'scale', + duration: 1000, + args: 1.25 +}, { + name: 'moveAndHide', + duration: 1000, + args: {x: 100, y: -20} +}, { + name: 'showAndHide', + duration: 1000, +}, { + name: 'heartBeating', + duration: 1000, + cycled: true +}, { + name: 'custom', + duration: 3000 +}]; +addListeners(); + +function addListeners() { + animations.forEach(anim => addListener(anim.name, anim.duration, anim.cycled, anim.args)); + + const builtCustomAnim = animaster().addMove(1000, {x: 0,y: -100}).addMove(1000, {x: 0, y: 100}).buildHandler(); + document.getElementById('customBlock').addEventListener('click', builtCustomAnim); } -/** - * Функция, увеличивающая/уменьшающая элемент - * @param element — HTMLElement, который надо анимировать - * @param duration — Продолжительность анимации в миллисекундах - * @param ratio — во сколько раз увеличить/уменьшить. Чтобы уменьшить, нужно передать значение меньше 1 - */ -function scale(element, duration, ratio) { - element.style.transitionDuration = `${duration}ms`; - element.style.transform = getTransform(null, ratio); +function addListener(name, duration = 1000, cycled = false, args = []) { + document.getElementById(`${name}Play`) + .addEventListener('click', function () { + const block = document.getElementById(`${name}Block`); + const resetter = animaster()[name](duration, args).play(block, cycled); + + if (cycled) + document.getElementById(`${name}Stop`) + .addEventListener('click', () => resetter.stop()); + + document.getElementById(`${name}Reset`) + .addEventListener('click', () => resetter.reset(block)); + }); } function getTransform(translation, ratio) { @@ -63,3 +239,24 @@ function getTransform(translation, ratio) { } return result.join(' '); } + +function Test_animationUniqueness() { + const block = document.getElementById('customBlock'); + const a = animaster().addMove(1000, {x:-100,y:-100}); + const b = a.addScale(500, 2); + const resetter = a.play(block); + setTimeout(() => resetter.reset(block), 2000); +} + +function Test_animationStacking() { + const customAnimation = animaster() + .addMove(200, {x: 40, y: 40}) + .addScale(800, 1.3) + .addMove(200, {x: 80, y: 0}) + .addScale(800, 1) + .addMove(200, {x: 40, y: -40}) + .addScale(800, 0.7) + .addMove(200, {x: 0, y: 0}) + .addScale(800, 1); + customAnimation.play(document.getElementById('customBlock')); +} \ No newline at end of file From 8d11732725c0d75e15f0c8233ac8afe0da97a2e4 Mon Sep 17 00:00:00 2001 From: Teelirium <74907594+Teelirium@users.noreply.github.com> Date: Thu, 21 Oct 2021 20:06:01 +0500 Subject: [PATCH 2/2] buildHandler fix --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 8d6ce85..791d503 100644 --- a/index.js +++ b/index.js @@ -210,7 +210,7 @@ addListeners(); function addListeners() { animations.forEach(anim => addListener(anim.name, anim.duration, anim.cycled, anim.args)); - const builtCustomAnim = animaster().addMove(1000, {x: 0,y: -100}).addMove(1000, {x: 0, y: 100}).buildHandler(); + const builtCustomAnim = animaster().addMove(1000, {x: 0,y: -100}).addMove(1000, {x: 0, y: 0}).buildHandler(); document.getElementById('customBlock').addEventListener('click', builtCustomAnim); }