DEV Community

loading...
Cover image for I've created an awesome painting app using React and Canvas API

I've created an awesome painting app using React and Canvas API

adrianbdesigns profile image Adrian Bece Updated on ・3 min read

I've just submitted this app for Hashnode Christmas hackathon so I wanted to talk about it here in more detail.

I didn't have a chance to work with HTML canvas and the Canvas API, so this hackathon gave me a nice reason to dive right into it.

I also wanted to add something unique to make the result more magic, so I added a dynamic color brush and dynamic width brush. Turns out that this effect indeed looks more magic and dream-like.

Tech stack

  • React (with custom React hooks)
  • Canvas API
  • Native color picker and range inputs
  • Font awesome icons
  • Netlify hosting

Intro screen

Since I'm primarily a frontend developer and I want to pay special attention to design and details, I've wanted to create a nice splash screen for the app. I was inspired by the watercolor and paint set box designs.

I remember when I was buying paint sets for school, I was impressed by the images on the boxes. They showed a beautiful painting and were basically communicating "You can paint this beautiful image with this set". So I wanted to mimic that feeling with the splash screen.

If you wonder how I managed to overlay a gradient on the heading text, here is a code snippet.

  background: linear-gradient(
    90deg,
    hsl(0, 100%, 50%),
    hsl(211, 100%, 50%) 50%,
    hsl(108, 100%, 40%)
  );
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
Enter fullscreen mode Exit fullscreen mode

Custom hook

I've added the painting functionality with event listeners and Canvas API using a custom hook that returns a bunch of states and functions that are required for switching brushes, setting up a Canvas ref, and keeping track of active states.

Dynamic color and brush width

This is where the magic happens. In the magic brush mode, I'm shifting the Hue value of HSL color for each paint event. The resulting effect is a wonderful color gradient. I've also added controls to change the color gradient saturation and lightness for more options and moods.

ctx.current.strokeStyle = `hsl(${hue.current},${selectedSaturation.current}%,${selectedLightness.current}%)`;
ctx.current.globalCompositeOperation = "source-over";

hue.current++;

if (hue.current >= 360) hue.current = 0;
Enter fullscreen mode Exit fullscreen mode

Similar to the magic brush mode, I've also added a dynamic width mode that changes brush size value up and down between the minimum and maximum value. When combined with the magic brush mode, you can create some awesome art and effects.

  const dynamicLineWidth = useCallback(() => {
    if (!ctx || !ctx.current) {
      return;
    }
    if (ctx.current.lineWidth > 90 || ctx.current.lineWidth < 10) {
      direction.current = !direction.current;
    }
    direction.current ? ctx.current.lineWidth++ : ctx.current.lineWidth--;
  }, []);
Enter fullscreen mode Exit fullscreen mode

App demo

https://magic-painter.netlify.app/

Source code

Post the art you create with the app in the comments! :)

If you enjoyed this post on my hackathon project for Hashnode, check out my hackathon project for DEV x DigitalOcean


These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.

Buy Me A Coffee

Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.

Discussion (30)

pic
Editor guide
Collapse
golangch profile image
Collapse
adrianbdesigns profile image
Collapse
dhruvgarg79 profile image
Dhruv garg
const cursor = `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="%23000000" opacity="0.3" height="${width}" viewBox="0 0 ${width} ${width}" width="${width}"><circle cx="${widthHalf}" cy="${widthHalf}" r="${widthHalf}" fill="%23000000" /></svg>') ${widthHalf} ${widthHalf}, auto`;
Enter fullscreen mode Exit fullscreen mode

can you please explain this line from source code?

Collapse
adrianbdesigns profile image
Adrian Bece Author

Sure. I'm replacing the custor with an dynamic background-image svg that is a circle with a certain radius.

Collapse
dhruvgarg79 profile image
Dhruv garg

got it. thanks.

Collapse
samymp profile image
SamSam

I've had to do a similar project during my studies, which made me notice something I struggled fixing when I coded it : when you resize the window, the cursor no longer paints where you click but a few (or several) pixels away.

It's up to you to try and fix that if you want, but good job anyway :)

Collapse
jeremierousseau profile image
JeremieRousseau

yes if your canvas is not clipped to the border top and border left, you must to use something as this :

