B4J Tutorial [BANanoAPI] - Scripting the DOM: The HTML5 Canvas Story (Advanced Users)

Ola

Download

BANanoCanvas - Find the replica there for use in all BANano Projects

DISCLAIMER: This is purely for fun and learning purposes.

Another pleasure, BANanoAPI. Why the suffix API? Well, the functionality of the library used here is based on the BANanoObject. In December 2018, a thread was posted about BANanoObject - talks with javascript. and this enabled a lot of possibilities for us to maneuver our way around the javascript framework used for Web Development.

So what is this all about?

This is about DOM, BOM, CSSOM scripting using the BANanoObject.

As you will find, the source code of the library has a lot of .GetField, .SetField, and .RunMethod. Thus the helper classes created are on top of the BANanoObject and in some instances the BANanoElement.

Wait, why are you having direct javascript in the B4X IDE?

Whilst you will notice some similarities within the code examples here with some javascript code, that was just done for simplicity and speed. I just wanted to copy and paste source code and run it and just wrapped a few things to achieve that for my purposes. Yes, its a personal project, but then again, why not share it.

Remember, with BANano, we have #if javascript and .CallJavaScriptMethod, so we could have just used that syntax instead, but then again, I am adventurous, so don't be alarmed if most of the cases you don't see a lot of the BANano core code in the examples, underneath it is all its just the BANanoObject and some shortcuts.

Why re-invent the wheel?

Some things were done for fun, learn and explore what will happen.

Do you have to write code like I have done here mostly?

Absolutely NOT. For HTML elements, its rather and ALWAYS better and recommended to use the Abstract Designer. There is an independent custom view in the library that you can use outside of this API. To regress a little, on the issue of the custom view...

I tested that with some Application Modelling Language that I was curious about. A pure MVC javascript framework. The image below was the eye candy for that experiment. AML is used for prototyping apps, its old tech. It is when I was exploring that framework that this BANanoAPI was birthed.

1601159902877.png


Anyway...

Beginning the HTLM5 Canvas is no easy feat and yes there are libraries out there added on top of it that are better. The purpose though here is learning it and how it works. The best way to do that is to learn some javascript that is specific to the canvas. You get the javascript specific to the canvas, convert it to the BANano language and code your way to canvas based dev or rather use #if javascript with .CallJavaScript.

My approach was rather simple, use the existing language but make it work with BANano. This helps me learn the underlying language and at the same time also learn how to manipulate it to suit my needs. So I created some helper classes for the purpose. The helper classes are based on the BANanoObject and will work around that object ONLY.

To use the canvas, you need to create the canvas element for your page body. The example below, creates a canvas, gets the 2d context and then adds this to an existing table at RC position provided.

B4X:
Sub Skeleton(doc As JSDocument, tb As JSTable, cid As String, rowPos As Int, cellPos As Int) As JSCanvas
    'create the canvas
    Dim mycanvas As JSElement = doc.createElement("CANVAS")
    mycanvas.id = cid
    mycanvas.width = 300
    mycanvas.height = 150
    mycanvas.style.border = "1px solid #d3d3d3"
    mycanvas.innerHTML = "Your browser does Not support the HTML5 canvas tag"
    'add canvas to table
    tb.row(rowPos).cell(cellPos).empty
    tb.row(rowPos).cell(cellPos).appendChild(mycanvas)
    'creare the context
    Dim ctx As JSCanvas
    ctx.Initialize(mycanvas, "2d")
    Return ctx
End Sub

Like I said, we are using BANanoObject for this library, so we call the DOM API directly. Looking at .CreateElement, we will note that its defined like this.

B4X:
'createElement
Sub createElement(arguements As String) As JSElement
    Dim bo As BANanoObject = d.RunMethod("createElement", Array(arguements))
    Dim jse As JSElement = ToJSElement(bo)
    Return jse
End Sub

Writing this the BANano Core way would be.

B4X:
Sub CreateElement(arguments as string) as BANanoObject
      dim bo As BANanoElement
       bo.Initialize(arguments)
       return bo.ToObject
End Sub

JSElement = BANanoObject (with a name)

Whilst this thread is about DOM/BOM/CSSOM scripting

1. Creating User Interfaces by scripting the DOM is not recommended - use the abstract designer where absolutely necessary.
2. Creating Cascading Style Sheets by scripting the BOM/CSSOM is not recommended - use .css and inline styles

Things to remember

B4X:
JSWindow (BANanoObject) = BANAnoWindow
JSDocument (BANanoObject) = BANano.Window.GetField("document") / BANanoWindow.GetField("document")
body (BANanoObject) = BANano.Window.GetField("document").GetField("body")
JSElement (BANAnoObject) = BANanoElement.Initialize(?).ToObject

One of the nice examples of this exercise is this pie chart.

Day19.jpg
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay01.Init

After you create the canvas, you need to get its 2d context and then that is what you draw on.

If you could have seen a panel where you have to write your signature on your device, that is using the HTML5 Canvas.

To run each of the examples above, just uncomment, each of these lines on the demo. Open and run the library first to compile it in BANano, it does not have any dependencies, and the open the demo and then start playing..

For the code to work with the canvas, we have tried to be precise to how they write the code when creating the canvas, however, due to some conflicts and other things, some methods are suffixed with 1. These are mostly for .RunMethod(?, ?)

To make it easy to make, we have created the drawing canvases inside a table for each example.

Step 1 - call .Skeleton.

This creates a canvas, add it to a table, get its 2d context and then returns it as a JSElement (BANanoObject).

B4X:
Dim mycanvas As JSElement = doc.createElement("CANVAS")
    mycanvas.id = cid
    mycanvas.width = 300
    mycanvas.height = 150
    mycanvas.style.border = "1px solid #d3d3d3"
    mycanvas.innerHTML = "Your browser does Not support the HTML5 canvas tag"
    'add canvas to table
    tb.row(rowPos).cell(cellPos).empty
    tb.row(rowPos).cell(cellPos).appendChild(mycanvas)
    'creare the context
    Dim ctx As JSCanvas
    ctx.Initialize(mycanvas, "2d")

We then specify a fillstyle and a fillrect for the canvas..

B4X:
ctx1.fillStyle = "#ff0000"
    ctx1.fillRect1(20, 20, 150, 100)

This produces this.

1601160524325.png


Our canvas has a border and is 300x150. We draw a filled rectangle inside is that is 150x100.

Taking a look at fillRect1, this receives the passed context and then executes .RunMethod on it.

B4X:
'fillRect
Sub fillRect1(x As Int, y As Int,width As Int, height As Int) As JSCanvas
    Context.RunMethod("fillRect", Array(x,y,width,height))
    Return Me
End Sub

and fillStyle?

This property/attribute is a setter and getter defined inside the JSCanvas (BANanoObject) class for such a purpose. Its defined as.

B4X:
'set fillStyle
Sub setfillStyle(value As Object) As JSCanvas
    Context.SetField("fillStyle", value)
    Return Me
End Sub

'get fillStyle
Sub getfillStyle() As Object
    Return Context.GetField("fillStyle").Result
End Sub

As noted, these uses .GetField and .SetField methods of the BANanoObject.

So for the course of this library, with all methods, properties / attributes, we are just calling .RunMethod, .GetField, and .SetField in most of the instances of a BANanoObject called JSCanvas. On top of that we are performing Canvas context specific calls.

This enables us to "talk to javascript".
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay02.Init

We explore strokeStyles for rectangles,

Day02.jpg


As an example, we execute this canvas specific code

B4X:
Dim gradient As JSCanvas = ctx2.createLinearGradient1(0, 0, 170, 0)
    gradient.addColorStop1(0, "magenta")
    gradient.addColorStop1(0.5, "blue")
    gradient.addColorStop1(1.0, "red")
    ctx2.strokeStyle = gradient.Context
    ctx2.lineWidth = 5
    ctx2.strokeRect1(20, 20, 150, 100)
 

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay05.Init

Day05.jpg


We create some rainbows (expected to be vertical though)

And yes, we are still just calling BANanoObject related functions for the canvas 2d context.

B4X:
Dim grd10 As JSCanvas = ctx10.createLinearGradient1(0, 0, 170, 0)
    grd10.addColorStop1(0, "black")
    grd10.addColorStop1("0.3", "magenta")
    grd10.addColorStop1("0.5", "blue")
    grd10.addColorStop1("0.6", "green")
    grd10.addColorStop1("0.8", "yellow")
    grd10.addColorStop1(1, "red")
 
    ctx10.fillStyle = grd10.Context
    ctx10.fillRect1(20, 20, 150, 100)
 

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay06.Init

This is about the capping of the lines.

Day06.jpg


B4X:
ctx.beginPath1
    ctx.lineWidth = 10
    ctx.lineCap = "butt"
    ctx.moveTo1(20, 20)
    ctx.lineTo1(200, 20)
    ctx.stroke1

    ctx.beginPath1
    ctx.lineCap = "round"
    ctx.moveTo1(20, 40)
    ctx.lineTo1(200, 40)
    ctx.stroke1

    ctx.beginPath1()
    ctx.lineCap = "square"
    ctx.moveTo1(20, 60)
    ctx.lineTo1(200, 60)
    ctx.stroke1
 

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay09.Init

Day09.jpg


Some more rectangle explorations.

