Easing и плавность
Когда цыплёнок не прыгает рывком, а мягко подъезжает к цели и сам притормаживает в конце — это и есть easing, главный секрет приятной анимации.
Главная идея урока: чтобы движение было плавным, не телепортируй объект к цели и не двигай его на одинаковый шаг. Каждый кадр сдвигай его на часть оставшегося расстояния до цели. Чем ближе — тем меньше шаг, тем мягче торможение.
Зачем тебе easing
Открой любое приложение на телефоне и смахни экран. Заметил? Список не останавливается мгновенно, а проезжает чуть дальше и плавно тормозит. Нажми на кнопку в игре — она не дёргается, а мягко увеличивается и оседает. Открой меню — оно не появляется рывком, а будто подъезжает на место. Всё это движение живое именно потому, что оно не резкое. За этой мягкостью стоит один простой приём, и сегодня ты научишь ему своего цыплёнка.
В прошлом уроке про переменные состояния мы уже двигали CodeChick по холсту: хранили его координату в переменной и каждый кадр прибавляли к ней постоянную скорость. Цыплёнок ехал — но ехал как робот: ровно, механически, с одинаковой скоростью от старта до самого конца, а потом резко вставал. Сегодня мы исправим именно это. Научимся, чтобы цыплёнок разгонялся мягко и сам притормаживал, подъезжая к точке так, будто у него есть инерция.
К концу урока ты соберёшь скетч, где CodeChick живёт в центре холста, а как только ты задашь ему новую цель — он плавно, с замедлением подкатывается к ней и аккуратно останавливается. Никаких рывков. И всё это — буквально одной строчкой формулы. Звучит как магия дорогих приложений, но на деле это арифметика уровня шестого класса. Давай разбираться.
Кстати, у этого приёма есть и практический бонус, не только красота. Резкое движение тяжело воспринимать глазами: когда объект прыгает рывком, мозг на долю секунды теряет его и заново ищет, где он теперь. Плавное движение глаз отслеживает легко — он просто ведёт цыплёнка по дуге. Поэтому easing делают везде, где важно, чтобы человеку было комфортно: в переключении вкладок, в подсказках, в анимации лайка под постом. Ты не просто учишься рисовать красиво — ты учишься говорить с глазом зрителя на его языке.
Что такое easing
Метафора: половина пути до двери
Представь, что ты стоишь в коридоре, а в десяти метрах от тебя — дверь. И у тебя странное правило: каждый шаг ты проходишь ровно половину оставшегося до двери расстояния. Первый шаг — 5 метров (полпути). Второй — 2,5 метра (половина остатка). Третий — 1,25. Дальше шаги становятся всё мельче и мельче: ты несёшься в начале и почти ползёшь у самой двери, мягко к ней притираясь. Ты никогда не врежешься в неё рывком — ты к ней подкрадываешься.
Easing (произносится «и́зинг») — это приём плавного движения, когда объект каждый кадр приближается к цели на часть оставшегося расстояния и за счёт этого замедляется к концу.
Вот почему это так приятно глазу: в природе ничто не стартует и не тормозит мгновенно. Мяч, который ты катишь по полу, сначала катится быстро, а потом всё медленнее, пока не замрёт. Машина у светофора не встаёт в стену — она плавно гасит скорость. Easing воспроизводит ровно это ощущение замедления — и поэтому анимация с ним выглядит «дорого» и живо, а без него — дёргано и дёшево.
Чем это отличается от постоянной скорости
В прошлом уроке мы двигали цыплёнка так: x = x + 3;. Это значит «каждый кадр сдвигайся ровно на 3 пикселя». Шаг тут фиксированный — что в начале пути, что у самой цели цыплёнок идёт с одной и той же скоростью, как заводная игрушка. А когда он добирается до цели, останавливать его приходится вручную: писать if, ловить момент, иначе он просто проедет мимо и улетит за край.
Easing переворачивает эту идею. Шаг здесь не фиксированный, а пропорциональный — он зависит от того, как далеко осталось ехать. Сравни две строки внимательно:
x = x + 3;— шаг всегда 3, скорость постоянная, остановки нет.x = x + (targetX - x) * 0.1;— шаг каждый раз свой: большой вдалеке, крошечный у цели.
Эта маленькая разница в формуле и даёт всё ощущение жизни. Постоянная скорость — это движение робота по рельсам. Easing — это движение чего-то живого, у чего есть вес и нежелание врезаться в стену. Запомни этот контраст: он объясняет, зачем мы вообще усложняем простое сложение.
Формула плавного приближения
Вся магия умещается в одну строку. У нас есть текущая позиция объекта x и цель targetX, к которой он стремится. Каждый кадр делаем так:
x = x + (targetX - x) * 0.1;Разберём по кусочкам, потому что это сердце всего урока:
targetX - x— это оставшееся расстояние до цели. Если цыплёнок далеко, число большое; если близко — маленькое; если уже на месте — ноль.* 0.1— берём от этого расстояния только десятую часть. Это и есть наш «шаг половиной пути», только не половина, а одна десятая.x = x + ...— прибавляем этот кусочек к текущей позиции, сдвигая цыплёнка чуть ближе к цели.
Фокус в том, что расстояние targetX - x пересчитывается каждый кадр заново. Пока цыплёнок далеко, десятая часть большого расстояния — это большой шаг, он мчится. По мере приближения расстояние тает, десятая часть от него становится крошечной — и цыплёнок сам собой замедляется. Никаких отдельных «команд тормозить» писать не нужно: торможение встроено в саму формулу.
Разбираем на примерах
Пример 1: цыплёнок едет к центру
Начнём с простого. Цыплёнок стартует у левого края, а его цель — центр холста. Смотри, как он подъезжает.
let x = 20; // стартовая позиция цыплёнка
let targetX = 200; // куда он едет (центр)
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(135, 206, 235);
// easing: сдвигаемся на 1/10 оставшегося пути
x = x + (targetX - x) * 0.1;
// тело CodeChick
fill(255, 209, 64);
circle(x, 200, 80);
// клюв
fill(255, 140, 30);
triangle(x + 30, 195, x + 55, 200, x + 30, 210);
}Результат: на голубом небе жёлтый цыплёнок стартует у левого края и быстро срывается вправо, к центру. По мере приближения к центру он заметно замедляется и мягко замирает ровно в точке x = 200, не дёргаясь и не проскакивая мимо. Движение похоже на то, как закрывается ящик с доводчиком — сначала бодро, потом нежно.
Обрати внимание: в коде нигде нет команды «остановись». Цыплёнок тормозит сам, потому что чем ближе он к 200, тем меньше становится (targetX - x), а значит и шаг. Это и есть красота easing — ты описываешь не само движение по шагам, а лишь стремление к цели.
Пример 2: коэффициент easing — крутилка плавности
Число 0.1 в формуле — это и есть та самая «крутилка» мягкости. Программисты называют его коэффициентом easing. Он всегда между 0 и 1, и от него зависит характер движения:
| Коэффициент | Что получаем |
| 0.02 | очень медленно, лениво, долгий подъезд |
| 0.1 | мягко и плавно — золотая середина |
| 0.3 | бодро, быстро, чуть резче |
| 1.0 | телепорт: сразу в цель, без плавности |
Давай вынесем коэффициент в отдельную переменную, чтобы им было удобно играть:
let x = 20;
let targetX = 320;
let ease = 0.08; // крутилка плавности: меняй меня!
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(135, 206, 235);
x = x + (targetX - x) * ease;
fill(255, 209, 64);
circle(x, 200, 80);
}Результат: цыплёнок плавно подъезжает к правой части холста. При ease = 0.08 подъезд получается заметно неторопливым и очень мягким. Поменяй число на 0.3 — и цыплёнок домчится почти мгновенно, с лёгким торможением в конце; поставь 0.02 — и он поползёт томно, словно нехотя.
Поиграй с этим числом сам. Это лучший способ почувствовать, что коэффициент — не «правильное значение из учебника», а художественный выбор. Для меню в приложении берут одно, для летящего снаряда в игре — другое. У тебя теперь есть ручка громкости для плавности.
Пример 3: резко против плавно — сравнение бок о бок
Чтобы разница врезалась в память, нарисуем двух цыплят разом. Верхний прыгает к цели рывком (как телепорт), нижний подъезжает плавно через easing. Цель у обоих — туда, куда ты кликнул мышью по горизонтали.
let easeX = 200; // плавный цыплёнок
let targetX = 200; // общая цель по клику
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(135, 206, 235);
// ВЕРХНИЙ: резко — сразу в цель, без easing
fill(230, 90, 90);
circle(targetX, 130, 70);
// НИЖНИЙ: плавно — через формулу easing
easeX = easeX + (targetX - easeX) * 0.1;
fill(255, 209, 64);
circle(easeX, 270, 70);
}
function mousePressed() {
targetX = mouseX; // новая цель — туда, куда кликнули
}Результат: на холсте два цыплёнка друг под другом. Когда ты кликаешь мышью, красный верхний мгновенно перескакивает на новую горизонтальную позицию — телепортом, рывком. Жёлтый нижний в тот же момент начинает плавно ехать к той же точке, разгоняется и мягко тормозит, прибывая на место с лёгким запозданием. Разница видна сразу: верхний дёргается, нижний живёт.
Этот пример — главный аргумент урока. Один и тот же код целей, одна и та же цель — но плавный вариант ощущается в разы приятнее. Именно так разработчики игр и приложений делают интерфейс «вкусным»: добавляют easing там, где наивный код просто перескочил бы.
Пример 4: easing работает не только с координатами
Вот что важно понять: формула плавного приближения ничего не знает про «позицию». Она просто мягко тянет одно число к другому. А раз так — этим числом может быть что угодно: размер, прозрачность, угол поворота, цвет. Давай заставим цыплёнка плавно раздуваться, когда ты наводишь на него мышь, и так же мягко сдуваться обратно, когда уводишь.
let size = 80; // текущий размер тела
let targetSize = 80; // желаемый размер
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(135, 206, 235);
// если курсор близко к центру — цель больше
let d = dist(mouseX, mouseY, 200, 200);
if (d < 80) {
targetSize = 160; // раздуться
} else {
targetSize = 80; // обычный размер
}
// easing того же вида, но для размера
size = size + (targetSize - size) * 0.1;
fill(255, 209, 64);
circle(200, 200, size);
}Результат: жёлтый цыплёнок сидит в центре. Когда ты подводишь мышь к нему, он не дёргается, а мягко и упруго раздувается почти вдвое, будто радостно надувается. Уводишь курсор — он так же плавно сдувается до обычного размера. Получается приятный «отклик», как у кнопок в хороших приложениях.
Заметь: строка easing size = size + (targetSize - size) * 0.1; устроена точь-в-точь как для координаты — просто буквы другие. Это и есть сила приёма: один раз понял формулу — и применяешь её к любому числу, которое хочешь оживить.
Частые ошибки и подводные камни
Перепутал порядок:
x - targetXвместоtargetX - x. Если вычесть наоборот, знак расстояния перевернётся, и цыплёнок поедет прочь от цели, улетая за край холста. Запомни направление: «куда хочу минус где я сейчас».Коэффициент больше 1 или отрицательный. Easing-коэффициент живёт строго между 0 и 1. Поставишь 1.5 — цыплёнок будет проскакивать цель и дёргаться туда-сюда, как на пружине; поставишь отрицательное — улетит назад. Держись диапазона от 0 до 1.
Забыл, что цель должна меняться. Если
targetXзадан раз и навсегда и нигде не обновляется, цыплёнок один раз доедет до неё и навсегда замрёт — анимации больше не будет. Чтобы он снова поехал, цель надо менять (по клику, по таймеру, случайно).Ждёшь, что объект «доедет точно» и остановится насовсем. Формально
xприближается к цели бесконечно: шаги становятся микроскопическими, но строго нулём не становятся. На глаз это незаметно — разница в тысячные доли пикселя. Но если тебе важно поймать момент «приехал», сравнивай не на равенство, а на близость:if (abs(targetX - x) < 0.5).Поставил easing-формулу, но всё равно дёргается. Чаще всего причина — забытый
background()в началеdraw()(тогда копятся старые кадры) или слишком большой коэффициент вроде 0.9, при котором плавности почти нет. Проверь обе вещи.
Мини-практика: цыплёнок гоняется за мышью
Собери свой скетч, где CodeChick плавно следует за курсором — это классическое и очень залипательное упражнение. План такой:
- Заведи переменные
xиyдля позиции цыплёнка (стартуй из центра, 200 и 200). - В
draw()каждый кадр делай easing по обеим осям: цель по x — этоmouseX, цель по y — этоmouseY. Получится две строки:x = x + (mouseX - x) * 0.1;и такая же дляy. - Нарисуй цыплёнка в точке
(x, y): жёлтое тело и оранжевый клюв. - Поэкспериментируй с коэффициентом: сделай 0.05 — цыплёнок будет лениво догонять мышь с запозданием, как на резинке; сделай 0.5 — будет почти приклеен к курсору.
Когда заработает, попробуй добавить «хвост»: убери background() или сделай его полупрозрачным (background(135, 206, 235, 40)), и за цыплёнком потянется мягкий шлейф из прошлых кадров. Это уже почти спецэффект из настоящей игры — а в основе всё та же одна строчка easing.
Если захочется пойти дальше — добавь цыплёнку живой клюв: пусть его размер тоже плавно меняется через easing, когда курсор близко, как в четвёртом примере. Совмести две идеи в одном скетче: цыплёнок и догоняет мышь, и реагирует на неё размером. Когда у объекта плавно меняется сразу несколько свойств, он начинает выглядеть по-настоящему живым — и всё это собрано из одной и той же простой формулы, просто применённой к разным числам.
Итоги
Сегодня ты освоил приём, который отличает живую анимацию от деревянной:
- Easing — это движение на часть оставшегося расстояния каждый кадр: чем ближе к цели, тем мельче шаг, тем мягче торможение.
- Вся суть — в одной формуле:
x = x + (targetX - x) * ease;, гдеtargetX - xпересчитывается каждый кадр. - Коэффициент
ease(от 0 до 1) — это крутилка плавности: меньше — ленивее и мягче, ближе к 1 — резче и быстрее. - Торможение получается само собой, без отдельных команд: оно встроено в то, что расстояние тает по мере приближения.
Теперь CodeChick умеет двигаться так, будто у него есть вес и инерция — мягко разгоняться и аккуратно подъезжать к цели. Это огромный шаг: твоя анимация перестала быть механической. Дальше будет ещё интереснее — в следующем уроке мы займёмся отскоком и упругостью: научим цыплёнка прыгать так, чтобы он отталкивался от стенок холста и пружинил. Easing и отскок вместе превратят его в настоящего живого героя. Увидимся на следующей странице!