Создаем приложение для рисования на HTML, CSS и JavaScript

Приложение для рисования на веб-странице — это отличная возможность для практики работы с HTML, CSS и JavaScript, а также для создания интерактивных инструментов

Введение

Приложение для рисования на веб-странице — это отличная возможность для практики работы с HTML, CSS и JavaScript, а также для создания интерактивных инструментов. В этой статье мы создадим простое приложение для рисования, которое будет включать инструменты для рисования различных фигур, выбор цветов и сохранение созданного изображения. Приложение будет иметь удобный интерфейс с несколькими инструментами, настройками размера кисти и цветов.

Для полного исходного кода и, возможно, дополнительных улучшений, загляни на наш GitHub.

Демо проекта

Подготовка

Перед тем как начать писать код, убедимся, что у нас есть необходимые компоненты:

  • HTML — для структуры страницы.

  • CSS — для стилизации интерфейса.

  • JavaScript — для функционала рисования, обработки событий и сохранения изображений.

Нам также понадобятся иконки для кнопок, которые будут использоваться для выбора инструментов рисования. Эти иконки будут загружены в нашем проекте через файлы в папке icons.

Создание HTML и подключение фото и файлов

Начнем с создания HTML-страницы. Это будет основная структура нашего приложения.

HTML создает структуру страницы и задает разметку для всех элементов, которые будут отображаться в приложении. Вот более подробное описание каждого элемента в коде HTML:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Приложение для рисования</title>
    <link rel="stylesheet" href="style.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="script.js" defer></script>
  </head>
  <body>
    <div class="container">
      <!-- Панель инструментов -->
      <section class="tools-board">
        <div class="row">
          <label class="title">Формы</label>
          <ul class="options">
            <!-- Инструмент для рисования прямоугольника -->
            <li class="option tool" id="rectangle">
              <img src="icons/rectangle.svg" alt="Rectangle">
              <span>Прямоугольник</span>
            </li>
            <!-- Инструмент для рисования круга -->
            <li class="option tool" id="circle">
              <img src="icons/circle.svg" alt="Circle">
              <span>Круг</span>
            </li>
            <!-- Инструмент для рисования треугольника -->
            <li class="option tool" id="triangle">
              <img src="icons/triangle.svg" alt="Triangle">
              <span>Треугольник</span>
            </li>
            <li class="option">
              <input type="checkbox" id="fill-color">
              <label for="fill-color">Цвет заливки</label>
            </li>
          </ul>
        </div>
        <div class="row">
          <label class="title">Опции</label>
          <ul class="options">
            <!-- Инструмент кисть -->
            <li class="option active tool" id="brush">
              <img src="icons/brush.svg" alt="Brush">
              <span>Щетка</span>
            </li>
            <!-- Инструмент ластик -->
            <li class="option tool" id="eraser">
              <img src="icons/eraser.svg" alt="Eraser">
              <span>Ластик</span>
            </li>
            <!-- Ползунок для изменения размера кисти -->
            <li class="option">
              <input type="range" id="size-slider" min="1" max="30" value="5">
            </li>
          </ul>
        </div>
        <div class="row colors">
          <label class="title">Цвета</label>
          <ul class="options">
            <!-- Кнопки выбора цвета -->
            <li class="option"></li>
            <li class="option selected"></li>
            <li class="option"></li>
            <li class="option"></li>
            <li class="option">
              <input type="color" id="color-picker" value="#4A98F7">
            </li>
          </ul>
        </div>
        <div class="row buttons">
          <!-- Кнопки очистки и сохранения -->
          <button class="clear-canvas">Очистить</button>
          <button class="save-img">Сохранить как фото</button>
        </div>
      </section>

      <!-- Холст для рисования -->
      <section class="drawing-board">
        <canvas></canvas>
      </section>
    </div>
  </body>
</html>

Объяснение HTML:

  1. <section class="tools-board">:

    • Это панель инструментов, которая содержит несколько опций для пользователя. Здесь можно выбрать форму (прямоугольник, круг, треугольник), цвет кисти, размер кисти, и другие настройки.

  2. <ul class="options">:

    • Внутри панели инструментов создаются списки (<ul>), каждый из которых представляет группу опций. Это список кнопок для выбора формы (прямоугольник, круг и т.д.), изменения цвета, размера кисти и других настроек.

  3. <canvas>:

    • Это основной холст, на котором будет происходить рисование. Он не содержит контента по умолчанию и используется JavaScript для рисования.

  4. Кнопки и ползунки:

    • Кнопки "Очистить" и "Сохранить как фото" дают возможность очистить холст и сохранить изображение соответственно. Ползунок (<input type="range">) позволяет изменять размер кисти.

  5. Цветовые кнопки:

    • В разделе цветов есть несколько кнопок для выбора цвета кисти. Каждая кнопка представляет цвет, а последний элемент — это элемент для выбора цвета с помощью стандартного цветового picker.

Стилизация

Теперь, когда структура HTML готова, можно перейти к стилизации с помощью CSS. Вот пример стилей для нашего приложения:

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: 'Poppins', sans-serif;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: #040925;
}

.container {
  display: flex;
  width: 100%;
  gap: 10px;
  padding: 10px;
  max-width: 1050px;
}

section {
  background: #fff;
  border-radius: 7px;
}

.tools-board {
  width: 210px;
  padding: 15px 22px 0;
}

.tools-board .row {
  margin-bottom: 20px;
}

.row .options {
  list-style: none;
  margin: 10px 0 0 5px;
}

.row .options .option {
  display: flex;
  cursor: pointer;
  align-items: center;
  margin-bottom: 10px;
}

.option:is(:hover, .active) img {
  filter: invert(17%) sepia(90%) saturate(3000%) hue-rotate(900deg) brightness(100%) contrast(100%);
}

.option :where(span, label) {
  color: #5A6168;
  cursor: pointer;
  padding-left: 10px;
}

.option:is(:hover, .active) :where(span, label) {
  color: #4A98F7;
}

.option #fill-color {
  cursor: pointer;
  height: 14px;
  width: 14px;
}

#fill-color:checked ~ label {
  color: #4A98F7;
}

.option #size-slider {
  width: 100%;
  height: 5px;
  margin-top: 10px;
}

.colors .options {
  display: flex;
  justify-content: space-between;
}

.colors .option {
  height: 20px;
  width: 20px;
  border-radius: 50%;
  margin-top: 3px;
  position: relative;
}

.colors .option:nth-child(1) {
  background-color: #fff;
  border: 1px solid #bfbfbf;
}

.colors .option:nth-child(2) {
  background-color: #000;
}

.colors .option:nth-child(3) {
  background-color: #E02020;
}

.colors .option:nth-child(4) {
  background-color: #6DD400;
}

.colors .option:nth-child(5) {
  background-color: #4A98F7;
}

.colors .option.selected::before {
  position: absolute;
  content: "";
  top: 50%;
  left: 50%;
  height: 12px;
  width: 12px;
  background: inherit;
  border-radius: inherit;
  border: 2px solid #fff;
  transform: translate(-50%, -50%);
}

.colors .option:first-child.selected::before {
  border-color: #ccc;
}

.option #color-picker {
  opacity: 0;
  cursor: pointer;
}

.buttons button {
  width: 100%;
  color: #fff;
  border: none;
  outline: none;
  padding: 11px 0;
  font-size: 0.9rem;
  margin-bottom: 13px;
  background: none;
  border-radius: 4px;
  cursor: pointer;
}

.buttons .clear-canvas {
  color: #6C757D;
  border: 1px solid #6C757D;
  transition: all 0.3s ease;
}

.clear-canvas:hover {
  color: #fff;
  background: #6C757D;
}

.buttons .save-img {
  background: #4A98F7;
  border: 1px solid #4A98F7;
}

