diff --git a/README.md b/README.md index c044e46..521c069 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ При клике на кнопку play возле названия анимации запустят ее. -1. Создай функцию с названием `animaster` +✓ 1. Создай функцию с названием `animaster` -2. Сейчас функции-анимации (`move`, `fadeIn`, `scale`) лежат в глобальной области видимости и имеют очень простые названия — их легко случайно переопределить. Поэтому спрячь их определения внутрь функции `animaster`. Функция `animaster` должна возвращать объект с методами для запуска разных анимаций. Сделай так, чтобы кнопки на страничке все еще показывали анимацию. +✓ 2. Сейчас функции-анимации (`move`, `fadeIn`, `scale`) лежат в глобальной области видимости и имеют очень простые названия — их легко случайно переопределить. Поэтому спрячь их определения внутрь функции `animaster`. Функция `animaster` должна возвращать объект с методами для запуска разных анимаций. Сделай так, чтобы кнопки на страничке все еще показывали анимацию. Использовать анимации теперь можно будет вот так: @@ -14,22 +14,22 @@ animaster().scale(element, 500, .8) ``` -3. По аналогии с функцией `fadeIn` создай функцию `fadeOut`, скрывающую элемент. +✓ 3. По аналогии с функцией `fadeIn` создай функцию `fadeOut`, скрывающую элемент. -4. Мы хотим, немного облегчить жизнь пользователям анимастера, поэтому напишем для них несколько сложных анимаций. Сложные анимации состоят из нескольких простых, запущенных в правильном порядке и в нужное время. Добавь в интерфейс анимастера три новые анимации. Не забудь сделать так, чтобы с html-странички можно было их запустить. Вот такие анимации должен реализовывать анимастер: +✓ 4. Мы хотим, немного облегчить жизнь пользователям анимастера, поэтому напишем для них несколько сложных анимаций. Сложные анимации состоят из нескольких простых, запущенных в правильном порядке и в нужное время. Добавь в интерфейс анимастера три новые анимации. Не забудь сделать так, чтобы с html-странички можно было их запустить. Вот такие анимации должен реализовывать анимастер: - `moveAndHide` — блок должен одновременно сдвигаться на 100 пикселей вправо и на 20 вниз, а потом исчезать. Метод на вход должен принимать продолжительность анимации. При этом 2/5 времени блок двигается, 3/5 — исчезает. - `showAndHide` — блок должен появиться, подождать и исчезнуть. Каждый шаг анимации длится 1/3 от времени, переданного аргументом в функцию. - `heartBeating` — имитация сердцебиения. Сначала элемент должен увеличиться в 1,4 раза, потом обратно к 1. Каждый шаг анимации занимает 0,5 секунды. Анимация должна повторяться бесконечно. -5. Сделай возможность отключать анимацию сердцебиения: +✓ 5. Сделай возможность отключать анимацию сердцебиения: - сделай, чтобы метод `heartBeating` возвращал объект с методом `stop` - при вызове метода `stop` анимация должна прекращаться (скорее всего, нужно останавливать таймер, которым ты анимацию запускаешь) - добавь в разметку кнопку stop по аналогии с кнопкой play - в коде добавь обработчик клика по аналогии с тем, как это сделано для кнопки play. Обработчик должен вызывать метод `stop` анимации -6. Сделай функции отмены для каждой элементарной операции. Создай служебные (не доступные снаружи анимастера) функции `resetFadeIn`, `resetFadeOut` и `resetMoveAndScale`. Все эти функции принимают на вход элемент, на котором надо сбросить состояние. +✓ 6. Сделай функции отмены для каждой элементарной операции. Создай служебные (не доступные снаружи анимастера) функции `resetFadeIn`, `resetFadeOut` и `resetMoveAndScale`. Все эти функции принимают на вход элемент, на котором надо сбросить состояние. `resetFadeIn` и `resetFadeOut` должны менять обратно css-классы в classList — каждая из них должны убирать все дописанные стили, устанавливая их в `null`: @@ -37,9 +37,9 @@ animaster().scale(element, 500, .8) element.style.заданныйАнимациейСтиль = null; ``` -7. Сделай кнопку reset для анимации `moveAndHide` по аналогии с кнопкой stop из одного из предыдущих пунктов. При нажатии на кнопку reset последующие шаги анимации должны отменяться аналогично stop из `heartBeating`, а элемент должен мгновенно вернуться в исходное состояние с помощью функций из предыдущего пункта. +✓ 7. Сделай кнопку reset для анимации `moveAndHide` по аналогии с кнопкой stop из одного из предыдущих пунктов. При нажатии на кнопку reset последующие шаги анимации должны отменяться аналогично stop из `heartBeating`, а элемент должен мгновенно вернуться в исходное состояние с помощью функций из предыдущего пункта. -8. Сделай так, чтобы анимацию `move` можно было вызвать не только так: +✓8. Сделай так, чтобы анимацию `move` можно было вызвать не только так: ``` animaster().move(element, 500, {x: 20, y: 20}); @@ -67,11 +67,11 @@ animaster().addMove(500, {x: 20, y:20}).play(element); - метод `play` должен принимать элемент, который будем анимировать, в качестве аргумента - метод `play` должен выполнять по очереди все шаги анимации, которые лежат в массиве `this._steps`, применяя их к переданному ему элементу. -9. Сделай так, чтобы метод `move` работал на написанных на предыдущем шаге методах. +✓9. Сделай так, чтобы метод `move` работал на написанных на предыдущем шаге методах. -10. Сделай по аналогии с методом `addMove` методы `addScale`, `addFadeIn` и `addFadeOut`. Перепиши соответствующие анимации, чтобы они переиспользовали эти методы. +✓10. Сделай по аналогии с методом `addMove` методы `addScale`, `addFadeIn` и `addFadeOut`. Перепиши соответствующие анимации, чтобы они переиспользовали эти методы. -11. Теперь у тебя должно быть возможным такое использование: +✓11. Теперь у тебя должно быть возможным такое использование: ``` const customAnimation = animaster() @@ -88,9 +88,9 @@ customAnimation.play(element); В этот момент анимация запускается, элемент перемещается по четырем точкам и меняет размеры. В конце элемент возвращается в исходную точку. Если `customAnimation.play(element)` запустить несколько раз с разными элементами, то анимации должны работать. -12. Перепиши сложные анимации `moveAndHide`, `showAndHide` и `heartBeating` на использование новых методов `add...` и `play`. Чтобы сделать `showAndHide`, добавь метод `addDelay`, добавляющий шаг, в котором ничего не происходит в течение заданного времени. Чтобы сделать `heartBeating`, добавь в `play` в аргументы флаг `cycled`, означающий, что шаги анимации надо повторять бесконечно. +✓12. Перепиши сложные анимации `moveAndHide`, `showAndHide` и `heartBeating` на использование новых методов `add...` и `play`. Чтобы сделать `showAndHide`, добавь метод `addDelay`, добавляющий шаг, в котором ничего не происходит в течение заданного времени. Чтобы сделать `heartBeating`, добавь в `play` в аргументы флаг `cycled`, означающий, что шаги анимации надо повторять бесконечно. -13. Метод `play` должен возвращать объект с методом `stop` (применяется для `heartBeating`) и `reset`. Метод `reset` должен возвращать элемент в исходное состояние (то, которое было до анимации): +✓13. Метод `play` должен возвращать объект с методом `stop` (применяется для `heartBeating`) и `reset`. Метод `reset` должен возвращать элемент в исходное состояние (то, которое было до анимации): - если элемент был скрыт, то скрыть его - если был показан, то показать @@ -98,7 +98,7 @@ customAnimation.play(element); Для этого воспользуйся функциями отмены элементарных операций. -14. Добавь метод `buildHandler`, результат которого можно сразу передать в `addEventListener`: +✓14. Добавь метод `buildHandler`, результат которого можно сразу передать в `addEventListener`: ``` const worryAnimationHandler = animaster() @@ -115,9 +115,9 @@ document Особенность `addEventListener` в том, что если ему во второй параметр передать не стрелочную функцию, то в качестве контекста вызова `this` он передаст ей DOM-элемент с которым происходит событие. -15. \* Реализуй какую-нибудь еще анимацию, которой пока нет. В качестве идей, можешь использовать этот сайт: http://leaverou.github.io/animatable/ Если на этой странице нажать кнопку "Animate All", становится весело =) +✓15. \* Реализуй какую-нибудь еще анимацию, которой пока нет. В качестве идей, можешь использовать этот сайт: http://leaverou.github.io/animatable/ Если на этой странице нажать кнопку "Animate All", становится весело =) -16. \* Сейчас есть проблема, что если написать вот так: +✓16. \* Сейчас есть проблема, что если написать вот так: ``` const a = animaster().addMove(111, {x: 10, y: -10}) diff --git a/index.html b/index.html index 6546475..08e3e55 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,13 @@

fadeIn

+
+
+

fadeOut

+ +
+
+

move

@@ -27,6 +34,36 @@

scale

+
+
+

moveAndHide

+ + +
+
+
+
+
+

showAndHide

+ +
+
+
+
+
+

heartBeating

+ + +
+
+
+
+
+

Star power is unstoppable!

+ +
+
+
diff --git a/index.js b/index.js index 61e55f6..c462c4b 100644 --- a/index.js +++ b/index.js @@ -1,57 +1,294 @@ +let timer; +let y; +let z; + + addListeners(); +function animaster(){ + return new AnimasterClass; +} + +class AnimasterClass{ + constructor(info=[]){ + this._steps = info + }; + fadeIn=function (element, duration) { + element.style.transitionDuration = `${duration}ms`; + element.classList.remove('hide'); + element.classList.add('show'); + }; + + fadeOut=function (element, duration) { + element.style.transitionDuration = `${duration}ms`; + element.classList.remove('show'); + element.classList.add('hide'); + }; + + move=function (element, duration, translation) { + element.style.transitionDuration = `${duration}ms`; + element.style.transform = getTransform(translation, null); + }; + + scale=function(element, duration, ratio) { + element.style.transitionDuration = `${duration}ms`; + element.style.transform = getTransform(null, ratio); + }; + + moveAndHide=function(element, duration, translation) { + element.style.transitionDuration = `${duration*2/5}ms`; + element.style.transform = getTransform(translation, null); + element.classList.remove('show'); + setTimeout(()=>element.classList.add('hide'), duration*3/5); + }; + + showAndHide=function (element, duration) { + element.style.transitionDuration = `${duration/3}ms`; + element.classList.remove('hide'); + element.classList.add('show'); + setTimeout(()=>element.classList.remove('show'), duration/3); + setTimeout(()=>element.classList.add('hide'), duration/3); + }; + + heartBeating=function(element, duration, ratio) { + element.style.transitionDuration = `${duration}ms`; + if (!isNaN(y)){ + clearInterval(y); + clearInterval(z); + } + y = setInterval(()=>element.style.transform = getTransform(null, ratio), duration/2); + z = setInterval(()=>element.style.transform = getTransform(null, 1), duration); + return {stop:() =>{ + clearInterval(y); + clearInterval(z);} + } + }; + + #resetFadeIn=function(element){ + element.style.opacity = null; + element.classList.remove('show'); + element.classList.add('hide'); + }; + + #resetFadeOut=function(element){ + element.style.opacity = null; + element.classList.remove('hide'); + element.classList.add('show'); + }; + + #resetMoveAndScale=function(element){ + element.style.transitionDuration = `${0}ms`; + element.style.transform = null; + }; + moveAndHideReset = function moveAndHideReset(element){ + this.#resetMoveAndScale(element); + this.#resetFadeOut(element); + } + addMove=function(duration, translation){ + this._steps.push({ + duration, + translation, + name: 'move', + rewind:(element)=>{this.#resetMoveAndScale(element)} + }); + return this; + }; + addScale=function(duration, ratio){ + this._steps.push({ + duration, + ratio, + name: 'scale', + rewind:(element)=>{this.#resetMoveAndScale(element)} + }); + return this; + }; + addFadeIn=function(duration){ + this._steps.push({ + duration, + name: 'fadeIn', + rewind:(element)=>{this.#resetFadeIn(element)} + }); + return this; + }; + addFadeOut=function(duration){ + this._steps.push({ + duration, + name: 'fadeOut', + rewind:(element)=>{this.#resetFadeOut(element)} + }); + return this; + }; + addDelay=function(duration){ + this._steps.push({ + duration, + name: 'delay', + rewind:()=>{} + }); + return this; + }; + play=function(element, cycled = false){ + let time = 0; + let allComands = () =>{ + for(let i of this._steps) + switch(i.name){ + case 'move': + console.log('move'); + setTimeout(()=>this.move(element, i.duration, i.translation),(++time)*i.duration); + break; + case 'scale': + console.log('scale'); + setTimeout(()=>this.scale(element, i.duration, i.ratio),(++time)*i.duration); + break; + case 'fadeIn': + console.log('fadeIn'); + setTimeout(()=>this.fadeIn(element, i.duration),(++time)*i.duration); + break; + case 'fadeOut': + console.log('fadeOut'); + setTimeout(()=>this.fadeOut(element, i.duration),(++time)*i.duration); + break; + case 'delay': + console.log('delay'); + setTimeout(()=>console.log('Waited enough'), i.duration) + break; + } + } + if (!isNaN(y)) + clearInterval(y); + + if (cycled) + y = setInterval(()=>allComands(), this._steps.reduce((x,z)=>x.duration+z.duration)); + else + allComands(); + + return { + stop: ()=>clearInterval(y), + reset: something =>{ + clearTimeout(y); + something.style.transitionDuration=null; + this._steps.reverse().forEach(command=>command.rewind(something)) + } + } + }; + buildHandler = function () { + const x = this; + return function() { + x.play(this); + } + } +} +// 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); + function addListeners() { + document.getElementById('fadeInPlay') .addEventListener('click', function () { const block = document.getElementById('fadeInBlock'); - fadeIn(block, 5000); + //animaster().fadeIn(block, 5000); + animaster().addFadeIn(1000).play(block); + }); + + document.getElementById('fadeOutPlay') + .addEventListener('click', function () { + const block = document.getElementById('fadeOutBlock'); + //animaster().fadeOut(block, 5000); + animaster().addFadeOut(1000).play(block); + //customAnimation.play(block); }); document.getElementById('movePlay') .addEventListener('click', function () { const block = document.getElementById('moveBlock'); - move(block, 1000, {x: 100, y: 10}); + //animaster().move(block, 1000, {x: 100, y: 10}); + animaster().addMove(1000, {x: 100, y: 10}).play(block); }); document.getElementById('scalePlay') .addEventListener('click', function () { const block = document.getElementById('scaleBlock'); - scale(block, 1000, 1.25); + //animaster().scale(block, 1000, 1.25); + animaster().addScale(1000, 1.25).play(block); + }); + document.getElementById('moveAndHidePlay') + .addEventListener('click', function () { + const block = document.getElementById('moveAndHideBlock'); + //animaster().moveAndHide(100, {x: 100, y: 20}); + animaster().addMove(400, {x: 100, y: 20}).addFadeOut(600).play(block); + }); + document.getElementById('moveAndHideReset') + .addEventListener('click', function () { + const block = document.getElementById('moveAndHideBlock'); + animaster().moveAndHideReset(block); + }); + document.getElementById('showAndHidePlay') + .addEventListener('click', function () { + const block = document.getElementById('showAndHideBlock'); + //animaster().showAndHide(block, 5000); + animaster().addFadeIn(1000).addDelay(1000).addFadeOut(1000).play(block); + }); + document.getElementById('heartBeatingPlay') + .addEventListener('click', function () { + const block = document.getElementById('heartBeatingBlock'); + //timer = animaster().heartBeating(block, 500, 1.4); + timer=animaster().addScale(500, 1.4).addDelay(500).addScale(500, 1).addDelay(500).play(block, true); + }); + document.getElementById('heartBeatingStop') + .addEventListener('click', function () { + timer.stop(); + }); + document.getElementById('CustomPlay') + .addEventListener('click', function () { + const block = document.getElementById('CustomBlock'); + animaster().addMove(250, {x: 200, y: 0}) + .addMove(250, {x: 0, y: 100}) + .addMove(250, {x: 100, y: -100}) + .addMove(250, {x: 200, y: 100}) + .addMove(250, {x: 0, y: 0}).play(block, true); }); } -/** - * Блок плавно появляется из прозрачного. - * @param element — HTMLElement, который надо анимировать - * @param duration — Продолжительность анимации в миллисекундах - */ -function fadeIn(element, duration) { - element.style.transitionDuration = `${duration}ms`; - element.classList.remove('hide'); - element.classList.add('show'); -} -/** - * Функция, передвигающая элемент - * @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); -} -/** - * Функция, увеличивающая/уменьшающая элемент - * @param element — HTMLElement, который надо анимировать - * @param duration — Продолжительность анимации в миллисекундах - * @param ratio — во сколько раз увеличить/уменьшить. Чтобы уменьшить, нужно передать значение меньше 1 - */ -function scale(element, duration, ratio) { - element.style.transitionDuration = `${duration}ms`; - element.style.transform = getTransform(null, ratio); -} +// /** +// * Блок плавно появляется из прозрачного. +// * @param element — HTMLElement, который надо анимировать +// * @param duration — Продолжительность анимации в миллисекундах +// */ +// function fadeIn(element, duration) { +// element.style.transitionDuration = `${duration}ms`; +// element.classList.remove('hide'); +// element.classList.add('show'); +// } + +// /** +// * Функция, передвигающая элемент +// * @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); +// } + +// /** +// * Функция, увеличивающая/уменьшающая элемент +// * @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 getTransform(translation, ratio) { const result = [];