scrolledY = Math.ceil( window.scrollY );

 x = event.clientX - Id("canvasToDraw").offsetLeft;
 y = event.clientY + scrolledY  - Id("canvasToDraw").offsetTop; 
Enter fullscreen mode Exit fullscreen mode

to compute where is your point on the canvas and this point with the border, and on a android tablet i use this :
x = Math.floor( event.touches[0].clientX - Id("canvasToDraw").getBoundingClientRect().left );
y = Math.floor( event.touches[0].clientY + scrolledY - Id("canvasToDraw").getBoundingClientRect().top );

and you must manage the scroll, so the scroll is at 0 with variable scrollY.

make a real software is fun, but that can become a real headache sometime...

Collapse
jeremierousseau profile image
JeremieRousseau

My work and my paint app of nears 10 thousand of lines of code in js/html5/css : essaie.fr/

But me I use the fact that html can create a matrix of pixels, to work pixels by pixels, and I created a system at a moment where if you click on a canvas you create a matrix of pixels to be used on another matrix of pixels to making your own brush, but it's slower that your solution, my app is in black and white to be used with a eink screens/ereader, and I tried to implements all of tools than an artist can image, and finished by to try to reinvent the wheel. I search few scripts in C to translate in JS to implements more features as an antialiasing post filter, but it's difficult to find documentation to do something beyond the documentation as MDN documentation and others tutorials on the web.

Collapse
adrianbdesigns profile image
Adrian Bece Author

Wow, that is impressive! I don't think JS can handle advanced transforms like antialiasing, but keep working on it. It is very sophisticated!

My app is a bit simpler since it's done for a hackathon in a few days, but I could also add more functionalities later down the line.

Collapse
bkis profile image
bkis

Nice! Adding configurable boundaries for the oscillating color/size of the magic brush would increase the possibilities a lot! A color change is very useful, but if it always goes through the whole rainbow spectrum, you'll always have a kids room's wall painting in the end ;)

Collapse
zimlearn profile image
Dr Abstract

Tidy - it looks like you might have fun with ZIM - it is a canvas framework with components built in at zimjs.com - there is a Gen Art section on the front that has results of various drawing works including from zimjs.com/genpen. You may also want to apply damping to your motion to make the drawing smoother. You can see that at work at zimjs.com/angels - there is a book of Angels there but also a link to the tool on the first page at left. Cheers!

Collapse
telecomunicorn profile image
TelecomUnicorn

Ah, this is super cool!!! Beautiful work!

Collapse
raounek profile image
touibeg mohamed

amazing i like....

Collapse
lyavale95 profile image
LyAVALE95

Amazing, conngrats

Collapse
dineshrathee12 profile image
Collapse
adithyarafk profile image
AdithyaR-afk

Beautiful!

Collapse
raubaca profile image
Raúl Barrera

Nice, it reminds me a plain javascript version I see some time ago :)
youtube.com/watch?v=8ZGAzJ0drl0

Collapse
opauloh profile image
Collapse
helleworld_ profile image
Collapse
chinedu profile image
chinedu | ddevguys

This is Amazing, the level of creativity is top notch!❤️❤️

Collapse
joachimzeelmaekers profile image
Joachim Zeelmaekers

Amazing! Great job! 👌

Collapse
lucasandre profile image
Collapse
lfl976 profile image
lfl976

find some interesting CSS Animation

Collapse
greg_x_willis profile image
Greg X Willis

Nice. I love it.

Collapse
blakecodez profile image
blakewood84

Great job! Truly inspiring!

Collapse
adrianbdesigns profile image
Adrian Bece Author

Thank you! Glad you like it

Collapse
alexkapustin profile image
Oleksandr

Looks like you have an issue with cursor offset :)
If you look closely difference between cursor and where it's actually painting increases when you are in right bottom corner

Collapse
israelcalderon profile image
Israel Calderón

Wow this is really cool!! I've tried in mobile just for curious and it didn't work (mozilla firefox on iphone) have you addressed this issue and know how to fix it?

Collapse
adrianbdesigns profile image
Adrian Bece Author

I didn't really test it on mobile. It was a quick hackathon project. I might address it in the future.

Collapse
reotech profile image
Reotech

Nice!!

This is really amazing