Пишем javascript модуль для wysiwyg редактора изображений Collage_n

от автора

В данной статье будет описано создание части модуля для рисования — paintSprites для редактора Colllage_n и за одно будет рассмотрен принцип написания модулей на фреймворке Htmlix. Данный модуль позволяет создавать кисти на основе какого либо спрайта и рисовать ею как в обычном редакторе paint. Таким образом можно отредактировать и вырезать кусочек изображения какой не будь картинки, создать из нее спрайт и рисовать им как обычным карандашом или кистью, а также вращать узор кисти по оси или масштабировать ее колесиком мыши. Например вырезать цветок из какого либо изображения отформатировать его и рисовать.

Редактор работает онлайн, для работы на локальном сервере необходим node.js Для запуска необходимо скопировать файлы редактора, файл модуля paintSprites.js в папку modules, в командной строке из корневой папки запустить node app и перейти по ссылке http://localhost:3000/. После загрузки редактора модуль нужно будет включить во вкладке Загрузить модуль, прописав адрес модуля «./js/modules/paintSprites.js» и нажав кнопку загрузить модуль. Можно также сохранить его в текстовых настройках для автозагрузки в дальнейшем.

Краткий обзор корневого приложения

Collage_n написан на фреймворке htmlix это позволяет загружать модули динамически в процессе работы для удобства. Для начала давайте разберемся с базовыми принципами создания модуля для редактора. В файле interface.js находятся обработчики для кнопок корневого приложения, общие переменные, методы и пользовательские события для всего приложения (в конце файла).

["emiter-create-sprite"] : {prop: ""}, //событие создания спрайта ["emiter-mousemove-canvas"] : {prop: ["x", "y"]}, //событие движения курсора по канвас и массив с координатами точки  ["emiter-mousedown-canvas"] : {prop:  ["x", "y"]}, //событие клика по канвас с координатами ["emiter-mouseup-canvas"] : {prop:  ["x", "y"]},  //событие окончания клика с координатами ["emiter-operation-with"] : { //событие смены выполняемой операции и метод для сохранения названия текущей операции в глобальную переменную operationWith нную prop: "common", behavior: function(){ this.$props().operationWith = this.prop; } },

Пользовательские события нужны чтобы сообщить всем компонентам которые на него подписаны о каком либо событии корневого приложения, их можно вызвать из любой точки приложения или в модуле this.$$("emiter-имя-события").set("данные для подписчиков")

Компонент canvas имеет три метода обработчика событий: mousedown, обработка клика по канвас для фоновой картинки if(this.$props("operationWith") == "common") либо для спрайта if(this.$props().sprites[this.props("operationWith")]) два других mousemove и mouseup работают аналогично в них мы проверяем значение глобальной переменной «operationWith» если это операция со спрайтом то в данной пременной будет его id.

Остальные файлы: sprites.js — объект для создания и сохранения спрайтов CollageSprite на компьютере, в интерфейсе они представлены массивом sprites и связаны с объектами CollageSprite через глобальную пременную — объект $props().sprites через id, draw_functions.js — просто глобальные функции для работы с canvas: попиксельная обработка, масштабирование, вращение области выделения и изображения и т.д.

Создание модуля

Для начала создадим разметку модуля: кнопка включения, два поля ввода для размера и цвета окружности которой будем рисовать.

//создаем модуль  //onloadModules - глобальный объект список загружаемых модулей и скриптов main.js (function(){    if(onloadModules.paintSprites  != undefined)return;    var html = `   <div data-paint_sprites_panel="container"  class="form-group" name="paint_sprites_panel"> <label for="exampleFormControlInput1" style="font-size: 15px; color: red; font-weight: bold;">Рисовать</label> <div class="form-row">   <div class="form-group col-md-4"> <button type="button"  style="" name="paint_btn" class="btn btn-danger btn-sm" title="Для рисования нужно выбрать спрайт нажать кнопку и рисовать с нажатой кнопкой мыши">paint</button> </div>    <div class="form-group col-md-4">     <input name="draw_sircle_radius" type="text" class="form-control form-control-sm"  placeholder="радиус" title="" value="5"> </div> <div class="form-group col-md-4"> <input name="draw_sircle_color" type="text" class="form-control form-control-sm"  placeholder="цвет" title="" value="red"> </div>                  </div>    </div> `;    //добавление разметки модуля в общюю разметку обычными функциями javascript в любое место //создали div нашли куда вставить "[data-main-form]" и и перед кем "[name='common_btn_class']"   var div = document.createElement("div");   div.innerHTML = html;   div = div.querySelector("div");   var parent = document.querySelector("[data-main_form]");   var insert_before = document.querySelector("[name='common_btns_class']")   var insertedElement = parent.insertBefore(div, insert_before);      //объект описания модуля     var paint_sprites_panel = {      container: "paint_sprites_panel",      props: [ ],      methods: {}    } //добавили описание модуля в описание всего приложения.   HM.description.paint_sprites_panel  = paint_sprites_panel; //загрузили модуль     HM.containerInit(div , HM.description, "paint_sprites_panel"); HM.eventProps["emiter-operation-with"].emit(); //вызываем пустым (без параметра) чтобы отключить слушателей canvas событий при старте модуля другими модулями //поставили флаг для избежания случайной двойной загрузки модуля onloadModules.paintSprites  = true; })()

