Тач и адаптив для телефона
Твой цыплёнок уже бегает за мышью на компьютере — а теперь научится жить в телефоне, где вместо курсора палец, а вместо большого окна узкий экран.
Главное правило урока: чтобы скетч был играбельным на телефоне, нужно две вещи — ловить касания экрана (а не только мышь) и подстраивать размер холста под окно через
windowWidthиwindowHeight.
Зачем это вообще нужно
Представь: ты сделал классный скетч с CodeChick, скинул ссылку другу — а он открывает её с телефона. И тут начинается грусть. Холст 400×400 торчит куда-то вбок, половина уезжает за край экрана, приходится щипать пальцами и скроллить. А когда друг тыкает в цыплёнка пальцем — ничего не происходит, ведь у телефона нет мыши. Скетч, который на компьютере выглядел живым, на телефоне превращается в обрезок картинки, который даже толком не потрогать.
А ведь почти все твои друзья откроют ссылку именно с телефона. TikTok, мемы, игры, музыка — всё это давно живёт в кармане, а не за монитором. Если твоя работа не работает на телефоне, считай, её половина людей вообще не увидит по-человечески. И дело не только в друзьях: даже если ты делаешь скетч для себя, приятно, когда его можно открыть с любого устройства под рукой, а не объяснять «запускай только с компьютера, иначе всё кривое».
Хорошая новость: чтобы сделать скетч мобильным, не нужно переписывать его с нуля. Те же фигуры, те же цвета, тот же цыплёнок — меняются буквально пара строк про размер холста и пара новых функций про касание. Это финальный штрих, который превращает домашнюю поделку в работу, которой не стыдно поделиться.
В прошлом уроке про мышь и mouseX/mouseY ты заставил цыплёнка следить за курсором. Сегодня мы сделаем тот же мир по-настоящему мобильным: холст растянется на весь экран телефона, а цыплёнок начнёт реагировать на палец так же, как раньше реагировал на мышь. К концу урока у тебя будет сцена, которую можно открыть на любом телефоне в полный экран и тыкать пальцем — и цыплёнок будет послушно прыгать туда, куда ты ткнул.
Звучит как много работы? На самом деле это буквально несколько новых функций. Давай разберёмся.
Холст под размер экрана: windowWidth и windowHeight
Проблема фиксированного размера
Когда ты пишешь createCanvas(400, 400), ты прибиваешь холст гвоздями к размеру 400 на 400 пикселей. На широком мониторе это нормально. Но экран телефона узкий и высокий — например, 390 пикселей в ширину и 800 в высоту. Холст 400 в него уже не влезает по ширине, и браузер показывает горизонтальную полосу прокрутки. Выглядит криво.
Решение простое: не задавать размер числом, а спросить у браузера, какого размера сейчас окно, и сделать холст ровно таким. Для этого у p5.js есть две встроенные переменные — как mouseX и mouseY, только про окно:
windowWidth— ширина окна браузера в пикселях прямо сейчас;windowHeight— высота окна браузера в пикселях прямо сейчас.
Метафора: представь, что ты раскладываешь плед на полу. Можно отрезать кусок ткани заранее «на глаз» — и он окажется то мал, то велик. А можно сначала посмотреть на комнату и раскроить плед ровно по полу. windowWidth и windowHeight — это как взгляд на комнату: ты узнаёшь реальный размер места и кроишь холст точно под него.
Холст на весь экран
function setup() {
createCanvas(windowWidth, windowHeight);
noStroke();
}
function draw() {
background(135, 206, 235); // небо на весь экран
fill(255, 209, 64);
circle(width / 2, height / 2, 120); // цыплёнок ровно по центру
}Результат: голубое небо заполняет весь экран целиком, без полей и полос прокрутки, а жёлтый цыплёнок сидит ровно в центре — и на узком телефоне, и на широком ноутбуке. Размер холста сам подстроился под устройство.
Разберём по шагам. createCanvas(windowWidth, windowHeight) создаёт холст ровно такого размера, какое сейчас окно. Дальше мы рисуем цыплёнка не в точке (200, 200), как раньше, а в width / 2, height / 2 — то есть в середине холста. Тут важная деталь: width и height — это встроенные переменные p5.js с реальными размерами холста. Раз холст занял весь экран, width / 2 — это его центр на любом устройстве. Если бы мы написали circle(200, 200, 120), цыплёнок на большом экране уехал бы в левый верхний угол.
Запомни: когда холст подстраивается под экран, забудь про числа-координаты вроде 200 или 400 и считай всё от
widthиheight. Центр — этоwidth / 2, правый край —width, низ —height.
Когда экран повернули: windowResized()
На телефоне есть ещё одна засада. Человек повернул телефон из вертикального положения в горизонтальное — окно стало другим: было узкое и высокое, стало широкое и низкое. А наш холст создался один раз в setup() и о повороте не знает. Получится холст не того размера.
Чтобы холст подстраивался при изменении окна, у p5.js есть специальная функция windowResized(). Она работает как setup() или draw(): ты её просто объявляешь, а p5.js сам вызывает её каждый раз, когда окно меняет размер (поворот телефона, изменение размера окна на компьютере).
function setup() {
createCanvas(windowWidth, windowHeight);
noStroke();
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight); // перекроить холст под новое окно
}
function draw() {
background(135, 206, 235);
fill(255, 209, 64);
circle(width / 2, height / 2, 120);
}Результат: цыплёнок всё так же сидит по центру неба. Но теперь, если повернуть телефон, холст мгновенно перекраивается под новую форму экрана, и цыплёнок остаётся точно в середине — без обрезанных краёв и пустых полей.
Здесь главное — функция resizeCanvas(windowWidth, windowHeight). Она не создаёт новый холст, а меняет размер уже существующего. Внутри windowResized() мы говорим: «окно изменилось — подгони холст под свежие windowWidth и windowHeight». Поскольку мы рисуем всё от width и height, картинка сама встаёт на место.
Касания: палец вместо мыши
Чем мышь отличается от тача
Теперь самое интересное. У компьютера один курсор, и он всегда где-то есть на экране — даже когда ты ничего не нажимаешь. У телефона курсора нет вообще: пока палец не коснулся стекла, телефон не знает, где ты «навёл». Касание появляется ровно в тот момент, когда палец прижался, и исчезает, как только ты его отпустил.
Вот ключевая разница, которую важно прочувствовать:
| Мышь | Тач (палец) |
| Курсор есть всегда, можно «навести» не нажимая | Координаты есть только пока палец на экране |
| Один указатель | Может быть несколько пальцев сразу |
mouseX, mouseY | массив touches со всеми касаниями |
событие mousePressed() | событие touchStarted() |
Хорошая новость: p5.js многое делает за тебя. Когда ты касаешься экрана пальцем, p5.js сам кладёт координаты касания в те же mouseX и mouseY. То есть твой старый код с цыплёнком, следящим за мышью, на телефоне уже частично работает — палец двигает «мышь». Но чтобы поймать именно момент касания и сделать всё надёжно, есть отдельные события.
Есть и вторая важная мелочь. На компьютере ты можешь нажать кнопку мыши и не отпускать, и курсор всё это время будет где-то лежать. А на телефоне палец либо касается экрана, либо нет — промежуточного «навёл, но не нажал» не существует в принципе. Поэтому всё, что у мыши делилось на «навёл» и «нажал», у тача сливается в одно: коснулся — значит сразу и навёл, и нажал. Если держать это в голове, мобильная логика становится даже проще мышиной.
touchStarted: ловим момент касания
Функция touchStarted() — это «тач-версия» события нажатия. p5.js вызывает её ровно один раз в тот момент, когда палец впервые коснулся экрана. Внутри неё mouseX и mouseY уже содержат точку касания.
let chickX, chickY;
function setup() {
createCanvas(windowWidth, windowHeight);
noStroke();
chickX = width / 2;
chickY = height / 2;
}
function draw() {
background(135, 206, 235);
// тело
fill(255, 209, 64);
circle(chickX, chickY, 120);
// клюв
fill(255, 140, 30);
triangle(chickX, chickY - 10, chickX + 55, chickY, chickX, chickY + 14);
// глаз
fill(40);
circle(chickX - 26, chickY - 22, 16);
}
function touchStarted() {
// палец коснулся экрана — телепортируем цыплёнка туда
chickX = mouseX;
chickY = mouseY;
return false; // не даём странице прокручиваться от касания
}Результат: цыплёнок сидит на голубом небе. Тыкаешь пальцем в любое место экрана — и он мгновенно перепрыгивает туда, куда ты ткнул, целиком: тело, клюв и глаз вместе. Каждое новое касание переносит его в новую точку.
Разберём по шагам. У цыплёнка есть переменные состояния chickX и chickY — его позиция, которую мы рисуем в draw() каждый кадр. В setup() мы ставим его в центр. А функция touchStarted() срабатывает в момент касания и записывает в chickX, chickY координаты пальца — то есть mouseX и mouseY, которые p5.js заполнил за нас. Цыплёнок рисуется из нескольких фигур, и все они считаются от chickX, chickY, поэтому перепрыгивает он целиком, не разваливаясь.
Отдельно про загадочную строку return false; в конце. Без неё браузер на телефоне может решить, что ты хочешь прокрутить страницу, и начнёт её дёргать при каждом касании. return false говорит браузеру: «это касание для скетча, страницу не трогай». Запомни как привычку: в конце touchStarted() почти всегда ставь return false;.
touchMoved: цыплёнок едет за пальцем
А если хочется не телепорта, а чтобы цыплёнок ехал за пальцем, пока ты ведёшь им по экрану? Для этого есть touchMoved() — она вызывается, пока палец движется по стеклу.
let chickX, chickY;
function setup() {
createCanvas(windowWidth, windowHeight);
noStroke();
chickX = width / 2;
chickY = height / 2;
}
function draw() {
background(135, 206, 235);
fill(255, 209, 64);
circle(chickX, chickY, 120);
}
function touchMoved() {
// цыплёнок мягко догоняет палец (easing)
chickX = chickX + (mouseX - chickX) * 0.2;
chickY = chickY + (mouseY - chickY) * 0.2;
return false;
}Результат: ведёшь пальцем по экрану — и жёлтый цыплёнок плавно катится следом, чуть отставая, будто на резинке. Убрал палец — он замирает на месте. Это тот же easing, что и в прошлом уроке с мышью: каждый шаг проходим лишь часть пути до пальца.
Обрати внимание: touchMoved() срабатывает только пока палец на экране и движется. Как только палец оторвался, движение прекращается — в этом и есть разница с мышью, у которой координаты есть всегда. На телефоне «навести, не касаясь» невозможно: нет пальца — нет координат.
Несколько пальцев сразу: массив touches
Ещё одно, чего мышь не умеет в принципе: на телефоне можно касаться экрана несколькими пальцами одновременно. Вспомни, как ты двумя пальцами увеличиваешь фотографию. p5.js складывает все активные касания в массив touches: у каждого касания есть свои .x и .y. mouseX и mouseY при этом держат координаты только первого пальца — поэтому для мультитача нужен именно массив.
function setup() {
createCanvas(windowWidth, windowHeight);
noStroke();
}
function draw() {
background(135, 206, 235);
// под каждым пальцем — свой цыплёнок
fill(255, 209, 64);
for (let i = 0; i < touches.length; i++) {
circle(touches[i].x, touches[i].y, 100);
}
}Результат: прижми к экрану один палец — появится один жёлтый цыплёнок под ним. Прижми сразу три пальца — будет три цыплёнка, каждый под своим пальцем, и все двигаются независимо. Убрал палец — его цыплёнок исчезает.
Здесь мы перебираем массив touches обычным циклом, как делали со стаей цыплят раньше: для каждого касания touches[i] берём его .x и .y и рисуем там круг. Сколько пальцев на экране — столько и цыплят. Это уже совсем не то, что можно сделать мышью, и хороший пример того, чем мобильный интерактив богаче настольного.
Частые ошибки и подводные камни
Оставил createCanvas с числами. Написал
createCanvas(400, 400)и удивляешься, почему на телефоне холст торчит за край и появилась полоса прокрутки. Для мобильного скетча используйcreateCanvas(windowWidth, windowHeight)— холст подстроится под экран.Рисуешь по числам вместо width и height. Сделал холст на весь экран, но цыплёнка ставишь в
circle(200, 200, 120). На большом экране он уедет в угол, а центр окажется пустым. Считай всё отwidthиheight: центр — этоwidth / 2,height / 2.Забыл return false в touchStarted/touchMoved. При каждом касании страница дёргается и прокручивается, играть невозможно. Поставь
return false;в конце тач-функции — браузер перестанет считать касание прокруткой.Пишешь touchStarted внутри draw() или setup().
touchStarted(),touchMoved(),windowResized()— это отдельные функции верхнего уровня, какsetup()иdraw(). Их нельзя вкладывать одну в другую: объявляй каждую отдельно, а p5.js сам их вызовет в нужный момент.Ждёшь mouseX в touchStarted, но проверял только на компьютере. На мышином компьютере касаний нет, и
touchStarted()просто не сработает — будет казаться, что код не работает. Проверяй мобильное поведение на телефоне (или в режиме эмуляции устройства в браузере), а не только на ноутбуке.
Мини-практика: играбельный цыплёнок в кармане
Возьми за основу пример с touchStarted() и доведи его до настоящей мини-игры для телефона. План:
- Сделай так, чтобы при каждом касании цыплёнок не телепортировался резко, а плавно ехал к точке касания. Подсказка: запомни цель касания в
targetX,targetYвtouchStarted(), а вdraw()двигайchickXк ней через easing. - Нарисуй на небе неподвижное зёрнышко в случайной точке (от
widthиheight). Когда цыплёнок доезжает близко к зерну, перенеси зерно в новую случайную точку — будто он его склевал. - Добавь
windowResized()сresizeCanvas(windowWidth, windowHeight)и проверь, что после поворота телефона всё остаётся на месте (помни: координаты считаем отwidthиheight). - Для смелых: выведи в углу счётчик склёванных зёрен через
text(), размеромwidth / 12, чтобы цифры читались и на маленьком экране.
Открой готовое на своём телефоне и дай потыкать другу. Меняй числа в easing и размере — и сразу смотри, как меняется ощущение игры под пальцем.
Итоги
Теперь твой цыплёнок живёт не только на компьютере, но и в кармане. Вот что ты освоил:
windowWidthиwindowHeight— размеры окна прямо сейчас;createCanvas(windowWidth, windowHeight)растягивает холст на весь экран.- Когда холст под размер экрана, координаты считают от
widthиheight, а не от чисел вроде 200 — иначе на разных устройствах всё съезжает. windowResized()сresizeCanvas(...)перекраивает холст при повороте телефона и изменении окна.- Мышь и тач — разные: у мыши курсор есть всегда, у тача координаты появляются только пока палец на экране.
touchStarted()ловит момент касания,touchMoved()— движение пальца; внутри них работаютmouseX/mouseY, а в конце ставьreturn false;, чтобы страница не прокручивалась.
Ты прошёл целый раздел про интерактив: цыплёнок научился следить за мышью, реагировать на клики, слушать клавиши и теперь — жить на телефоне под твоим пальцем. Дальше нас ждёт самое вкусное: мы соберём все приёмы курса в один большой генеративный аватар CodeChick — твою финальную работу, которую не стыдно опубликовать. Увидимся на следующей странице!