Всем ку
Уже больше полугода прошло с момента выхода уникального в плане пользования механик тест challenge? от Etern. Более того, он
Еще полгода назад я грезил возможностями, но уперся в ограничения редактора, однако за последние две недели я очень сильно продвинулся в сием мероприятии, потому поделюсь своими открытиями с вами.
Я скажу прямо - я CSS не знаю, и я слишком ленив, чтобы учить полноценный язык программирования для каких-то тестиков на пикуко, потому техническую часть от меня не ждите, я вайбкодер. Тем не менее, общие технические принципы я понимаю и поясню, так что у вас будет два пути - или по моему объяснению самостоятельно написать код, или же отдать все клоду, гпт или дипсику с подробным описанием принципов, чтобы они язык слов перевели в язык слова.

Итак, прежде чем начинать разбор некоторых механик, необходимо вообще понять, с чем мы имеем дело - как вообще работают челленджи на пикуко.
Первое, что нам нужно понимать - это что такое svg в принципе, а это векторный формат изображений. У него много отличий от растровых, но для нас важно другое - по сути такое изображение и не изображение в полном смысле слова, по крайней мере в том смысле, в котором мы говорим про растр. Данный формат не задает цвет пикселя в каждой точке, как делают изображения. Что же делает свг? Грубо говоря, это очень большая инструкция по тому, что и как нужно будет нарисовать.
Для нас это принципиально важно, потому что это означает, что свг по-хорошему - это текстовый файл, а в текстовый файл можно вносить свои приколы при большом желании. По сути, мы можем задавать правила интеракции и анимации в самом файле свг.
И тут важно уточнить - в свг можно прописать код на языке HTML и на языке CSS (ну и собственные свойства свг тоже можно прописывать, естественно). И очень важно запомнить - Пикуко съедает HTML. Ничего, что вы напишете через HTML, работать на сайте не будет (это во многом просто защита пользователей), и единственный способ сохранить какие-то данные - это не пользоваться HTML от слова совсем и обходиться альтернативными методами.
Теперь нужно вообще понять, как работает закраска на пикуко. Итак, в svg имеются объекты - по сути те самые кляксы, которые вы рисуете. Сервер пикуко берет эти элементы и присваивает им CSS-правило, по которому какой-то элемент будет выглядеть - в котором с пометкой !important прописан конкретный серый цвет. При вводе правильного ответа идет обращение к серверу, который находит то, какой объект был привязан к данному ответу и какой цвет нужно присвоить, и просто накидывает поверх серого правила новое правило (дальше буду называть стилем для простоты) с уже нужным цветом.
В данном случае нам важно запомнить две вещи - обработка изображения идет таким образом, что заданный сайтом стиль имеет строгий приоритет над тем, что задают стили свг. Также важно - цвет прописан именно в стиле и с пометкой !important (эта пометка значит, что правило, прописанное под ним важнее всего, что мы пытается сделать дальше, и он никак не поменяется). И наконец - правило стиля нашего объекта не меняется, а просто создает новое на странице вашего браузера.
Также уточню еще один важный момент - описанное выше актуально не для всего. Данный стиль не применяется к объектам ellipse (эллипс), line (линия), polyline (ломаная) и, внезапно, image (встроенное изображение). Другими словами, эти объекты сохранят тот же цвет, что вы задали в свг. Дополнительно отмечу, что обводке прямо указано сохранять цвет, так что в теории можно обойтись с помощью нее, но по моему опыту это работает кривовато. Можно исхищряться и делать все через эллипсы, если вам нужно будет, например, понизить вес картинки и сохранить цвета (хотя на практике проверено - пнгшки более чем успешно сохраняются в свгшках на пикуко)

Итак, вот мы узнали, как работают челленджи. Представим, что мы уже знаем нужные языки и знаем, что это делать, и готовы работать с нашим изображением. Какие проблемы у нас возникают?
Самое первое, что нас беспокоит - сложность покраски. Поскольку суть челленджей заключена в покраске свг-карты, все правила, связанные с цветами заданы очень строго, потому придется исхищряться (хотя никто не запрещает вам все-таки передать покраску на пользователей). Тем не менее, есть сразу несколько способов задать цвета насильно (уточняю - через фотошоп не проверял, смотрел в фигме):
1) Описал еще Etern в своем посте и я же упоминал выше - нарисовать все нужные элементы с помощью ellipse, line и polyline. Для меня это выглядит как какая-то лютая дрочь, но тем не менее метод более, чем рабочий. Но тоже важно учитывать - если вы выгрузите идеальный круг (ширина и высота одинаковые), то при экспорте данная фигура будет помечена как circle, а на него правило покраски идет как на обычные объекты. Поэтому делайте ширину или высоту хотя бы на 1 десятую пикселя больше (фигма поддерживает дробные значения).
2) Использовать пнг-картинку. Для этого можно просто закинуть пнгшку в какую-нибудь фигму и сделать экспорт в svg. На самом деле, для всех сложных неинтерактивных элементов это лучший вариант. НО! Очень важный момент. Картинку лучше вставить в первородной форме, т.е. не делать заливку изображением, а именно засунуть само изображение. Заливку пикуко съест.
Из-за этого есть 2 проблемы. Во-первых, у нас все еще есть ограничение в 512КБ, а пнгшки довольно жирные по памяти, так что придется ужиматься (к слову, при добавлении в свг пнг еще и жирнее станет примерно на треть). Вторая проблема - к сожалению, графические редакторы вроде фигмы делают именно заливку, так что придется поработать ручками с кодом свг. Дальше задроченное описание, можете скипнуть:
Открываете файл, например, через блокнот. В начале файла у вас прописываются все объекты, которые если в вашем файле с помощью, в нашем случае интересует <rect> (если мы встраиваем изображение, чаще всего оно вписывается в прямоугольник). При выгрузке из фигмы он будет ссылаться на какой-то pattern, сможете найти дальше по файлу. Проверяет, что данный паттерн (запись начинается с <pattern id= *ваш айди*) ссылается на изображение (должно быть <use xlink:href= и какой-то #image)
Когда это нашли, удаляем строку <rect, ссылающуюся на наше изображение. После удаляем и все, начиная с соответствующего <pattern>, заканчивая </pattern>. В конце берем наше изображение - все, что начинается с <image id= и вставляем на то место, где стоял rect. Таким образом мы меняем логику изображения - вместо того, чтобы делать прямоугольник, у которого добавлена заливка изображением, мы вставляем само изображение. Также изображение будет окружено строками <defs> и </defs>. Они нам не нужны, так что можно их удалить, но в целом, если вы их оставите, ничего не поменяется - картинка все так же отобразится, важно вынести само изображение из этих строк
Еще важный момент. Пикуко определяет, какой объект находится выше на свг-карте, чтобы определить область закрашивания. Так, вы можете просто поместит обычные элементы свг поверх картинки, и спокойно выбирать его для закрашивания. Ну и рекомендую заранее в графическом редакторе задавать размеры и координаты, иначе придется их править ручками в файле.
3) Для каждого объекта вручную прописать пометку !important. Опять же, для этого нужно открывать код, но зато вариант дает полный контроль над свгшкой - находите ваши объекты (например, rect) и ищете свойство style ="fill: . Дальше будет прописан цвет - либо цветовым кодом, либо просто словом (red). Внутри этих же кавычек сразу после цвета пишете !important и вуаля - все готово (еще нюанс - если style= не был прописан, пропишите его, а fill внесите в кавычки)
Для кода помните правило - объект, написанный ниже, находится выше. По сути первая часть - это ваши слои с обозначением, кто за кем идет, к которым потом добавляются какие-то правила заполнения

Как видите, все три способа дают цвет без необходимости его закраски.
Второй вопрос, который у нас возникает - как нам взаимодействовать с ответами, введенными на пикуко? Тыкаться в картинку - это, конечно, круто, но хотелось бы, чтобы и сам смысл челленджа был сохранен (как вот в этом моем тесте или в этом моем тесте)
Что ж, есть у меня ответ и на это. Есть в CSS такая функция - :has(). По сути он проверяет наличие какого-то элемента на странице в данный момент. И у нас есть два варианта - в котором нам неважно, в каком порядке введут ответы (как в 100 к 1) и тот, где мы точно знаем порядок (как в тесте про уток)
Разница между ними минимальна. Поскольку в первом мы не знаем, какой элемент пользователь впишет первым, нам нужно точно увериться, что нужный нам триггер сработает на конкретный ответ, а не на любой. Это мы делаем с помощью задавания определенного цветового кода в проверке. Во втором случае достаточно просто написать количество введенных ответов и триггерить действия в зависимости от номера ответа
Важно! Если вы используете первый вариант, нужно обязательно, чтобы цвет на пикуко совпадал с цветом в вашем свг-файле. Если цвет будет отличаться хоть на 1 последнюю цифру, все перестанет работать.
Дальше немного по коду, можете пропустить, но если хотите прописывать это самостоятельно или дать пояснения нейронке, можете почитать (или скопировать)
Пример такого кода:
.challenge__window:has( - запускаем проверку в рамках окна челленжа на наличие css-правила
.challenge__answer[style*="border-color: rgb(255, 202, 40)"] span style - в данном случае проверяет появление правила на конкретный цвет. По возможности желательно и hex-код прописать, но и ргб в целом хватает (если что, проверяется не сам цвет, а запись, так что лучше задать явно)
)
#my-pikuco-game .your-object {} - в фигурных скобках прописываем, что должно произойти. Любая анимация, движение, изменение размеров, изменение прозрачности и тд
</style> - закрываем блок.
Второй вариант реализуется примерно так
.challenge__window:has(
.challenge__answer:nth-child(
3 of .challenge__answer[style*="border-color"]
) span style и дальше как в прошлом коде - грубо говоря, проверь, чтобы css-правил стало три. По сути, можно применять, если до этого вы итак счетчик сделали

Картинка жабы для разгрузки
Следующий возможный вопрос - как нам выполнить запоминание какого-то значения? HTML и JS обрезаются, а CSS и SVG не дают памяти в явном виде. Тут нам на помощь придет такая вещь, как фокус.
Что такое фокус. По сути мы можем задать какую-то анимацию любому объекту. Но проблема будет в том, что мы не контролируем ее запуск - она может начаться с момента запуска теста. Однако мы можем прописать условие, при котором анимация запустится только в момент взаимодействия с объектом - и делаем это с помощью :focus:
В чем хитрость - мы заранее прописываем какую-то анимацию (например, понижение прозрачности) и времени, за которое событие должно произойти. А потом, когда мы как-то с ним взаимодействуем, запускаем необратимую анимацию. Пример кода под спойлером
<style>
#my-pikuco-game #focus-object {
opacity: 1; - задаем начальное состояние (100% непрозрачно)
transition: opacity 0.3s; - задаем время анимации (для мгновенного можно 1ms поставить)
}
#my-pikuco-game #focus-object:focus { - пишем, что при фокусе должно что-то произойти
opacity: 0.4; - задаем конечное состояние
}
</style>
<rect - находим нужный нам объект
id="focus-object" - в айдишник
tabindex="0" - получаем фокус при клике
И остальные свойства оставляем как есть
Второй вопрос - что делать, если мы хотим обратить анимацию вспять (как в этом моем тесте). Тут все несколько сложнее, но вполне реализуемо. Значится, помимо времени перехода мы также можем задать и время действия анимации. По сути мы просто создаем два фокуса в одной области - и в каждом запускает анимацию на срок больше, чем идет тест (например, миллиард лет). Настраиваем кнопку переключения режима, которые присваивают определенное значение заданной переменной, которая, в свою очередь, определит, какая из областей будет активна в данный момент. А затем каждой из них задаем анимацию на фокус. Пример кода:
.flaghit, .erasehit { - переменные
pointer-events: none; - начальное состояние, не взаимодействуем с ними
}
#flagmode:target ~ #main #assembly .flaghit {
pointer-events: all; - при активации принимаем все действия
}
#erasemode:target ~ #main #assembly .erasehit {
pointer-events: all; - то же самое, но со вторым режимом
}
<a href="#flagmode">...</a>
<a href="#erasemode">...</a> - задаем кнопки преключения режимов
.flaghit, .erasehit { - переменные
pointer-events: none; - начальное состояние, не взаимодействуем с ними
}
#flagmode:target ~ #main #assembly .flaghit {
pointer-events: all; - при активации принимаем все действия
}
#erasemode:target ~ #main #assembly .erasehit {
pointer-events: all; - то же самое, но со вторым режимом
}
.flagState {
visibility: hidden; - пишем, что в конце флаг должен оказаться скрыт
transition: visibility 1000000000s step-end; - указываем, что событие должно произойти после конца анимации через миллиард секунд
}