.drawing-board {
  flex: 1;
  overflow: hidden;
}

.drawing-board canvas {
  width: 100%;
  height: 100%;
}

Этот CSS оформляет элементы, такие как кнопки инструментов, цвета и размеры, создавая современный и удобный интерфейс.

Написание логики на JavaScript

Теперь перейдем к самой интересной части — функционалу рисования. Мы будем использовать JavaScript, чтобы обрабатывать события, связанные с рисованием на холсте. Код JavaScript подключается через файл script.js, который мы рассмотрим ниже:

JavaScript код управляет функциональностью рисования на холсте, взаимодействует с пользователем и обновляет отображение в зависимости от выбранных настроек. Рассмотрим детали каждого фрагмента кода.

Инициализация переменных:

const canvas = document.querySelector("canvas"),
    toolBtns = document.querySelectorAll(".tool"),
    fillColor = document.querySelector("#fill-color"),
    sizeSlider = document.querySelector("#size-slider"),
    colorBtns = document.querySelectorAll(".colors .option"),
    colorPicker = document.querySelector("#color-picker"),
    clearCanvas = document.querySelector(".clear-canvas"),
    saveImg = document.querySelector(".save-img"),
    ctx = canvas.getContext("2d");

let prevMouseX, prevMouseY, snapshot,
    isDrawing = false,
    selectedTool = "brush",
    brushWidth = 5,
    selectedColor = "#000";
  • canvas: Ссылается на элемент <canvas> на странице, на котором будет происходить рисование.

  • toolBtns: Список всех кнопок инструментов (щетка, ластик и формы), с которыми пользователь может взаимодействовать.

  • fillColor: Переменная для отслеживания состояния чекбокса "Цвет заливки".

  • sizeSlider: Ссылка на ползунок для выбора размера кисти.

  • colorBtns: Переменная для всех кнопок выбора цвета, которые пользователь может кликать для выбора цвета.

  • colorPicker: Ссылка на стандартный HTML-пикер цвета.

  • clearCanvas и saveImg: Кнопки для очистки холста и сохранения изображения.

  • ctx: Контекст рисования канваса, с помощью которого рисуются линии, фигуры и выполняются другие операции.

Установка фона канваса:

const setCanvasBackground = () => {
    ctx.fillStyle = "#fff";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = selectedColor;
}

Этот код устанавливает белый фон для холста, затем восстанавливает текущий цвет кисти для рисования.

Инициализация холста после загрузки страницы:

window.addEventListener("load", () => {
    canvas.width = canvas.offsetWidth;
    canvas.height = canvas.offsetHeight;
    setCanvasBackground();
});

При загрузке страницы устанавливается размер холста, соответствующий его текущим размерам на экране (через offsetWidth и offsetHeight). Затем вызывается функция для установки фона.

Обработчики событий:

  1. Обработчики выбора инструмента (щетка, ластик и фигуры):

toolBtns.forEach(btn => {
    btn.addEventListener("click", () => {
        document.querySelector(".options .active").classList.remove("active");
        btn.classList.add("active");
        selectedTool = btn.id;
    });
});

Каждая кнопка инструмента (toolBtns) добавляет обработчик события на клик, который делает кнопку активной, добавляя класс active, и меняет выбранный инструмент, обновляя переменную selectedTool.

  1. Изменение размера кисти:

sizeSlider.addEventListener("change", () => brushWidth = sizeSlider.value);

Когда пользователь меняет значение ползунка, обновляется размер кисти.

  1. Изменение цвета:

colorBtns.forEach(btn => {
    btn.addEventListener("click", () => {
        document.querySelector(".options .selected").classList.remove("selected");
        btn.classList.add("selected");
        selectedColor = window.getComputedStyle(btn).getPropertyValue("background-color");
    });
});

Каждая кнопка выбора цвета обновляет текущий цвет кисти (selectedColor) и делает выбранную кнопку активной, добавляя класс selected.

  1. Использование цветового пикера:

colorPicker.addEventListener("change", () => {
    colorPicker.parentElement.style.background = colorPicker.value;
    colorPicker.parentElement.click();
});

Когда пользователь выбирает цвет через стандартный HTML-пикер, цвет обновляется и на кнопке.

  1. Очистка холста:

clearCanvas.addEventListener("click", () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    setCanvasBackground();
});

При нажатии на кнопку "Очистить" весь холст очищается, и фон восстанавливается.

  1. Сохранение изображения:

saveImg.addEventListener("click", () => {
    const link = document.createElement("a");
    link.download = `${Date.now()}.jpg`;
    link.href = canvas.toDataURL();
    link.click();
});

При нажатии на кнопку "Сохранить как фото" создается временная ссылка для скачивания изображения, преобразуя содержимое холста в формат data URL.

Рисование на холсте:

const startDraw = (e) => {
    isDrawing = true;
    prevMouseX = e.offsetX;
    prevMouseY = e.offsetY;
    ctx.beginPath();
    ctx.lineWidth = brushWidth;
    ctx.strokeStyle = selectedColor;
    ctx.fillStyle = selectedColor;
    snapshot = ctx.getImageData(0, 0, canvas.width, canvas.height);
}

const drawing = (e) => {
    if (!isDrawing) return;
    ctx.putImageData(snapshot, 0, 0);
    if (selectedTool === "brush" || selectedTool === "eraser") {
        ctx.strokeStyle = selectedTool === "eraser" ? "#fff" : selectedColor;
        ctx.lineTo(e.offsetX, e.offsetY);
        ctx.stroke();
    } else if (selectedTool === "rectangle") {
        drawRect(e);
    } else if (selectedTool === "circle") {
        drawCircle(e);
    } else {
        drawTriangle(e);
    }
}

canvas.addEventListener("mousedown", startDraw);
canvas.addEventListener("mousemove", drawing);
canvas.addEventListener("mouseup", () => isDrawing = false);
  • startDraw: Когда пользователь нажимает на холст, начинается рисование. Сохраняются начальные координаты, и создается снимок текущего состояния холста (для отмены).

  • drawing: Когда пользователь двигает мышью, выполняется рисование в зависимости от выбранного инструмента.

В общем, HTML создает интерфейс и структуру, а JavaScript управляет всем функционалом рисования, изменением инструментов и сохранением рисунка.

Отлично. А вот так выглядит весь java script код:

const canvas = document.querySelector("canvas"),
    toolBtns = document.querySelectorAll(".tool"),
    fillColor = document.querySelector("#fill-color"),
    sizeSlider = document.querySelector("#size-slider"),
    colorBtns = document.querySelectorAll(".colors .option"),
    colorPicker = document.querySelector("#color-picker"),
    clearCanvas = document.querySelector(".clear-canvas"),
    saveImg = document.querySelector(".save-img"),
    ctx = canvas.getContext("2d");

let prevMouseX, prevMouseY, snapshot,
    isDrawing = false,
    selectedTool = "brush",
    brushWidth = 5,
    selectedColor = "#000";

const setCanvasBackground = () => {
    ctx.fillStyle = "#fff";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = selectedColor;
}

window.addEventListener("load", () => {
    canvas.width = canvas.offsetWidth;
    canvas.height = canvas.offsetHeight;
    setCanvasBackground();
});

// Функции для рисования форм
const drawRect = (e) => {
    if (!fillColor.checked) {
        return ctx.strokeRect(e.offsetX, e.offsetY, prevMouseX - e.offsetX, prevMouseY - e.offsetY);
    }
    ctx.fillRect(e.offsetX, e.offsetY, prevMouseX - e.offsetX, prevMouseY - e.offsetY);
}