Для того чтобы в неактивном состоянии модуль не прослушивал глобальные события клика по канвас и перемещения по нему курсора — добавим слушатель события смены текущей операции operation_with и если модуль не активен != paint-sprites, будем отключать обработчики.

  props: [          ["canvas_click", "emiter-mousedown-canvas", ""],     ["canvas_move", "emiter-mousemove-canvas", ""],     ["mouse_up", "emiter-mouseup-canvas", ""],          //Отключать и включать мы их будем в слушателе события      ["operation_with", "emiter-operation-with", ""], ] methods: {   operation_with: function(){ //отключает слушателей canvas событий ( mousedown) если модуль находится в пассивном состоянии     if(this.emiter.prop != "paint-sprites" ){      this.parent.props.canvas_click.disableEvent();// отключаем события         this.parent.props.canvas_move.disableEvent();         this.parent.props.mouse_up.disableEvent();   }else{   this.parent.props.canvas_click.enableEvent();         this.parent.props.canvas_move.enableEvent();         this.parent.props.mouse_ap.enableEvent();   }                } }  

Далее добавим обработчик кнопке рисовать

props: [            ["paint_btn", "click", "[name='paint_btn']"], //находим кнопку в разметке по ее имени (будет искать относительно контейнера в котором она находится)    paint_btn: function(){ //добавляем обработчик клику по кнопке         //вызываем пользовательское событие с именем операции(имя модуля) чтобы включить обработчики канвас и сказать другим модулям в разных частях приложения отключить свои обработчики или выполнить другие действия (скрыть лишние кнопки и т.д.)ия о   this.$$("emiter-operation-with").set("paint-sprites");        }, 

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

props:[   //добавили два свойства для размера и цвета кисти   //...........................    ["draw_sircle_radius", "inputvalue", "[name='draw_sircle_radius']"],     ["draw_sircle_color", "inputvalue", "[name='draw_sircle_color']"], ]  canvas_click: function(){//нажатие кнопки      //сохранить фоновое изображение до рисования, чтобы можно было отменить functions.js   saveStep(saveImg, this.$props().commonProps.area_1); ctx.save();       ctx.putImageData(saveImg, 0, 0); ////////////////////////         //рисуем круг данные берем из формы ввода цвета и размера         //this.emiter.prop доступ к параметру пользовательского события- координаты клика по x и у осям вычисляется в корневом приложении ctx.beginPath(); ctx.arc(this.emiter.prop[0], this.emiter.prop[1], this.parent.props.draw_sircle_radius.getProp(), 0, 2*Math.PI, false); ctx.fillStyle =  this.parent.props.draw_sircle_color.getProp(); ctx.fill(); ctx.lineWidth = 0.1; ctx.strokeStyle =  this.parent.props.draw_sircle_color.getProp(); ctx.stroke();         ///сохраняем данные для метода ниже чтобы не брать их постоянно из формы ввода при движении курсора        this.parent.props.canvas_move.prop  =  { color:  this.parent.props.draw_sircle_color.getProp(), radius:  this.parent.props.draw_sircle_radius.getProp(),    }       },     canvas_move: function(){ //тоже самое и при движении курсора мыши       if(this.prop != null){ //проверяем наличие параметров чтобы не рисовать с когда кнопка поднята   ctx.beginPath();   ctx.arc(this.emiter.prop[0], this.emiter.prop[1], this.prop.radius, 0, 2*Math.PI, false);   ctx.fillStyle =  this.prop.color;   ctx.fill();   ctx.lineWidth = 0.1;   ctx.strokeStyle =  this.prop.color;   ctx.stroke();       }   }, mouse_up: function(){                 //удаляем данные для отмены рисования                    this.parent.props.canvas_move.prop = null; //сохраняем изображение в глобальную переменную saveImage чтобы можно было нарисовать сверху спрайты, контур и т.д.                 //Для будущих преобразований картинки фона данные берутся из нее (main.js) saveImg = ctx.getImageData(0,0, srcWidth, srcHeight); ctx.restore();    },  

Теперь модуль готов для рисования обычным карандашем, цвета можно задавать любыми удобными форматами "rgba(32, 45, 21, 0.3)", red, #6610f2 и т.д.


ссылка на оригинал статьи https://habr.com/ru/post/646457/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *