Skip to content

Latest commit

 

History

History
304 lines (252 loc) · 10.1 KB

README.md

File metadata and controls

304 lines (252 loc) · 10.1 KB

Fable.React.DrawingCanvas Nuget

Demo Apps

Real-time Analog Clock
Interactive Fractals
20,000 Particles
Turtle Graphics
Bubbles

About

This is a Fable React wrapper for canvas that allows you to declare a drawing like this:

    open Fable.React.DrawingCanvas
    open Fable.React.DrawingCanvas.Builder

    div [] [
        drawingcanvas {
            Redraw = Drawing (drawing {
                resize 400.00 400.0
                translate 200.0 200.0
                lineWidth 6.0
                beginPath
                arc 0.0 0.0 195.0 0.0 (2.0 * Math.PI) false
                stroke
            })
            Props = [ ]
        }
    ]

One more option is to pass redraw function from which you may launch missiles if you wish (this is what all presentations about pure functions fear the most):

    open Fable.React.DrawingCanvas

    div [] [
        drawingcanvas {
            Redraw = DrawFunction (fun ctx ->
                ctx.canvas.width <- 400.0
                ctx.canvas.height <- 400.0
                ctx.translate(200.0, 200.0)
                ctx.lineWidth <- 6.0
                ctx.beginPath()
                ctx.arc (0.0, 0.0, 195.0, 0.0, (2.0 * Math.PI), false)
                ctx.stroke()
            )
            Props = [ ]
        }
    ]

The clock demo linked at the top of this page includes code to draw the clock in both ways. See these files for comparison:

  • ./app/ClockUsingBuilder.fs
  • ./app/ClockUsingFunction.fs

Control Structures

This example comes from ClockUsingBuilder.fs.

    open Fable.React.DrawingCanvas
    open Fable.React.DrawingCanvas.Builder

    drawing {
        repeat [ 0 .. 59 ] (fun i ->
            preserve {
                rotate (float i * pi / 30.0)
                translate 0. (-radius + 12.)
                beginPath

                ifThenElse (i % 5 = 0)
                    (drawing {
                            moveTo  0.   6.
                            lineTo  4.0  0.0
                            lineTo  0.0 -6.0
                            lineTo -4.0  0.0
                            lineTo  0.0  6.0
                        }
                    )
                    (drawing { arc 0. 0. 3. 0. (2. * pi) false })
                fill
            })
    }
Term Explanation
drawing Builds a plain old DrawCommand list
preserve A drawing wrapped in Save and Restore
repeat Collects all iterations of the given function over the given range, and inserts a flattened DrawCommand list into the current drawing
ifThenElse Inserts one of the two given drawings according to the boolean selector
-------- The following aren't shown in the example:
ifThen Inserts the drawing according to the boolean selector
strokepath A drawing wrapped in BeginPath and Stroke
fillpath A drawing wrapped in BeginPath and Fill
sub d Include the sub-drawing d at this point. This is how you include drawings from functions

The type of drawing { } is now unit -> Drawing. This allowed the removal of the lazy keyword used in the previous release.

The type Drawing is aliased to DrawCommand list.

Turtle Graphics

You can also build drawings using turtle graphics. The Fractal demo is now implemented purely in terms of turtle { .. }.

The type of turtle { } is unit ->Drawing.

Supported commands are

Command Description
penDown Set pen down
penUp Set pen up
penColor c Set pen colour
rotateHue n Rotate pen's hue by n
increaseWidth n Increase pen's width by n
increaseAlpha n Increase pen's alpha (transparency) by n
increaseRed n Increase pen's red by n (where 0 <= red <= 1)
increaseGreen n Increase pen's green by n (where 0 <= green <= 1)
increaseBlue n Increase pen's blue by n (where 0 <= blue <= 1)
forward n Move forward distance n. A line will be drawn if the pen is down
turn a Turn by a degrees
ifThen d Conditional sub turtle drawing
ifThenElse d1 d2 Alternate sub turtle drawings
repeat seq<T> f Repeated sub turtle drawing returned from function f. Type of f is T -> (unit -> Drawing)
sub d Include the sub-drawing d at this point. This is how you include drawings from functions

Some of these commands are ported from the Elm library turtle-graphics, which is where the turtle demo comes from (see SquareSpiral.elm and SquareSpiral.png)

Examples that draw squares:

    // Long-hand
    turtle {
        penDown
        penColor "red"
        forward 100
        turn 90
        forward 100
        turn 90
        forward 100
        turn 90
        forward 100
        turn 90
    }

    // Using repeat + lambda
    turtle {
        penDown
        penColor "red"
        repeat [1..4] (fun _ -> turtle {
            forward 100
            turn 90
        })
    }

    // Using repeat + function (note iteration variable i)
    let side d i = turtle { forward d; turn 90 }

    turtle {
        penDown
        penColor "red"
        repeat [1..4] (side 100)
    }

    // Unrolled loop with function
    let side d = turtle { forward d; turn 90 }

    turtle {
        penDown
        penColor "red"
        sub (side 100)
        sub (side 100)
        sub (side 100)
        sub (side 100)
    }

Motivation

This component was inspired by Maxime Mangel's Elmish.Canvas. I created this component as a learning exercise mainly. I wanted to see if I could derive the React component entirely in Fable, and I also wanted to see how the drawing syntax would look as a Computation Expression. This is my first attempt at a CE, and while it didn't turn out as neatly as I wanted, I'm pleased that it works. I like how the CE variant removes tuple-form arguments, for example.

Inspiration for using Custom Expressions for computation expressions came from seeing how Isaac Abraham's Farmer implements its builders (such as webApp, arm etc).

API Coverage

? API Member
canvas
currentTransform
direction
fillStyle
filter
font
globalAlpha
globalCompositeOperation
imageSmoothingEnabled
imageSmoothingQuality
lineCap
lineDashOffset
lineJoin
lineWidth
miterLimit
shadowBlur
shadowColor
shadowOffsetX
shadowOffsetY
strokeStyle
textAlign
textBaseline
addHitRegion()
arc()
arcTo()
beginPath()
bezierCurveTo()
clearHitRegions()
clearRect()
clip()
closePath()
createImageData()
createLinearGradient()
createPattern()
createRadialGradient()
drawFocusIfNeeded()
drawImage()
drawWidgetAsOnScreen()
drawWindow()
ellipse()
fill()
fillRect()
fillText()
getImageData()
getLineDash()
getTransform()
isPointInPath()
isPointInStroke()
lineTo()
measureText()
moveTo()
putImageData()
quadraticCurveTo()
rect()
removeHitRegion()
resetTransform()
restore()
rotate()
save()
scale()
scrollPathIntoView()
setLineDash()
setTransform()
stroke()
strokeRect()
strokeText()
transform()
translate()

Availability

Available from NuGet as Fable.React.DrawingCanvas.

Revision History

1.3.1

  • Update Fable.React dependency

1.3

  • Update fable and dependencies

1.2

  • More turtle support to implement spiral demo

1.1

  • Basic turtle support
  • Builders return (unit -> Drawing) so we can drop need for lazy
  • Renamed loop to repeat
  • Renamed insert to sub
  • More unit tests
  • Focus more on usability of builders than plain lists, though still supported, they don't look as neat

1.0.1

  • Module name "DrawingCanvas" -> "Fable.React.DrawingCanvas"
  • Update README
  • It's a breaking change, but I'm not going to bump the major until the API is a little more complete and I've got more testing done.

1.0.0

  • Initial release