joakin / elm-canvas Goto Github PK
View Code? Open in Web Editor NEWA canvas drawing library for Elm
Home Page: https://package.elm-lang.org/packages/joakin/elm-canvas/latest/
License: BSD 3-Clause "New" or "Revised" License
A canvas drawing library for Elm
Home Page: https://package.elm-lang.org/packages/joakin/elm-canvas/latest/
License: BSD 3-Clause "New" or "Revised" License
Right when a texture fails to load for some reason, we can't tell why it failed because we just get a Nothing
. Could you change this API to use a Result Http.Error Texture
or even just a Result String Texture
describing the failure?
fillText
seems to be missing from the newer versions of elm-canvas and there doesn't appear to be another documented method for setting text color.
playing with this library, looks great so far.
question though. would it not make more sense for type matching to use the native elm color instead of custom CanvasColor?
I might be missing the reason for not, just mentioning it in case.
This would allow for using other libraries for color like elm-visualization
and color scales.
In your example Textures.elm
line 147, If you delete rect ( 0, 0 ) w h
, then everything becomes strange.
It seems that you have to add a "curtain" behind everything, is it normal?
What's the principle of it?
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/roundRect
This shape isn't trivial to implement with paths, unfortunately.
It would be nice if there would be a way to make background transparent.
The function is pretty confusing to use, at least some docs should help with it.
Hi!
I banged my head on this one :)
I'm pretty sure the bug is in the library so here is my bug report.
The bug cannot be observed by opening the documented examples, such as this one: https://chimeces.com/elm-canvas/ellie.html
But the bug can be observed by opening the documented starter template example, here: https://ellie-app.com/62Dy7vxsBHZa1
The code of both examples is mostly the same, but if you look carefully at the ellie template, canvas and elm-canvas don't share the same height:
This causes problems with edge detection (I wanted to animate a bouncing ball).
After much head scratching, I noticed that removing <!DOCTYPE html>
from my HTML document made the problem disappear! And this is consistent with the two examples mentioned above: the one displaying the problem does have a <!DOCTYPE html>
decleration, the one that is not displaying the problem does not have a <!DOCTYPE html>
declaration.
See the effect of removing/adding the <!DOCTYPE html>
declaration below:
I found writing custom CSS rules quite tricky with this library, so in the end I followed the starter template's suggestion.
Maybe all that's required is suggesting better CSS defaults?
I tested your code and examples, but nothing shows up.
Is this repo still maintained?
I'm trying to draw a canvas that spans the entire width and height of the window and resizes dynamically when the window size changes. However, the canvas isn't getting painted on the initial page load, but only on subsequent updates. I've tried multiple implementations but the same problem keeps recurring.
Here's an Ellie showing the issue. Notice how the screen is blank until after a keypress event fires the update, and then the canvas appears.
As a work-around, I can change the screen
record into a Maybe
, however that just shifts the problem from occurring on initial screen load to on resize.
And just for the sake of good record-keeping here's the code from the Ellie linked above:
module Main exposing (..)
import Browser
import Browser.Dom exposing (Viewport, getViewport)
import Browser.Events exposing (onKeyDown, onResize)
import Canvas exposing (..)
import Canvas.Settings exposing (..)
import Canvas.Settings.Advanced exposing (..)
import Color
import Html exposing (Html, div)
import Html.Attributes exposing (style)
import Json.Decode as Decode
import Task
main : Program () Model Msg
main =
Browser.element
{ init = init
, view = view
, subscriptions = subscriptions
, update = update
}
-- MODEL
type alias Model =
{ screen : { width : Int, height : Int }
}
init : () -> ( Model, Cmd Msg )
init _ =
( { screen = { width = 800, height = 600 }
}
, Task.perform (\{ viewport } -> ScreenSize (round viewport.width) (round viewport.height)) getViewport
)
-- UPDATE
type Msg
= TurnLeft
| TurnRight
| MoveForward
| Other
| ScreenSize Int Int
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
TurnLeft ->
( model, Cmd.none )
TurnRight ->
( model, Cmd.none )
MoveForward ->
( model, Cmd.none )
ScreenSize w h ->
( { model | screen = { width = w, height = h } }
, Cmd.none
)
Other ->
( model, Cmd.none )
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.batch
[ onKeyDown keyDecoder
, onResize ScreenSize
]
keyDecoder : Decode.Decoder Msg
keyDecoder =
Decode.map toDirection (Decode.field "key" Decode.string)
toDirection : String -> Msg
toDirection string =
case string of
"ArrowLeft" ->
TurnLeft
"ArrowRight" ->
TurnRight
"ArrowUp" ->
MoveForward
_ ->
Other
-- VIEW
clearScreen : Float -> Float -> Renderable
clearScreen width height =
shapes [ fill Color.black ] [ rect ( 0, 0 ) width height ]
view : Model -> Html Msg
view { screen } =
div
[ style "display" "flex"
, style "justify-content" "center"
, style "align-items" "center"
]
[ Canvas.toHtml
( screen.width, screen.height )
[]
[ clearScreen (toFloat screen.width) (toFloat screen.height)
, shapes [ fill Color.red ] [ rect ( 30, 30 ) 200 200 ]
]
]
(Per discussion on Slack.)
Currently there are no functions along the lines of group : List Setting -> List Renderable -> Renderable
. As such, if one has e.g. a standard transformation, there is no way to apply it to all (or some) of one's Renderable
s, rather than having to apply it to each individually.
While the canvas API does not natively support groups, it is a common feature in many other interfaces to it, as well as e.g. elm-playground
.
When I get ~100 canvases on page and change something on one of them, all canvases are redrawn.
If this change is something intensive as mousemove, the CPU load jumps and stays high until I stop changing the canvas.
Is it possible to not redraw canvases for which draw commands did not change? Will commands diffing in set cmds (values)
be less CPU-intensive than redrawing?
When drawing a path, to fill it, it seems I have to add the first point at the end again. The closePath
function does that in JS. I would like an equivalent in the elm-canvas package. Or did I miss something?
Images downloaded as textures seems not to use crossOrigin = "Anonymous"
flag and they make my canvas tained.
I've created this Ellie (https://ellie-app.com/86XKmYD9Mxya1) that loads a texture, just popup the browser console and execute this
var tmpCtx=document.createElement("canvas").getContext("2d");
tmpCtx.drawImage(document.querySelectorAll("img[src='https://www.alsipnursery.com/wp-content/uploads/2019/04/1703190.jpg']")[0],0,0);
tmpCtx.getImageData(1,1,1,1);
You can see that on my project https://kubaracek.github.io/hanggliding-map/ that uses map tiles
var tmpCtx=document.createElement("canvas").getContext("2d");
tmpCtx.drawImage(document.querySelectorAll("img[src='https://tile.nextzen.org/tilezen/terrain/v1/256/terrarium/11/1087/715.png?api_key=Nl9_q6yZQA6NIJp6Zn9kYg']")[0],0,0);
tmpCtx.getImageData(1,1,1,1);
Interesting this is that on my project the image returns: access-control-allow-origin: *
First off, awesome package! I'm having fun with it so far.
I was curious if you had any plans for an API to get/put raw image data? It seems like might be possible with Texture, but I'm not too sure.
Thanks!
I have an app that gets a list of files and sends them as List D.Value
to a port, on js side I do this:
File -> Image -> Canvas -> downscale -> Canvas -> Image -> send it back to elm through port.
I send Images back one by one as soon as any of them is ready. Then in elm I decode them with Canvas.Texture.fromDomImage
and save in a list in model, then draw them in view with List.map
.
Often that breaks elm app in Firefox. Sometimes works ok. With the same images. When it breaks - it is always the same error, I made screenshots.
I've updated Firefox to the latest version. This does not happen in Chrome or Safari.
I really like this package! I'd love to help anyway I can!
The app I am working on is here:
https://github.com/shepelevstas/elmphoto.git
Hello! First of all - Great package!!! Thanks you!!! =D
In my Model I have List {texture:Canvas.Texture, id:Int}
when I delete on of textures with { model | textures = List.filter (.id >> (/=) delete_id) model.textures }
in update function - all canvases (drawn with List.map drawCanvas model.textures
) that have greater id
than the deleted one - become blank. They are redrawn as soon as I mouseover one of canvases.
Am I doing it wrong? Or is it a bug? Are there any workarounds?
Thank you!
I find that rendering text on a hidpi screen (in my case, just Windows 10 Chrome with interface set to 175%) is very blurry. I have read here:
https://www.html5rocks.com/en/tutorials/canvas/hidpi
and here:
https://groups.google.com/forum/#!topic/elm-discuss/pq1KS8XgFiQ
that this could be addressed by a few lines of JS trickery.
Any chance of some kind of support for that here?
It looks like the trick that they use is a drop-in option that shouldn't disturb any existing code that uses canvas and should still look perfect on non-HiDPI screens. :o
I know it's a titan task for a pure language like elm to juggle with referencing image resources across DOM elements.
Please point out the lack of support and the reasons why in your README and the elm package docs.
And please consider to find a way for base64 dataUrls as a start.
From slack, if you want to make textures directly in JS and pass them to Elm as flags/ports, it should be possible.
Should be easy to add and backwards compatible:
Canvas.Texture.fromDomImage : Json.Decode.Value -> Maybe Texture
Things to keep in mind:
Value
is actually a DOM image and that it has everything we needSome notes for docs/example:
How to load images in JS to pass to Elm:
function loadImage(src) {
return new Promise((resolve, reject) => {
let image = new Image();
image.onload = () => resolve(image)
image.onerror = (err) => reject(err)
image.src = src
})
}
let images = await Promise.all(imageUrls.map(loadImage))
image
An element to draw into the context. The specification permits any canvas image source (CanvasImageSource), specifically, a CSSImageValue, an HTMLImageElement, an SVGImageElement, an HTMLVideoElement, an HTMLCanvasElement, an ImageBitmap, or an OffscreenCanvas.
It may be easy to add support for these other values (SVG, Video, other canvas, at least). Could warrant an investigation.
It should be rendered with only fill, like the docs mention:
It looks like for some reason, if neither fill nor stroke is set, it defaults to both a fill and a stroke of black
But that contradicts the documentation which states
By default, renderables are drawn with no visible stroke.
Reported by @SiriusStarr in the Elm slack
Canvas.toHtml seems unresponsive to changes in the model:
https://ellie-app.com/4kq2Hz8699za1
In the Ellie example, the canvas element is initialized with width = 0 and it stays that way even when model.width is updated.
Namely: If I serve the canvas through reactor or through a compiled Html file, it's always just a transparent rectangle no matter what I put into it. I've tested this on up to date Opera, Chrome, and Firefox on Windows 10.
OTOH when I try the same thing (exact same code) with Ellie it seems to work fine in those same browsers: https://ellie-app.com/38zhvnLGCKMa1 :o
Do you reproduce the same, and/or is this a known issue at present? Or is what I describe difficult for others to reproduce?
Explain how they apply to the global coordinate system and add graphics.
Came from a conversation in #canvas in the elm slack.
For example this caused confusion thinking they were applied backwards: https://ellie-app.com/6D74dVWffsGa1
This was used to clarify: https://ellie-app.com/8gzr9vF4WHpa1
We need better docs around this.
And maybe how to install it explicitly to access it in your code
elm install avh4/elm-color
Refactoring the p5 examples to work with last version (4.0.1); found that the background color shrinks to a small height on the canvas.
Compare color-hue from docs[1] (canvas version 3.0.5) with color-hue refactored (canvas version 4.0.1)
the same happens with reactor.
[1] that doesn't compile on ellie, it seems that it ignores the version number and just uses the latest; but works just fine in a normal desktop setup. It compiles and the background color displays correctly.
Is it possible to apply a filter to Renderable object? (CanvasRenderingContext2D.filter)
I should expect that we could maintain the angles and just reverse the orientation of the arcs.
But trying to make a pie chart, I've found that only using startAngle > endAngle with clockwise = False works.
Using counter clockwise, this one do not work as expected. This other one, do work but it's rather confusing that startAngle must be greater than endAngle. Besides it's putting every arc next to the other in a clockwise manner. I except for the first one to be red in the top right quadrant, green on the top-left one and blue in the lower one.
It's like the angles always are clockwise and fall in the same way no matter if clockwise it's true or false, but only it fills well if the direction of the fill it's in accordance with clock direction (startAngle > endAngle for clockwise, the other way around for anti-clockwise) very confusing.
I was attempting to replicate p5 examples, namely pie chart.
Found a glitch with clockwise=True. The center of the arc seems a little off the provided point.
using clockwise induces a glitch. Using anti-clockwise doesn't, but it's really confusing.
Hi @joakin! Thanks for all the great work you've done on this package.
Recently I've been playing with elm-canvas and elm-geometry. They work really nicely together. elm-geometry has a lot of useful functions that help you do things like find the center of a rectangle, or work with vectors. And of course, elm-canvas has a great API for drawing shapes.
My one problem is that it requires a lot of code to convert elm-geometry types to elm-canvas types.
For example, elm-canvas's, Canvas.circle
gets called like this:
center = ( 100, 100 )
radius = 25
Canvas.circle center radius
But if I want to use elm-geometry types, I have to do this:
-- Define a helper function to convert elm-geometry types to something that elm-canvas understands.
circle : Quantity Int Pixels -> Point2d Pixels CanvasCoordinates -> Canvas.Shape
circle radius center =
let
-- Convert our Point2d [1] to a tuple like ( x, y ).
centerTuple =
Point2d.toTuple Pixels.inPixels center
-- Now convert radius to a Float. It starts as an instance of the `Quantity Pixels`
-- type [2], which is how elm-geometry does type-safe math and unit conversions.
radiusInPixels =
radius
|> Pixels.toInt
|> toFloat
in
Canvas.circle
centerTuple
radiusInPixels
[1]: Here are the Point2d docs.
[2]: And here are the Quantity docs.
Thanks again for all your hard work! I really like using this library, and I think that adding support for elm-geometry types would make it even more fun and enjoyable.
Unless you already know from JS experience, from the docs it is impossible to know what the coordinate system is and what positive Y/X or negative Y/X are.
If Canvas.arc
is passed a negative value for its radius, it crashes with
Uncaught DOMException: CanvasRenderingContext2D.arc: Negative radius
This should probably at least be documented clearly in a warning, since one is usually reasonably insulated from runtime crashes with Elm. (Obviously, the use of a custom element here does away with that "guarantee," but it's still not entirely expected.)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.