В данной статье будет описано создание части модуля для рисования — 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/
Добавить комментарий