Здравствуйте!

Очень непросто начинать рассказ глядя в белое окно редактора, поэтому не буду оригинальным и скажу, что написал пет-проект в виде онлайн сервиса, который хочу показать широкой аудитории, и за который, я надеюсь, мне будет не очень стыдно.

Основная цель сервиса - проектирование интерфейсов. Предвижу сразу же "пфф, тоже мне, таких сайтов и приложений вагон и маленькая тележка". Да, это несомненно так. Есть такие гиганты как фигма, (куда с ней конкурировать, серьезно, без иронии), есть и менее известные проекты. Изучить досконально все инструменты не представляется возможным по разным причинам: какие-то инструменты доступны только по платной подписке, о каких-то я вообще не слышал и не знаю, а какие-то слишком трудны для поверхностного ознакомления. Поэтому я понимаю, что мой проект вполне может повторять отчасти, а возможно и целиком уже что-то существующее, но тем не менее, спешу поделиться с вами своей наработкой.

Итак, поехали

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

Основная цель проекта заставить говорить на одном языке заказчика, дизайнера и разработчика. Как это будет выглядеть в реальности? В идеале дизайнер создаст макет, скинет на него ссылку заказчику, тому что-то не понравится, будут внесены правки, и ссылка полетит разработчику, который сможет уже взять код шаблона и использовать его по назначению. Понимаю, что это выглядит утопично, но вот такая она цель.

Поскольку основной задачей является проектирование интерфейсов, то в проекте можно создавать 2 вещи: разметку и вайрфреймы.

Создание разметки

Рассмотрим сценарий, в котором вам нужно на скорую руку сделать разметку.

Пример разметки
Пример разметки
Шаблон и его сокращенный вариант в HTML
Vue шаблон
<!--{}-->
<!--{"type":0,"title":"Основной контейнер","settings":{"default":{"0":{"container":{"direction":0}}}}}-->
<div v-bind:class='{"flex-row flex w-full h-full justify-between items-start":true}'>
     <!--{"type":0,"title":"Контейнер 1","settings":{"default":{"0":{"container":{"direction":0}}}}}-->
     <div v-bind:class='{"flex-row flex w-full h-full justify-between items-start":true}'>
         <!--{"type":0,"title":"Контейнер 1","settings":{}}-->
         <div v-bind:class='{"w-full h-full":true}'>
         </div>
         <!--{"type":0,"title":"Контейнер 2","settings":{}}-->
         <div v-bind:class='{"w-full h-full":true}'>
         </div>
         <!--{"type":0,"title":"Контейнер 3","settings":{}}-->
         <div v-bind:class='{"w-full h-full":true}'>
         </div>
     </div>
     <!--{"type":0,"title":"Контейнер 2","settings":{"default":{"0":{"container":{"direction":1}}}}}-->
     <div v-bind:class='{"flex-col flex w-full h-full justify-between items-start":true}'>
         <!--{"type":0,"title":"Контейнер 1","settings":{}}-->
         <div v-bind:class='{"w-full h-full":true}'>
         </div>
         <!--{"type":0,"title":"Контейнер 2","settings":{"default":{"0":{"container":{"direction":0}}}}}-->
         <div v-bind:class='{"flex-row flex w-full h-full justify-between items-start":true}'>
             <!--{"type":0,"title":"Контейнер 1","settings":{}}-->
             <div v-bind:class='{"w-full h-full":true}'>
             </div>
             <!--{"type":0,"title":"Контейнер 2","settings":{}}-->
             <div v-bind:class='{"w-full h-full":true}'>
             </div>
             <!--{"type":0,"title":"Контейнер 3","settings":{"default":{"0":{"container":{"direction":1}}}}}-->
             <div v-bind:class='{"flex-col flex w-full h-full justify-between items-start":true}'>
                 <!--{"type":0,"title":"Контейнер 1","settings":{}}-->
                 <div v-bind:class='{"w-full h-full":true}'>
                 </div>
                 <!--{"type":0,"title":"Контейнер 2","settings":{}}-->
                 <div v-bind:class='{"w-full h-full":true}'>
                 </div>
             </div>
             <!--{"type":0,"title":"Контейнер 4","settings":{}}-->
             <div v-bind:class='{"w-full h-full":true}'>
             </div>
         </div>
         <!--{"type":0,"title":"Контейнер 3","settings":{}}-->
         <div v-bind:class='{"w-full h-full":true}'>
         </div>
     </div>
     <!--{"type":0,"title":"Контейнер 3","settings":{"default":{"0":{"container":{"direction":1}}}}}-->
     <div v-bind:class='{"flex-col flex w-full h-full justify-between items-start":true}'>
         <!--{"type":0,"title":"Контейнер 1","settings":{}}-->
         <div v-bind:class='{"w-full h-full":true}'>
         </div>
         <!--{"type":0,"title":"Контейнер 2","settings":{"default":{"0":{"container":{"direction":0}}}}}-->
         <div v-bind:class='{"flex-row flex w-full h-full justify-between items-start":true}'>
             <!--{"type":0,"title":"Контейнер 1","settings":{}}-->
             <div v-bind:class='{"w-full h-full":true}'>
             </div>
             <!--{"type":0,"title":"Контейнер 2","settings":{}}-->
             <div v-bind:class='{"w-full h-full":true}'>
             </div>
         </div>
     </div>
