Chapter 1:
Learn basic Canvas Concepts

Step 1 - Add the Canvas component

Start with a new Anvil app with a Material 3 theme. We’ll start by adding the Canvas component to the startup Form.

You’ll find the Canvas component in the More Components section.

Adding a Canvas to the Form

Adding a Canvas to the Form

Step 2 - Setting the Canvas width and height

You can set the Canvas height using the Properties Panel, but the width property is not set that way. Instead, the width of the Canvas, like other Anvil components, is determined by the container you put the Canvas in. For us that’s the ColumnPanel that contains the Form component. You can use the Canvas’s get_width() function to see how wide that is.

For many uses of the Canvas, you want to set a specific width and height and for the Canvas to be centered in its container. The width is controlled by the container, and is based on the size of the browser window. That can vary widely based on the device being used (e.g. mobile, desktop). We’re going to set the height and then make sure to only draw in the right area of the Canvas. For example, if we wanted our Canvas to be 500 by 500, we would set the height to be 500 and then only draw in the center 500 pixels of the Canvas. That way the Canvas could be as wide as the container wants to make it, but to the user it will seem as if it’s only 500 pixels wide and centered in the container.

Put this code into the __init__ function of the Form to set the height to our desired size:

    def __init__(self, **properties):
        # Set Form properties and Data Bindings.
        self.init_components(**properties)

        self.canvas_size = 500
        self.canvas_1.height = self.canvas_size
        self.canvas_1.reset_context() 

The reset_context() function must be called anytime the Canvas either changes size or needs to be redrawn.

Step 3 - Coding the reset handler

Now we’re going to add code to draw on the Canvas.

The reset event handler function is called whenever the Canvas needs to be redrawn, such as when reset_context() is called, or the browser window is resized. All drawing on the Canvas should be in this function. Select the Canvas and use the Object Palette to add a reset event handler.

Setting a reset_context event handler on the Canvas

Setting a reset_context event handler on the Canvas

Now we’re going to add some basic code to draw a square and a circle.

Remember that we also want to only draw in the center self.canvas_size pixels of the Canvas, so it appears that our Canvas is that wide and centered. We do that using two techniques.

The first is that we use the Canvas’s translate function to reset the 0,0 coordinates to the right spot so the drawing area will be centered. The second is that we use the Canvas’s clip function to specify which area of the Canvas to draw in.

    def canvas_1_reset(self, **event_args):
        # Adjust these coordinates if you want the drawing area to not be centered
        self.canvas_offset = (self.canvas_1.get_width() - self.canvas_size)/2
        self.canvas_1.translate(self.canvas_offset, 0)

        # Restrict drawing to the section that we want visible
        self.canvas_1.begin_path()
        self.canvas_1.move_to(0, 0)
        self.canvas_1.line_to(self.canvas_size, 0)
        self.canvas_1.line_to(self.canvas_size,self.canvas_size)
        self.canvas_1.line_to(0, self.canvas_size)
        self.canvas_1.close_path()
        self.canvas_1.clip()
        
        # Draw a square
        self.canvas_1.begin_path()
        self.canvas_1.move_to(100, 100)
        self.canvas_1.line_to(150, 100)
        self.canvas_1.line_to(150, 150)
        self.canvas_1.line_to(100, 150)
        self.canvas_1.close_path()
        self.canvas_1.stroke() 

        # Draw a circle
        self.canvas_1.begin_path()
        self.canvas_1.arc(300, 100, 50)
        self.canvas_1.close_path()
        self.canvas_1.stroke()
        self.canvas_1.fill()

In general, you must call begin_path(), then execute some functions to add to the path, and finally call close_path(). Calling stroke() will outline the last path created, while calling fill() will fill in the last path created.

If you’re not familiar with Canvas drawing, see the Anvil Canvas docs.

Now run the app and you’ll see the square and circle drawn:

In the next chapter, we’ll use a model of information to draw shapes on the Canvas.

Chapter complete

Congratulations, you've completed this chapter!