![]() |
Real-time Analog Clock |
|---|---|
![]() |
Interactive Fractals |
![]() |
20,000 Particles |
![]() |
Turtle Graphics |
![]() |
Bubbles |
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
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.
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)
}
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 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() |
Available from NuGet as Fable.React.DrawingCanvas.
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
looptorepeat - Renamed
inserttosub - 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








