В этой статье я собираюсь показать создание небольшого приложения с помощью которого можно будет создавать пиксельные картинки, также будет возможность разместить эти картинки в галерее, проголосовать за понравившуюся. Вообщем будет блог, галерея и мастерская. Мне показалось, что это может быть интересно новичкам, которые уже немного научились Angular и пробуют делать различные приложения для того, чтобы выработать свой стиль и набить руку.
Это моя первая статья подобного рода и возможно я бы никогда ее не разместил, но моё лузерство и привычка быть обыкновенным, меня настолько раскрепостили, что я воспринимаю поражения как должное. Амбиций нет, есть только кайф от того, что я делаю, люблю созерцать, люблю что то создавать. Рад буду если это кому то принесет пользу. Надеюсь при просмотре опытными разработчиками все не окажется очень плохо и я найду в себе силы выложить следующие части.
А может кому то просто понравится рисовать, ссылка на демо ниже.
В этой части я буду использовать Angular, CSS фреймворк от w3schools
Итак, в первой части будет описан процесс создания вот такой мастерской.

Здесь можно посмотреть Demo, здесь находится код.
Идем устанавливаем Ангулар. Создаем новый проект.
Подразумевается, что вы умеете это делать по этому я не буду это описывать.
Наша мастерская будет создана в виде отдельного модуля.
Создаем его с помощью следующих команд:
ng g m canvas
ng g c canvas/components/main-canvas —module canvas.module
ng g c canvas/components/left-panel —module canvas.module
ng g c canvas/components/canvas —module canvas.module
ng g c canvas/components/form —module canvas.module
Где main-canvas у нас является родителем для компонентов left-panel, canvas, form.
Создаем component main-canvas
code main-canvas.component.html
<!DOCTYPE html> <html> <title>W3.CSS Template</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css"> <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto'> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> <style> html, body, h1, h2, h3, h4, h5, h6 { font-family: "Roboto", sans-serif } </style> <div class="w3-bar w3-light-grey"> <h1 style="padding-left: 2rem;">ARTpixel</h1> </div> <body class="w3-light-grey"> <!-- Page Container --> <div class="w3-content w3-margin-top" style="max-width:1400px;"> <!-- The Grid --> <div class="w3-row-padding"> <!-- Right Column --> <!-- Left Column --> <app-left-panel (pagesEvent)="getDataPanel($event)"></app-left-panel> <!-- End Left Column --> <div class="w3-twothird"> <div class="w3-container w3-card w3-white w3-margin-bottom"> <h2 class="w3-text-grey w3-padding-16"><i class="fa fa-paint-brush fa-fw w3-margin-right w3-xxlarge w3-text-teal"></i>Поле рисования </h2> <div class="w3-container"> <app-canvas [uploadSuccess]="uploadSuccess"></app-canvas> <hr> </div> </div> </div> <!-- End Right Column --> </div> <!-- End Grid --> </div> <!-- End Page Container --> <footer class="w3-container w3-teal w3-center w3-margin-top"> <p>Find me on social media.</p> <i class="fa fa-facebook-official w3-hover-opacity"></i> <i class="fa fa-instagram w3-hover-opacity"></i> <i class="fa fa-snapchat w3-hover-opacity"></i> <i class="fa fa-pinterest-p w3-hover-opacity"></i> <i class="fa fa-twitter w3-hover-opacity"></i> <i class="fa fa-linkedin w3-hover-opacity"></i> <p>Powered by <a href="https://www.w3schools.com/w3css/default.asp" target="_blank">w3.css</a></p> </footer> </body> </html>
code main-canvas.component.ts
import { Component, EventEmitter } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Component({ selector: 'app-main-canvas', templateUrl: './main-canvas.component.html', styleUrls: ['./main-canvas.component.scss'] }) export class MainCanvasComponent { uploadSuccess: EventEmitter<any> = new EventEmitter();//объявили constructor() { } getDataPanel($event: FormGroup) { const data = { widthRect: Number($event.value.widthCanvas), heightRect: Number($event.value.heightCanvas), numberOf: Number($event.value.hwPixel), borderRow: $event.value.meshThickness, numberRow: Number($event.value.hwPixel) + Number($event.value.meshThickness), innerWidth: Number($event.value.heightCanvas) * (Number($event.value.hwPixel) + Number($event.value.meshThickness)), innerHeight: Number($event.value.widthCanvas) * (Number($event.value.hwPixel) + Number($event.value.meshThickness)), colorfillStyle: $event.value.colorFone } this.uploadSuccess.emit(data);//передали } }
Пока в этом компоненте у нас находится единственный метод, который получает данные из компонента left-panel
<app-left-panel (pagesEvent)="getDataPanel($event)"></app-left-panel>
который в свою очередь получает их из компонента form и передает их в компонент canvas
<app-canvas [uploadSuccess]="uploadSuccess"></app-canvas>
где на основании отправленных данных происходит создание рисунка.
Создаем компонент left-panel
left-panel.cpmponent.html
<div class="w3-third"> <div class="w3-white w3-text-grey w3-card-4"> <div class="w3-container"> <p class="w3-large"><b><i class="fa fa-asterisk fa-fw w3-margin-right w3-text-teal"></i>Форма создания холста</b></p> <div class="w3-light-grey w3-round-xlarge w3-small"> <app-form (pagesEvent)="getData($event)" ></app-form> </div> <hr> </div> </div><br> </div>
Здесь все просто мы получаем данные из компонента форм
<app-form(pagesEvent)="getData($event)"></app-form>
left-panel.component.ts
import { Component, EventEmitter, Output } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Component({ selector: 'app-left-panel', templateUrl: './left-panel.component.html', styleUrls: ['./left-panel.component.scss'] }) export class LeftPanelComponent { @Output() pagesEvent: EventEmitter<FormGroup > = new EventEmitter< FormGroup >(); constructor() { } getData($event: FormGroup) { this.pagesEvent.emit($event); } }
Сейчас у нас здесь единственный метод getData() который передает полученный из формы данные в родительский компонент и дальше в канвас компонент
Создаем компонент form.
form.component.html
<form [formGroup]="form" class="w3-container"> <p> Холст </p> <label>Количество пикселей по горизонтали</label> <input class="w3-input" type="number" name="widthCanvas" formControlName="widthCanvas"> <label>Количество пикселей по вертикали</label> <input class="w3-input" type="number" name="heightCanvas" formControlName="heightCanvas"> <p> Pixel </p> <label>Размер пикселя</label> <input class="w3-input" type="number" name="hwPixel" formControlName="hwPixel"> <label>Толщина сетки</label> <input class="w3-input" type="number" name="meshThickness" formControlName="meshThickness"> <label>Цвет заливки холста</label> <input class="w3-input w3-border 0 input-color" type="color" name="colorFone" formControlName="colorFone"> <hr> <div class="form-group"> <button class="w3-button w3-pale-red" (click)="onCreate()"> Создать </button> </div> <div> <hr> </div> </form>
Создаем form.component.ts
import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-form', templateUrl: './form.component.html', styleUrls: ['./form.component.scss'] }) export class FormComponent implements OnInit { @Output() pagesEvent: EventEmitter<FormGroup> = new EventEmitter<FormGroup>(); constructor() { } form: FormGroup = new FormGroup( { "heightCanvas": new FormControl("24", Validators.required), "widthCanvas": new FormControl("24", Validators.pattern("[0-9]{10}")), "hwPixel": new FormControl("10", Validators.required), "meshThickness": new FormControl("1", Validators.pattern("[0-9]{10}")), "colorFone": new FormControl("green"), } ); onCreate(): void { this.pagesEvent.emit(this.form); } }
и наконец создаем компонент нашего ядра canvas.component
canvas.component.html
<div class="w3-card-4"> <div class="w3-container w3-center"> <label for="colorWell"></label> <input list="" id="colorWell" type="color" name="colorRect" [(ngModel)]="colorRect"> <button (click)="create()">Востановить рисунок</button> <button (click)="clearPixel()">Задать пикселю цвет фона</button> </div> </div> <div class="w3-card-4 layer"> <div class="w3-container w3-center"> <canvas id="canvas" #canvas></canvas> </div> </div>
canvas.component.ts
import { AfterViewInit, Component, ElementRef, EventEmitter, OnInit, Renderer2, ViewChild } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Component({ selector: 'app-canvas', templateUrl: './canvas.component.html', styleUrls: ['./canvas.component.scss'] }) export class CanvasComponent implements AfterViewInit, OnInit { canvas: HTMLCanvasElement; innerWidth: number; innerHeight: number; rendererRef: any; numberRow: number; numberOf = 10; borderRow = 1; widthRect = 50; heightRect = 50; x = 0; y = 0 canvasX: number // X click cordinates canvasY: number // Y click cordinates colorRect = '#242323';//цвет пикселя рисовалки colorfillStyle = '#19a923';//цвет пикселя холста matr = { numberOf: 10, backgroundColor: '#19a923', data: [{ x: 0, y: 0, color: '' }] } private ctx: CanvasRenderingContext2D | null; @ViewChild('canvas') canvasRef: ElementRef; constructor(private el: ElementRef, private renderer: Renderer2, ) { this.numberRow = this.numberOf + this.borderRow; this.innerWidth = this.heightRect * this.numberRow; this.innerHeight = this.widthRect * this.numberRow; } onResize(data: any) { this.innerWidth = data.innerWidth; this.innerHeight = data.innerHeight; this.widthRect = data.widthRect; this.heightRect = data.heightRect; this.numberOf = data.numberOf; this.borderRow = data.borderRow; this.numberRow = data.numberRow; this.canvas.width = this.innerWidth; this.canvas.height = this.innerHeight this.colorfillStyle = data.colorfillStyle; this.cleardraw() this.draw() } @Input() uploadSuccess: EventEmitter<FormGroup>; ngOnInit(): void { this.uploadSuccess.subscribe(data => { this.onResize(data); }); } ngAfterViewInit(): void { this.canvas = this.canvasRef.nativeElement; this.canvas.width = this.innerWidth; this.canvas.height = this.innerHeight; let image = document.getElementById('source'); this.cleardraw() this.draw(); const data = this.canvas?.toDataURL(); if (this.rendererRef != null) { this.rendererRef(); } this.rendererRef = this.renderer.listen(this.canvasRef.nativeElement, 'click', (event) => { let cX = event.layerX; let cY = event.layerY; const offsetLeft = this.canvasRef.nativeElement.offsetLeft; const offsetTop = this.canvasRef.nativeElement.offsetTop; this.canvasX = cX - offsetLeft; this.canvasY = cY - offsetTop; this.matr.data.map(data => { if (cX >= data.x && cX < data.x + this.numberOf && cY >= data.y && cY < data.y + this.numberOf) { this.ctx.fillStyle = this.colorRect; this.ctx.fillRect(data.x, data.y, this.numberOf, this.numberOf); data.color = this.colorRect; } }) localStorage.setItem('matr', JSON.stringify(this.matr)); const data = this.canvas?.toDataURL(); }) } draw(): void { this.matr.backgroundColor = this.colorfillStyle; this.matr.numberOf = this.numberOf; for (let i = 0; i < this.heightRect; i++) { for (let j = 0; j < this.widthRect; j++) { this.ctx.fillStyle = this.colorfillStyle; this.ctx.fillRect(j * this.numberRow, i * this.numberRow, this.numberOf, this.numberOf); this.matr.data.push({ x: j * this.numberRow, y: i * this.numberRow, color: this.colorfillStyle }) } } } cleardraw(): void { this.ctx = this.canvas.getContext('2d'); this.ctx.clearRect(0, 0, this.widthRect, this.heightRect); this.matr.data = []; } create(): void { const retrievedObject = localStorage.getItem('matr'); this.matr = JSON.parse(retrievedObject); this.matr.data.map(data => { this.ctx.fillStyle = data.color; this.ctx.fillRect(data.x, data.y, this.matr.numberOf, this.matr.numberOf); }) } clearPixel(): void { this.colorRect = this.matr.backgroundColor } }
А теперь давайте разбираться что здесь происходит. Компонент состоит из методов:
onResize(data: any), draw(), cleardraw(), create(), clearPixel()
onResize(data: any) — вызывается из метода ngOnInit, когда приходит событие о том, что пользователь отправил форму создания холста.
cleardraw(): подготавливает поле к перерисовке
draw(): отрисовывает холст
create(): восстанавливает рисунок из localStorage
clearPixel(): устанавливает цвет кисти равным цвету холста
ngOnInit: вызывается фреймворком Angular один раз после установки свойств компонента, которые участвуют в привязке. Выполняет инициализацию компонента. И здесь мы подписываемся на события из родительского компонента
ngAfterViewInit: вызывается фреймворком Angular после инициализации представления компонента, а также представлений дочерних компонентов. Вызывается только один раз, здесь мы подписываемся на пользовательское событие клика и собственно здесь и происходит рисование
На сегодня это все, иду готовить продолжение.
ссылка на оригинал статьи https://habr.com/ru/post/647411/
Добавить комментарий