</div>
HTML
<div class="flex-row flex w-full h-full justify-between items-start">
     <!--Основной контейнер-->
     <div class="flex-row flex w-full h-full justify-between items-start">
         <!--Контейнер 1-->
         <div class="w-full h-full">
             <!--Контейнер 1-->
         </div>
         <div class="w-full h-full">
             <!--Контейнер 2-->
         </div>
         <div class="w-full h-full">
             <!--Контейнер 3-->
         </div>
     </div>
     <div class="flex-col flex w-full h-full justify-between items-start">
         <!--Контейнер 2-->
         <div class="w-full h-full">
             <!--Контейнер 1-->
         </div>
         <div class="flex-row flex w-full h-full justify-between items-start">
             <!--Контейнер 2-->
             <div class="w-full h-full">
                 <!--Контейнер 1-->
             </div>
             <div class="w-full h-full">
                 <!--Контейнер 2-->
             </div>
             <div class="flex-col flex w-full h-full justify-between items-start">
                 <!--Контейнер 3-->
                 <div class="w-full h-full">
                     <!--Контейнер 1-->
                 </div>
                 <div class="w-full h-full">
                     <!--Контейнер 2-->
                 </div>
             </div>
             <div class="w-full h-full">
                 <!--Контейнер 4-->
             </div>
         </div>
         <div class="w-full h-full">
             <!--Контейнер 3-->
         </div>
     </div>
     <div class="flex-col flex w-full h-full justify-between items-start">
         <!--Контейнер 3-->
         <div class="w-full h-full">
             <!--Контейнер 1-->
         </div>
         <div class="flex-row flex w-full h-full justify-between items-start">
             <!--Контейнер 2-->
             <div class="w-full h-full">
                 <!--Контейнер 1-->
             </div>
             <div class="w-full h-full">
                 <!--Контейнер 2-->
             </div>
         </div>
     </div>
</div>

Создать разметку можно буквально за несколько кликов. Достаточно зажать клавишу Ctrl и несколько раз подряд вдоль и поперек кликнуть по холсту (основному контейнеру). В зависимости от направления кликов и их количества, контейнер разобьется на нужные контейнера, которые, в свою очередь, можно разбивать точно так же.

Каждый контейнер имеет настройки. Подробнее об этом написано в инструкции.

Создание wireframe

Пример аудиоплеера
Пример аудиоплеера
Шаблон и его сокращенный вариант в HTML
Vue шаблон
<!--{}-->
<!--{"type":0,"title":"Основной контейнер","settings":{"default":{"0":{"container":{"direction":1}}}}}-->
<div v-bind:class='{"flex-col flex w-full h-full justify-between items-start":true}'>
     <!--{"type":0,"title":"Контейнер 1","settings":{"default":{"0":{"container":{"direction":0}}}}}-->
     <div v-bind:class='{"flex-row flex w-full h-full justify-between items-start":true}'>
         <!--{"type":0,"title":"Контейнер 1","settings":{}}-->
         <div v-bind:class='{"flex w-full h-full justify-between items-start":true}'>
             <!--{"type":3,"title":"Обложка альбома","settings":{}}-->
             <div v-bind:class='{"block w-full h-full":true}'>
             </div>
         </div>
         <!--{"type":0,"title":"Контейнер 2","settings":{"default":{"0":{"container":{"expand":false,"width":3,"direction":1}}}}}-->
         <div v-bind:class='{"flex-col flex h-full justify-between items-start w-12":true}'>
             <!--{"type":0,"title":"Контейнер 1","settings":{"default":{"0":{"container":{"expand":false,"height":9}}}}}-->
             <div v-bind:class='{"flex w-full justify-between items-start h-64":true}'>
                 <!--{"type":1,"title":"Информация об исполнителе","settings":{},"custom":"H4sIAAAAAAAAA2XNMQ7DIAyF4asgz/HSIAYO0Qs0DBY4gARRFdw2UsTdq2xtsrzt+98OgWd6FQG7g1C8U2WwEPIbBvCFWuMGFiaYC28qC9eGnhfhVUV6olZVjimoJzhEyiWsvIB9/OY85kqRT8kPjkYlHM2Zuj78a+FNLtholfB2+XXd9f4FZYJDXdcAAAA="}-->
                 <div v-bind:class='{"block w-full h-full":true}'>
                 </div>
             </div>
             <!--{"type":0,"title":"Контейнер 2","settings":{}}-->
             <div v-bind:class='{"flex w-full h-full justify-between items-start":true}'>
                 <!--{"type":1,"title":"Другие треки","settings":{"default":{"0":{"block":{"clone":{"rows":8,"expandHorizontal":true,"height":5,"sizeStep":{"type":2,"value":1}}}}}},"custom":"H4sIAAAAAAAAA6tWSklNSyzNKVGyqlYqSUz3S8xNVbJSStZNLE3JzFfSUUrOyMxJKUrNU7KKjq2tBQAesZ6tLwAAAA=="}-->
                 <div v-bind:class='{"block w-full h-full":true}'>
                 </div>
             </div>
         </div>
     </div>
     <!--{"type":0,"title":"Контейнер 2","settings":{"default":{"0":{"container":{"expand":false,"height":5,"sizeStep":{"type":2,"value":1}}}}}}-->
     <div v-bind:class='{"flex w-full justify-between items-start h-[6rem]":true}'>
         <!--{"type":1,"title":"Управление воспроизведением","settings":{},"custom":"H4sIAAAAAAAAA6tWSklNSyzNKVGyqlYqSUz3S8xNVbJSStZNLE3JzFfSUUrOyMxJKUrNU7KKjq2tBQAesZ6tLwAAAA=="}-->
         <div v-bind:class='{"block w-full h-full":true}'>
         </div>
     </div>
</div>
HTML
<div class="flex-col flex w-full h-full justify-between items-start">
     <!--Основной контейнер-->
     <div class="flex-row flex w-full h-full justify-between items-start">
         <!--Контейнер 1-->
         <div class="flex w-full h-full justify-between items-start">
             <!--Контейнер 1-->
             <div class="block w-full h-full">
                 <!--Обложка альбома-->
             </div>
         </div>
         <div class="flex-col flex h-full justify-between items-start w-12">
             <!--Контейнер 2-->
             <div class="flex w-full justify-between items-start h-64">
                 <!--Контейнер 1-->
                 <div class="block w-full h-full">
                     <!--Информация об исполнителе-->
                 </div>
             </div>
             <div class="flex w-full h-full justify-between items-start">
                 <!--Контейнер 2-->
                 <div class="block w-full h-full">
                     <!--Другие треки-->
                 </div>
             </div>
         </div>
     </div>
     <div class="flex w-full justify-between items-start h-[6rem]">
         <!--Контейнер 2-->
         <div class="block w-full h-full">
             <!--Управление воспроизведением-->
         </div>
     </div>
</div>

В редакторе помимо основных элементов (тек��т, круг, изображение) также имеются готовые компоненты. Но есть одно "но", но об этом немного ниже.

Ещё немного плюшечек

Все блоки и контейнеры можно двигать, скрывать, менять размеры без написания кода. Код, напишется сам, по мере внесения изменений.

Все шаблоны можно делать адаптивными.

Можно привязывать сценарии (например, в шаблоне может быть некоторое условие или набор условий по которому будет скрываться или показывать какой-нибудь блок).

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

Ложка дёгтя

Возвращаясь к "но". Для использования компонентов в вайрфреймах потребуются минимальные знания html. Заранее вижу негати��, типа, "а как же те, кто с html ни в зуб ногой?". Увы, но пока на данном этапе ни о каком canvas редакторе даже не помышляю, поскольку эти данные нужно будет где-то хранить, а перегружать ими код шаблона вообще не хочется.

Дальнейшие планы

Рано пока строить какие-то планы. Вообще если проект покажется интересным людям, то есть мысль начать делать личный кабинет со всеми вытекающими: собственные библиотеки компонентов, canvas редактор и многое другое. Понятно, что оно будет не быстро воплатимо в жизнь, если оно вообще будет)

Спасибо большое за уделенное время.