Картинка жабы для разгрузки
Последний вопрос - как сделать счетчик очков. Тут все просто. Мы должны явно прописать все варианты очков на экране, а после проверять, какой из них мы должны показывать в данный момент. Делаем длинную вертикальную ленту со всеми вариантами количества и перемещаем их вниз-вверх в зависимости от количества очков (допустим, для 10 очков сдвигаем ленту вниз на 100 пикселей, на 20 - на 200 пикселей вниз и тд)
Отлично, но как понять, что настало время показывать определенное количество очков? А вот тут интересно. Сначала мы задаем анимацию движения - например, начисляя 10 очков, сдвигаем ленту на 100 пикселей вверх, а на 5 очков - на 50 пикселей вверх. После мы привязываем каждой кнопке соответствующее действие. И дальше главный прикол - мы создаем группу для первой анимации, затем кладем ее внутрь группы второй анимации, и так вкладываем до конца.
Зачем это делать именно так? Из-за того, что мы пользуемся фокусом, мы по сути просто создаем заранее все возможные варианты действия. Мы не запускаем анимацию одну за другой, а просто запускаем другую анимацию. И тут важный момент - фокус в данном случае переходит с одной кнопки на другую, почему же данные сохраняются? Ответ прост - фокус просто запускает воздействие (анимацию, в данном случае), которая не была зациклена, она постоянно назначена группе с момента, как проигралась, а потеря фокуса просто ставит анимацию на паузу, сохраняя ее в том же финальном состоянии. Это значит, что при потере фокуса все тупо зависнет в финальном состоянии, как и было
В итоге, вкладывая все эти перемещения внутрь друг друга, мы не будет последовательно двигать кнопку сначала на одну высоту, а потом с начальной же точки на вторую высоту, а сначала на первую высоту, а потом уже от новой стартовой точки. Код ниже:
<style>
#my-pikuco-game .points10 { - задаем правила работы анимации
animation: plus10 1ms step-end both paused; } - изначально на паузе, после конца застывает в новом состоянии
#my-pikuco-game .points5 { - то же самое, но для другого объекта (points5)
animation: plus5 1ms step-end both paused; }
@keyframes plus10 { - описываем анимацию plus10, которую задавали для объекта points10 выше
from { transform: translateY(0); } - изначальное положение - 0 по Y
to { transform: translateY(-100px); } - новое положение на 100 пикселей выше
}
@keyframes plus5 { - то же самое для 5 очков
from { transform: translateY(0); }
to { transform: translateY(-50px); }
}
#my-pikuco-game #add10:focus ~ #counter .points10 { - прописываем, что переключении фокуса (например, при клике) на кнопку add10 запускаем анимацию из группы points10 внутри группы counter
animation-play-state: running; }- собственно, описываем, что нужно запустить при фокусе
#my-pikuco-game #add5:focus ~ #counter .points5 { - то же самое для 5 очков. Прописывать весь путь необязательно, сверху главную группу, затем ту, на которую ссылаемся
animation-play-state: running; - запускаем анимацию}
</style>
<g id="counter"> - создаем главную группу
<g class="points10"> - внутрь этой группы вложили группу для 10 очков
<g class="points5"> - внутрь предыдущей группы вложили группу для 5 очков
</g>
<!-- Здесь числовая лента --> просто последовательный набор числе друг под другом на фиксированном рассторянии друг от друга. Например,
<text x="0" y="8">0</text>
<text x="0" y="18">1</text>
<text x="0" y="28">2</text>
<text x="0" y="38">3</text>
</g>
</g> - закрываем все 3 группы.
Важно, чтобы у всех групп был одинаковый родитель (в данном случае counter). И еще - такие кнопки одноразовые. То есть дважды нажать кнопку и прибавить еще очки не получится. Вы можете переместить на то же место другую кнопку, но такое ограничение важно учитывать

Мда, жаловаться на то, что пост Etern был непонятным и написать еще более задроченное чтиво - это я умею. Ну, а чего вы еще ожидали? На самом деле, я сомневаюсь, что кто-то будет прописывать это самостоятельно. Это очень большая дрочь, даже когда ты понимаешь, что делаешь, а большинство этого не будут понимать, потому что в их жизни есть что-то кроме программирования. Но 26 год - год нейрослопа, а нейронки очень хорошо умеют повторять задачи. Так что вам достаточно просто прислать нейронке описание действий, и она все пропишет сама. В общем, надеюсь, этот гайд кому-то что-то даст, а я за сим откланиваюсь
Прощальный ку
Поделиться:
Пожертвование пользователю Пёс Кошачий
Пожертвование пользователю Пёс Кошачий
Ваше пожертвование получено пользователем! Спасибо!
Лучшиекомментарии (10)
Кьянко
Ещё не стоит забывать, что если вы вайбкодеры, решите воспользоваться Дипсиком, то всегда есть шанс, что он даже с нормальным промптом напишет вам на свг, а сдвг
ftrewqq
Ответ на Ещё не стоит забыват... (Кьянко)
блин я помню как пытался с дипсика сделать свг, он мне 3 часа фигню генерил и дичь втирал мол это работает, в итоге я ниче с ним и не добился. И при этом дипсик одна из лучших бесплатных нейронок для вайбкодинга, с какой нибудь Алисой там вообще жуть жуткая😵
Не Топлес
Ответ на блин я помню как пыт... (ftrewqq)
Алиса это вообще голосовой помощник, её задача — по команде свет включать и музыку, а не код генерить...
Кьянко
Ответ на Алиса это вообще гол... (Не Топлес)
Писать код вообще задача этих несчастных лошков программистов
ftrewqq
Ответ на Алиса это вообще гол... (Не Топлес)
человаемый уважек, алиса щас не просто голосовой помощник ес че
MilkyWay
Легенда, спасибо
Рикардио
сдвг геймс
ftrewqq
пикуко съедает хтмл??? бедное хтмл 😭😭😁😭😥😥😥😥😨😭😭😭😭😰😰😰😭😭😂😭😭😭😭
sasha.go
А самое главное - вы оба НЕ рассказали нам, КАК писать код, только про функции рассказали)))))))
sasha.go
Ответ на А самое главное - вы... (sasha.go)
Ну типа что в школе надо писать толком не понятно, хотя тут частично это есть в отличие от этерна