Vanilla JavaScript Projects

Project Code Tour: Camera and Text

Anjana Vakil

Anjana Vakil

Software Engineer & Educator
Vanilla JavaScript Projects

Check out a free preview of the full Vanilla JavaScript Projects course

The "Project Code Tour: Camera and Text" Lesson is part of the full, Vanilla JavaScript Projects course featured in this preview video. Here's what you'd learn in this lesson:

Anjana continues exploring the Meme Me application. The camera module has additional functionality using setInterval to continuously update the camera image, simulating a live camera feed. A Text module has been added to draw the meme text on a separate canvas, making it more flexible when the text is updated.


Transcript from the "Project Code Tour: Camera and Text" Lesson

>> Now we have a camera.js, which again looks, really, really similar to what we had, if not identical. The only difference is that it is taking in a preview canvas. Argument, parameter, which would be that little canvas that's in the modal that we put there for a preview, okay?

And all this other stuff is great. It's saying, okay, if I have a preview canvas, then when the video is ready to play, and we're using our canplay listener here for that meaning like when the video is like got some stream data coming in. Then we're gonna run this drop preview function.

And then we noticed we have our like our draw video which looks the same as what we did before of drawing an image to the canvas from the video element, that's we already did that. Drawpreview looks very very similar with one small difference. The call to draw image, well it's a little bit, it's using one of those fancier, elaborate options of the ways you can tell Canvas what to do.

Essentially what this is doing is scaling down the video to fit in a smaller Canvas so that I'm not just drawing like a corner of my forehead. But this is all we looked at the docs and we saw kind of how we can access the video width and the canvas width and all of that stuff.

So this is what's happening in this call. And if I did that once I would get my just small little selfie. But what we're doing is putting that draw image inside of a set interval callback which is running every 16 milliseconds, so essentially every, I think that's, what is that, 60 hertz or something?

Whatever the fastest, basically, refresh rate that the browser and the video are both kind of able to do. Okay, so now, what else is happening in our main.js? Now, we know some of these APIs, essentially that our various modules are kind of exposing. Which, again, these are choices that, well, I made a lot of the choices, you might decide you want different choices.

And you are totally free to refactor a codebase like this as you would like. We'll talk about it in a second. And essentially, like I said, there's only one other little extra thing that is happening here that we haven't really talked about in our quick wins, which is.

Okay, so the memecanvas is that the big main Canvas. But instead of drawing directly to that, what I've done is I've split out two kind of like secret canvay elements of canvas elements. So I'm creating new elements that are never attached to the page. They're never actually rendered visually for the user to see but what this allows me to do is to have essentially separate layers one for the selfie and then one for the text, That are the same width and height as the memeCanvas, both of them.

And what that lets me do is create a little redrawMeme function, That is using again, that's same draw image. It's grabbing our memeCanvas context, and it's using our same, our good old friend drawImage, which we're just doing the simplest version of here. Because we know that we already made sure the width and the height are the same for all of these.

And you can pass in anything here that Canvas API knows how to interpret as an image, right? So we passed in a video. You could also pass in like a .jpeg or whatever that you've loaded into the browser, but we can even pass in another Canvas, because Canvas knows how to make an image of that.

That's how we could like save image by right clicking. So what we're doing is we're creating ourselves a little redraw function so that basically any time one of the two changes, either layer, we can redraw the selfie and then redraw the fresh text. So that we're not piling up just text objects on top of each other, on the Canvas, and then you wouldn't be able to read them, yeah?

>> So there's no step to [LAUGH] since the memeCanvas is made up of these other layers., you don't have to clear them out because they're stacked on each other. By the way, Canvas knows how to do its thing, right?
>> So, that's actually a very good question and it's something that we can kind of decide how we're doing.

And okay, we're jumping ahead a little bit but, let's take a look at some of these draw functions, shall we? So we have this drawText function and our text.js, and the text is really the one that's going to be a problem here because when we're redrawing. For example, in our drop preview where we're redrawing that image every 16 milliseconds.

Yes, there it is overwriting the data for the entire Canvas because it's always the same sized image. So it's essentially like blasting away all the previous pixels and putting in new pixels and blasting those away again every 16 milliseconds. But text as we saw, it doesn't take up the entire frame, it's kind of weird like transparent, the holes in the letters and stuff.

So Canvas knows how with the drawText method which we use already. It knows how to kind of figure all that out and like only overwrite the pixels that correspond to where the font based on I'm using impact and not Comic Sans or whatever. However, since that is not all of the pixels on the Canvas, when we draw that text, sorry, wrong tab.

When we draw that text, we do want to make sure that we're clearing any previous text that's on the canvas. So thank you for reminding me to mention that. And there is a method for that, there's an app for that [LAUGH], there's an API for that. And it's called, same as we had drawRect when we drew our beautiful JavaScript logo, we drew a yellow rectangle.

We also can clearRect, and if we make the Rect that we're clearing as big as the canvas so starting from 00 and going all the way to width height, the whole thing, then we're clearing the whole canvas. This is basically saying, get rid of all the pixels you had before, and then it'll be filling text like we saw before.

Just for a little better readability and contrast, as and meminess, we're also adding a stroke or sort of an outline around that text with the corresponding strokeText method. Which is, again you can go on MDN and read all about all of how all these work and everything, but essentially fill is like the inside of an object and the stroke is the outline of the object.

And we're setting up some styles, some colours, some fonts got to have that impact. [LAUGH] And that is then what we're redrawing on our new blank slate of a canvas. So what's happening is that we're not actually clearing the main canvas because every time we redraw the selfie it already sort of clears for us by redrawing all those pixels.

And then when we draw the text canvas on top of that. I want main here we are okay so then when we draw. So first we draw the selfieLayer to the memeContext, and that essentially is indistinguishable from first doing like a memecontext, clearRect width, height, 00 width height.

Because we know that the selfieLayer is the exact same dimensions. And that the video is the exact same dimensions and so it's gonna redraw all those pixels anyway. So we don't need to bother making them transparent and then filling them back in again we can just redraw on top of it.

And the canvas itself doesn't have any concept of layers, it's just pixels it knows that there's x and a y and a pixel. That's what it knows. So that's why we have to kind of implement our own layering if we want things like that transparency of the text, right?

And since we're gonna be hacking on this, y'all can find out what happens if you don't do this and why we're doing it this way. But suffice it to say. We're using all the same tools with the addition of clearRect, which is pretty analogous to drawRect. We're using all the same API's and same things that we already used.

We're also taking advantage of the fact that a canvas is an image as far as the browser is concerned. And so we can use drawImage with another canvas as data to fill in our beautiful meme. And the selfie tech, the selfieCanvas, and the selfieLayerCanvas and the textLayerCanvas, the user never actually sees those two canvases.

I don't know why the plural of Canvas is just, I'm not satisfied with any of these solutions.
>> It would be Canvi.
>> Exactly, can be like be, whatever. Anyway, neither of these two layer canvases is exposed to the user. They essentially just exist for us to save some data to, if you think of it that way.

And we had that case like a couple of times where we were for example, creating elements secretly before we actually render them by attaching them. Or appending them to elements that are in the DOM. Okay, so I believe we've now covered pretty much everything that's going on here.

Again, other than the CSS and things like that which you can peruse at your leisure. And then in terms of how I got this up onto it's the exact same thing that we just did for our selfie cam. So that's where we have a github/workflow in here, exact same file, deploy pages, YAML, and we have that vite.config.js, exact same thing, just with a different base path for the different repo name.

And everything else is like all stuff that we just did with the selfieCam.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now