2
\$\begingroup\$

So I have recently started learning web dev (including React since a few days ago), and I have tried to implement a simple drawing website that lets you change the brush size, color, and save the picture when you're finished.

I would appreciate any advice regarding style, good practices in React, etc.!

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

index.css

body, html {
    margin: 0;
    padding: 0;
}

App.jsx

import './App.css'
import DrawingCanvas from './DrawingCanvas'
import ColorButton from './ColorButton'
import { useState } from 'react'

function App() {
    const [color, setColor] = useState('black')
    const [strokeSize, setStrokeSize] = useState(10)
    const [canvasPNGDataURL, setCanvasPNGDataURL] = useState('')
    const colors = ['black', 'white', 'red', 'yellow', 'blue', 'green', 'purple', '#ff69b4', '#ff8c00', '#00ffff']

   return (
    <div className='outer-container'>
        <div className='top-bar'>
            <input className='stroke-size-input' value={strokeSize} type="text" onChange={(e) => {setStrokeSize(e.target.value)}}/>
            <div className='tool-bar'>
                {colors.map((color) => {
                    return <ColorButton key={color} color={color} colorSetFunction={setColor}></ColorButton>
                })}
            </div>
            <a className="save-button" href={canvasPNGDataURL} download="my-picture">Save</a>
        </div>
        <DrawingCanvas style="flex-grow: 1;" strokeSize={parseInt(strokeSize)} color={color} canvasPNGDataSetter={setCanvasPNGDataURL}></DrawingCanvas>
    </div>
  )
}

export default App

App.css

.top-bar {
    background-color: black;
    height: 120px;
    display: flex;
    align-items: center;
}

.top-bar > * {
    flex-shrink: 0;
}

.stroke-size-input {
    height: 50%;
    width: 120px;
    margin-left: 40px;
    margin-right: 40px;
    font-family: Arial, sans-serif;
    font-size:xx-large;
}

.tool-bar {
    flex-grow: 1;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: space-around;
}

.save-button {
    position: relative;
    height: 40%;
    width: 100px;
    margin-left: 40px;
    margin-right: 40px;
    background-color: black;
    font-family: Arial, sans-serif;
    text-decoration: none;
    color: white;
    font-weight: bold;
    font-size: xx-large;
    inset: 0;
    text-align: center;
    align-content: center;
}

.outer-container {
    display: flex;
    flex-direction: column;
}

ColorButton.jsx

import React from "react";
import PropTypes from "prop-types";
import "./ColorButton.css";

export default function ColorButton({ color, colorSetFunction }) {
    function applyColor() {
        colorSetFunction(color);
    }

    return (
        <button onClick={() => {applyColor()}} style={{ backgroundColor: color }}></button>
    );
}

ColorButton.propTypes = {
    color: PropTypes.string.isRequired,
    colorSetFunction: PropTypes.func.isRequired
};

ColorButton.css

button {
    height: 80px;
    width: 80px;
    border: white solid 1px;
    cursor: pointer;
}

button:active {
    opacity: 0.5;
}

DrawingCanvas.jsx

import { useEffect, useRef, useState } from 'react'
import './DrawingCanvas.css'

import PropTypes from 'prop-types';

function DrawingCanvas({ strokeSize, color, canvasPNGDataSetter }) {
  let canvasContext = useRef(null)
  const [isDrawing, setIsDrawing] = useState(false)

  useEffect(() => {
    let canvas = document.querySelector('#main-canvas')
    canvasContext.current = canvas.getContext('2d')
    canvasContext.current.lineJoin = 'round';
    canvasContext.current.lineCap = 'round';
    canvasContext.current.strokeSize = strokeSize;
  }, [])

  useEffect(() => {
    canvasContext.current.strokeStyle = color;
  }, [color])

  useEffect(() => {
    canvasContext.current.fillStyle = 'white';
    canvasContext.current.fillRect(0, 0, 1000, 1000)
  }, [])
  
  useEffect(() => {
      const canvas = document.getElementById('main-canvas')
      canvasPNGDataSetter(canvas.toDataURL('image/png'))
  })

  useEffect(() => {
    canvasContext.current.lineWidth = strokeSize
  }, [strokeSize])

  function drawIfMousePressed(event) {
    if (event.buttons !== 1) {
      closeDrawPath()
      return
    }
    drawNextLine(event)
  }

  function drawNextLine(event) {
    let rect = event.target.getBoundingClientRect()
    let x = event.clientX - rect.left
    let y = event.clientY - rect.top

    if (!isDrawing) {
      openDrawPath()
      canvasContext.current.moveTo(x, y)
    } else {
      canvasContext.current.lineTo(x, y)
      canvasContext.current.stroke()
    }
  }

  function closeDrawPath() {
    canvasContext.current.closePath()
    setIsDrawing(false)
  }

  function openDrawPath() {
    canvasContext.current.beginPath()
    setIsDrawing(true)
  }

  return (
    <div className="drawing-canvas-container">
      <canvas className="drawing-canvas" id="main-canvas" height="1000" width="1000" onMouseMove={drawIfMousePressed} onMouseOut={closeDrawPath}></canvas>
    </div>
  )
}

DrawingCanvas.propTypes = {
    strokeSize: PropTypes.number.isRequired,
    color: PropTypes.string.isRequired,
    canvasPNGDataSetter: PropTypes.func.isRequired
};

export default DrawingCanvas

DrawingCanvas.css

.drawing-canvas-container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    outline: solid green;
    padding-top: 75px;
    padding-bottom: 75px;
}

.drawing-canvas {
    outline: solid black;
    height: 1000px;
    width: 1000px;
}
\$\endgroup\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.