Страницы

Поиск по вопросам

воскресенье, 12 января 2020 г.

Подсчет шагов разных размеров - для более натуральной анимации Canvas

#java #android #анимация #animate


Здесь на русском StackOverflow я получил очень хороший совет и смог добавить анимацию
фишек в буквенную игру для Android.

Когда пользователь выбирает в меню "Вернуть буквы" и буква "A" должна прилететь в
позицию внизу экрана, показанную красной стрелкой:



Нынешний алгоритм работает так:


Текущие координаты фишки: mRect.top и mRect.left
Конечные координаты фишки: mTarget.x и mTarget.y
Пока на нем есть движущиеся фишки, экран мобильного приложения рисуется заново (для
этого снова и снова вызывается postInvalidate(30)).


Когда нужно анимированно переместить фишку, я вызываю следующий метод, который делит
предстоящий путь на 8 одинаковых шагов:

public void startAnimatedMove(float x, float y) {
    mTarget.x = x;
    mTarget.y = y;
    steps = 8;
    mStepX = (mTarget.x - mRect.left) / steps;
    mStepY = (mTarget.y - mRect.top) / steps;
}


Каждый раз, по истечении 30 миллисекунд, вызывается метод, сдвигающий фишку в нужном
направлении (причем последний шаг у меня особый - он помещает фишку точно на mTarget.x
и mTarget.y - чтобы избежать ошибки округления):

private void nextStep() {
    if (--steps == 0)
        moveTo(mTarget.x, mTarget.y);
    else 
        moveTo(mRect.left + mStepX, mRect.top + mStepY);
}


Этот алгоритм работает хорошо (и дает возможность анимировать фишки прямо из главного
thread-а приложения), но так как шаги mStepX и mStepY одинаковые, фишка движется неестественно.

Вопрос:

Как бы сделать шаги неодинаковыми? Они дожны быть большими в середине пути - и маленькими
в начале и конце (см. зеленый график наверху справа). Тогда фишка будет медленно начинать
и заканчивать движение - так называемый "easying in out".

То есть нужна функция (с использованием x*x или Math.sin()?) для каждой оси, которая
получала бы как input номер текушего шага, а выдавала как output дистанцию в пикселях
вдоль оси икс или игрек:

private void nextStep() {
    if (--steps == 0)
        moveTo(mTarget.x, mTarget.y);
    else 
        moveTo(mRect.left + calcStepX(steps), mRect.top + calcStepY(steps));
}


Обновление:

Нашел интересный сайт про Easing Equations by Robert Penner, но пока не разобрался,
как применить их к своему программному коду - особенно как рассчитать шаги, чтобы их
сумма попала в точку назначения...
    


Ответы

Ответ 1



как расчитать шаги, чтобы их сумма попала в точку назначения Все формулы на упомянутом сайте опираются на 4 параметра: t, b, c, d: t – текущее время (или номер кадра), b – начальное значение, c – (конечное значение - начальное значение), d – общая длительность анимации. Чтобы за 8 кадров некий параметр, напр., Y, изменился от 162 до 288, вы в понравившуюся формулу, напр., в экспонениальный out return c * ( -Math.pow( 2, -10 * t/d ) + 1 ) + b; подставляете b,c вашей анимации, d=7, а t=просчитываемый кадр (0..7) и получаете на выходе значение вашего параметра для этого кадра.

Ответ 2



Все гораздо проще. В android'е есть класс ValueAnimator Пользоваться примерно так: ValueAnimator va = ValueAnimator.ofInt(0, height); va.setDuration(300); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { Integer value = (Integer) animation.getAnimatedValue(); v.getLayoutParams().height = value.intValue(); v.requestLayout(); } }); Дефолтный интерполятор вычисляет значения именно так как вы хотите, т.е. начало и конец медленно, в середине быстрее. Вы естественно можете создать свой интерполятор. А если в интерполятор передать null, то будет линейно, как в вашем примере. ЗЫ Если надо анимировать два или больше независимых (т.е. когда dx через dy не посчитать) параметров можно воспользоваться AnimatorSet

Комментариев нет:

Отправить комментарий