const drawCircle = (e) => {
    ctx.beginPath();
    let radius = Math.sqrt(Math.pow((prevMouseX - e.offsetX), 2) + Math.pow((prevMouseY - e.offsetY), 2));
    ctx.arc(prevMouseX, prevMouseY, radius, 0, 2 * Math.PI);
    fillColor.checked ? ctx.fill() : ctx.stroke();
}

const drawTriangle = (e) => {
    ctx.beginPath();
    ctx.moveTo(prevMouseX, prevMouseY);
    ctx.lineTo(e.offsetX, e.offsetY);
    ctx.lineTo(prevMouseX * 2 - e.offsetX, e.offsetY);
    ctx.closePath();
    fillColor.checked ? ctx.fill() : ctx.stroke();
}

// Обработчики для инструментов
toolBtns.forEach(btn => {
    btn.addEventListener("click", () => {
        document.querySelector(".options .active").classList.remove("active");
        btn.classList.add("active");
        selectedTool = btn.id;
    });
});

// Обработчики для кисти
sizeSlider.addEventListener("change", () => brushWidth = sizeSlider.value);

colorBtns.forEach(btn => {
    btn.addEventListener("click", () => {
        document.querySelector(".options .selected").classList.remove("selected");
        btn.classList.add("selected");
        selectedColor = window.getComputedStyle(btn).getPropertyValue("background-color");
    });
});

colorPicker.addEventListener("change", () => {
    colorPicker.parentElement.style.background = colorPicker.value;
    colorPicker.parentElement.click();
});

// Очистка холста
clearCanvas.addEventListener("click", () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    setCanvasBackground();
});

// Сохранение изображения
saveImg.addEventListener("click", () => {
    const link = document.createElement("a");
    link.download = `${Date.now()}.jpg`;
    link.href = canvas.toDataURL();
    link.click();
});

// Рисование
const startDraw = (e) => {
    isDrawing = true;
    prevMouseX = e.offsetX;
    prevMouseY = e.offsetY;
    ctx.beginPath();
    ctx.lineWidth = brushWidth;
    ctx.strokeStyle = selectedColor;
    ctx.fillStyle = selectedColor;
    snapshot = ctx.getImageData(0, 0, canvas.width, canvas.height);
}

const drawing = (e) => {
    if (!isDrawing) return;
    ctx.putImageData(snapshot, 0, 0);
    if (selectedTool === "brush" || selectedTool === "eraser") {
        ctx.strokeStyle = selectedTool === "eraser" ? "#fff" : selectedColor;
        ctx.lineTo(e.offsetX, e.offsetY);
        ctx.stroke();
    } else if (selectedTool === "rectangle") {
        drawRect(e);
    } else if (selectedTool === "circle") {
        drawCircle(e);
    } else {
        drawTriangle(e);
    }
}

canvas.addEventListener("mousedown", startDraw);
canvas.addEventListener("mousemove", drawing);
canvas.addEventListener("mouseup", () => isDrawing = false);

Этот код реализует:

  • Рисование различных фигур (прямоугольник, круг, треугольник).

  • Выбор инструментов, включая кисть, ластик и опции заливки.

  • Изменение цвета и размера кисти.

  • Очистка холста и сохранение изображения.

Выводы и заключение

Мы создали простое приложение для рисования на HTML, CSS и JavaScript, которое позволяет рисовать различные фигуры, изменять цвет и размер кисти, а также сохранять изображение. Это приложение может быть расширено добавлением дополнительных инструментов, улучшением интерфейса и интеграцией с сервером для сохранения и обмена рисунками.

Бесплатно
Кодик: Интерактивное обучение!
Изучай HTML, JavaScript, CSS, Python, PHP, SQL, Git
Проходи практические уроки!
Получи сертификат!
Вам может быть интересно

Не нашли нужной статьи?
Напишите нам и ее сделаем!

Бесплатно
Кодик: Интерактивное обучение!
Изучай HTML, JavaScript, CSS, Python, PHP, SQL, Git
Проходи практические уроки!
Получи сертификат!
Главная
Курсы
Блог
Меню