Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Очередь анимации в цикличном requestAnimationFrame (https://javascript.ru/forum/misc/85608-ochered-animacii-v-ciklichnom-requestanimationframe.html)

Raadsert 08.11.2023 08:59

Очередь анимации в цикличном requestAnimationFrame
 
Подскажите, как можно реализовать очередь для анимации если используется цикличный requestAnimationFrame?

function animate() {
    render();
    requestAnimationFrame(animate);
}

function render() {
    // код для анимации каких ни будь элементов
}

animate()

voraa 08.11.2023 09:23

Во-первых в функции animate должна быть проверка на то, что анимация должна остановиться. Т.е. вызывать requestAnimationFrame больше не нужно, иначе получится бесконечный цикл.
Во-вторых у функции animate есть параметр - время. Его надо использовать, что бы узнавать время от начала анимации или от предыдущего вызова animate, что бы рассчитывать какие произвести изменения.

Условная схема такая
let tbeg;
let tlast;
 
function animate(t) {
	tbeg ??= t;
	tlast ?? = t
	const dtbeg = t - tbeg; // прошло от начала анимации
	const dtlast = t - tlast; // прошло от прошлой анимации
    render();  // расчет и внесение изменений с учетом dtbeg и/или dtlast
    tlast = t;
    if (какое то условие окончания) return;  // Завершить анимацию?
    requestAnimationFrame(animate);
    
}
 
function render() {
    // код для анимации каких ни будь элементов
}

animate(performance.now())


Примеры можно посмотреть тут
https://developer.mozilla.org/en-US/...AnimationFrame

Raadsert 08.11.2023 11:32

Цитата:

Сообщение от voraa (Сообщение 553917)
Во-первых в функции animate должна быть проверка на то, что анимация должна остановиться. Т.е. вызывать requestAnimationFrame больше не нужно, иначе получится бесконечный цикл.
Во-вторых у функции animate есть параметр - время. Его надо использовать, что бы узнавать время от начала анимации или от предыдущего вызова animate, что бы рассчитывать какие произвести изменения.

Условная схема такая
let tbeg;
let tlast;
 
function animate(t) {
	tbeg ??= t;
	tlast ?? = t
	const dtbeg = t - tbeg; // прошло от начала анимации
	const dtlast = t - tlast; // прошло от прошлой анимации
    render();  // расчет и внесение изменений с учетом dtbeg и/или dtlast
    tlast = t;
    if (какое то условие окончания) return;  // Завершить анимацию?
    requestAnimationFrame(animate);
    
}
 
function render() {
    // код для анимации каких ни будь элементов
}

animate(performance.now())


Примеры можно посмотреть тут
https://developer.mozilla.org/en-US/...AnimationFrame

Во первых, в Three.js анимация не останавливается, цикл отрисовки идёт бесконечно (возможно за исключением тех моментов когда окно сайта не активно). Во вторых, а как сделать то очередь из функций для анимации в таком цикле? Если к примеру, нужно чтоб после завершения одной анимации, начиналась другая. Разумеется не останавливая цикла и не создавая новый.

voraa 08.11.2023 11:44

Если вы используете Three.js, то и пользуйтесь тем, что он дает.
Цикл отрисовки вообще не останавливается. Каждые 1/60 сек (при частоте экрана 60гц) происходит перерисовка экрана с учетом тех изменений, которые были внесены (если браузер не занят орбаботкой длинной задачи).
requestAnimationFrame просто дает возможность внести изменения перед перерисовкой с учетом времени.
Он представляет базовый уровень, на основе которого можно сделать что угодно, но это именно надо делать самому.
Вызовов requestAnimationFrame может быть много (много циклов). И когда наступает время перерисовки страницы, вызываются все функции, зарегистрированные к этому моменту.

Raadsert 08.11.2023 11:59

Цитата:

Сообщение от voraa (Сообщение 553923)
Если вы используете Three.js, то и пользуйтесь тем, что он дает.
Цикл отрисовки вообще не останавливается. Каждые 1/60 сек (при частоте экрана 60гц) происходит перерисовка экрана с учетом тех изменений, которые были внесены (если браузер не занят орбаботкой длинной задачи).
requestAnimationFrame просто дает возможность внести изменения перед перерисовкой с учетом времени.
Он представляет базовый уровень, на основе которого можно сделать что угодно, но это именно надо делать самому.
Вызовов requestAnimationFrame может быть много (много циклов). И когда наступает время перерисовки страницы, вызываются все функции, зарегистрированные к этому моменту.

Но увеличение количества циклов сильно снижают производительность. Даже тот же Three.js, писался на нативном js. Вот мне и интересно, как можно реализовать очередь анимаций в постоянном цикле.

voraa 08.11.2023 12:04

Цитата:

Сообщение от Raadsert
Но увеличение количества циклов сильно снижают производительность.

Ничего они сильно не снижают. Просто перед перерисовкой будут вызываться несколько простых функций, каждая отвечающая за свое изменение анимации, вместо одной сложной, которая делает все изменения.

Зачем постоянный цикл? Если нет изменений, то и вызывать ничего не надо. Когда есть какие то изменения, то для них запускается цикл requestAnimationFrame

Цитата:

Сообщение от Raadsert
Даже тот же Three.js, писался на нативном js

А на чем еще можно?

Raadsert 08.11.2023 15:45

Цитата:

Сообщение от voraa (Сообщение 553925)
Ничего они сильно не снижают. Просто перед перерисовкой будут вызываться несколько простых функций, каждая отвечающая за свое изменение анимации, вместо одной сложной, которая делает все изменения.

Зачем постоянный цикл? Если нет изменений, то и вызывать ничего не надо. Когда есть какие то изменения, то для них запускается цикл requestAnimationFrame

А на чем еще можно?

Хорошо, тогда давайте будем отталкиваться от Three.js. Не знаете как можно сделать очередь анимаций для Three.js?

MallSerg 09.11.2023 18:36

>>Хорошо, тогда давайте будем отталкиваться от Three.js. Не знаете как можно сделать очередь анимаций для Three.js?

Так же как и всегда запускать следующую анимацию после окончания предыдущей и так в порядке очереди.
Возможно у тебя возникли сложности с определением события "конец анимации"?.

Как пример простого вызова последовательных анимаций мой пост 10ти летней давности =).
https://javascript.ru/forum/misc/496...tml#post326928

voraa 10.11.2023 09:32

Цитата:

Сообщение от MallSerg
Как пример простого вызова последовательных анимаций мой пост 10ти летней давности =).
https://javascript.ru/forum/misc/496...tml#post326928

Немного упростил и осовременил (с Promise и async/await)
<div id="moved" style="position: absolute;width:30px;height: 30px;background: red;"></div>
    <br>
    <input type="button" onclick="but1()" value="кнопка 1"> <br>
    
    <script type="text/javascript">
function but1 (){
    const element = document.getElementById("moved");
    animateMove( element , 800 , 100 , 1500)
	.then ( () => animateMove( element , -750 , 10 , 200) )
	.then ( () => animateMove( element , 150 , -60 , 1000) )
	.then ( () => {    
			element.style.background = ("#" + ( Math.random() * (999-100)+100 )).substr(0,4) ;
			element.style.top = "30px";
			element.style.left = "100px";
		})
}

/*  С async/await  
async function but1 (){
    const element = document.getElementById("moved");
    await animateMove( element , 800 , 100 , 1500);
    await animateMove( element , -750 , 10 , 200);
    await animateMove( element , 150 , -60 , 1000);
    
    element.style.background = ("#" + ( Math.random() * (999-100)+100 )).substr(0,4) ;
    element.style.top = "30px";
    element.style.left = "100px";
}
*/

function animateMove ( element , x , y , time = 1000){
    const statrX = element.offsetLeft;
    const startY = element.offsetTop;
    const startTime = performance.now();
    return new Promise ( (resolve) => {
		const animate = function (timenow){
			const EA = (timenow - startTime) / time;
			if (EA > 1) {
				resolve(); 
			} else {
				element.style.left = startX + (x * EA) + "px";
				element.style.top = startY + (y * EA) + "px";
				requestAnimationFrame(animate );
			}
		};
		animate(startTime);
    })
}

</script>

Raadsert 10.11.2023 14:34

Я имею ввиду несколько другое понятие очереди. Например этот же код (я его немного исправил), если несколько раз нажимать на кнопку - анимация будет воспроизводится по новой. А вот каким образом сделать так, чтоб каждое нажатие на кнопку ставило запуск новой функции анимации в некую очередь, и анимация воспроизводилась одна после другой?

1 нажатие на кнопку => animate()
2 нажатие на кнопку => animate() animate()
3 нажатие на кнопку => animate(), animate(), animate()
(при условии что анимация вызванная первым нажатием ещё не завершилась)



<div id="moved" style="position: absolute;width:30px;height: 30px;background: red; pointer-events: none;"></div>
    <br>
    <input type="button" onclick="but1()" value="кнопка 1"> <br>

    <script type="text/javascript">
        function but1 (){
            const element = document.getElementById("moved");
            animateMove( element , 500 , 100 , 1500)
        }

        /*  С async/await
        async function but1 (){
            const element = document.getElementById("moved");
            await animateMove( element , 800 , 100 , 1500);
            await animateMove( element , -750 , 10 , 200);
            await animateMove( element , 150 , -60 , 1000);

            element.style.background = ("#" + ( Math.random() * (999-100)+100 )).substr(0,4) ;
            element.style.top = "30px";
            element.style.left = "100px";
        }
        */

        function animateMove ( element , x , y , time = 1000){
            const startX = element.offsetLeft;
            const startY = element.offsetTop;
            const startTime = performance.now();
            return new Promise ( (resolve) => {
                const animate = function (timenow){
                    const EA = (timenow - startTime) / time;
                    if (EA > 1) {
                        resolve();
                    } else {
                        element.style.translate = `${startX + (x * EA)}px ${startY + (y * EA)}px`;
                        requestAnimationFrame(animate);
                    }
                };
                animate(startTime);
            })
        }

    </script>


Часовой пояс GMT +3, время: 04:21.