B4X:
' Red rectangle
    ctx1.beginPath1
    ctx1.lineWidth = "6"
    ctx1.strokeStyle = "red"
    ctx1.rect1(5, 5, 290, 140)
    ctx1.stroke1

    ' Green rectangle
    ctx1.beginPath1
    ctx1.lineWidth = "4"
    ctx1.strokeStyle = "green"
    ctx1.rect1(30, 30, 50, 50)
    ctx1.stroke1

    ' Blue rectangle
    ctx1.beginPath1
    ctx1.lineWidth = "10"
    ctx1.strokeStyle = "blue"
    ctx1.rect1(50, 50, 150, 80)
    ctx1.stroke1
 

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay10.Init

Day10.jpg


Some further eye candy about rectangles, lines, triangles etc.

The red triangle

B4X:
ctx5.beginPath1
    ctx5.moveTo1(20, 20)
    ctx5.lineTo1(20, 100)
    ctx5.lineTo1(70, 100)
    ctx5.closePath1
    ctx5.stroke1
    ctx5.fillStyle = "red"
    ctx5.fill1[/code
 

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay12.Init

Day12.jpg


Exploring curves...

B4X:
tb1.row(0).cell(2).innerText = "bezierCurveTo"
    Dim ctx2 As JSCanvas = canvdays.Skeleton(document, tb1, "ctx2", 1, 2)
    ctx2.beginPath1
    ctx2.moveTo1(10, 90)
    ctx2.bezierCurveTo1(10, 10, 90, 10, 50, 90)
    ctx2.lineTo1(90, 10)
    ctx2.lineTo1(10, 10)
    ctx2.closePath1
    ctx2.stroke1
 

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay14.Init

Day14.jpg


B4X:
Dim ctx As JSCanvas = canvdays.Skeleton(document, tb1, "ctx", 1, 0)
    ctx.fillStyle = "yellow"
    ctx.fillRect1(0, 0, 250, 100)

    ctx.transform1(1, 0.5, -0.5, 1, 30, 10)
    ctx.fillStyle = "red"
    ctx.fillRect1(0, 0, 250, 100)

    ctx.transform1(1, 0.5, -0.5, 1, 30, 10)
    ctx.fillStyle = "blue"
    ctx.fillRect1(0, 0, 250, 100)
    
    tb1.row(0).cell(1).innerHTML = "setTransform"
    Dim ctx1 As JSCanvas = canvdays.Skeleton(document, tb1, "ctx1", 1, 1)
    ctx1.fillStyle = "yellow"
    ctx1.fillRect1(0, 0, 250, 100)
    ctx1.setTransform1(1,0.5, -0.5, 1, 30, 10)
    ctx1.fillStyle = "red"
    ctx1.fillRect1(0, 0, 250, 100)
    ctx1.setTransform1(1,0.5, -0.5, 1, 30, 10)
    ctx1.fillStyle = "blue"
    ctx1.fillRect1(0, 0, 250, 100)
 

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay15.Init

Day15.jpg


Wring text on the canvas with fillText

B4X:
ctx.font = "20px Georgia"
    ctx.fillText1("Hello World!", 10, 50)
    ctx.font = "30px Verdana"
    'Create gradient
    Dim c As JSElement = document.getElementById("ctx")
    Dim gradient As JSCanvas = ctx.createLinearGradient1(0, 0, c.width, 0)
    gradient.addColorStop1("0", "magenta")
    gradient.addColorStop1("0.5", "blue")
    gradient.addColorStop1("1.0", "red")
    'Fill with gradient
    ctx.fillStyle = gradient.context
    ctx.fillText1("Big smile!", 10, 90)
 

Mashiane

Expert
Licensed User
Longtime User
On the demo source code, on the Main module, on BANano_Ready Sub, UNCOMMENT ONLY - pgCanvasDay17.Init

Day17.jpg


Attempts to read and write pixes on the canvas.

B4X:
ctx2.canvas.width = imgx.width
    ctx2.canvas.height = imgx.height
    
    'draw image on canvas
    ctx2.drawImage1(imgx, 0, 0)
    '
    Dim imgData As JSCanvas = ctx2.getImageData1(0, 0, ctx2.canvas.width, ctx2.canvas.height)
    'get each pixel
    Dim i As Int
    Dim s As Int = imgData.data.Size - 1
    For i = 0 To s
        Dim pxl As Pixel = imgData.getPixelColor(i)
        pxl.red = 255 - pxl.red
        pxl.green = 255 - pxl.green
        pxl.blue = 255 - pxl.blue
        pxl.alpha = 255
        imgData.setPixelColor1(i, pxl)
    Next    
    ctx2.putImageData1(imgData, 0, 0)

NB: The getPixelColor and .setPixelColor methods have known issues, will update examples as soon as we have a working solution

Enjoy!
 
Top