Shine verpackt die Zeichenfunktionen von Javascript in einer deklarativen API.
Stark von Glanz inspiriert.
Demo (zusammengestellte Shine-Beispiele) hier
Du brauchst ghcjs
Eine gute Möglichkeit, ghcjs-Pakete zu erstellen, ist die Verwendung der Reflex-Plattform
Picture
s Um Ihre Zeichnung darzustellen, müssen Sie einen Baum mit dem Picture
erstellen.
pic :: Picture
pic = Rect 10 20 -- represents a 10x20 square
Um mehrere Picture
zusammenzustellen, können Sie Over
verwenden, das zwei Picture
akzeptiert und diese überlappt.
Picture
ist ein Monoid: <>
ist ein Alias für Over
und mempty
ist das leere Bild.
-- draw some shapes on top of each other
pic :: Picture
pic = Rect 10 20
<> Translate 30 30 ( Circle 15 )
<> Colored ( Color 255 0 0 0.2 ) ( RectF 4 4 )
<> Text " Sans 12px " LeftAlign 200 " The quick brown fox jumps over the lazy dog. "
Mit Foldable
können Sie Dinge tun wie
concentricCircles :: Picture
concentricCircles = foldMap Circle [ 1 , 10 .. 100 ]
Picture
s Bevor Sie etwas zeichnen, müssen Sie einen CanvasRenderingContext2D
(und manchmal einen Document
) erhalten. Zu diesem Zweck stellt Shine zwei Hilfsfunktionen bereit: fullScreenCanvas
und fixedSizeCanvas
{-# LANGUAGE CPP #-}
import Graphics.Shine
import Graphics.Shine.Input
import Graphics.Shine.Picture
import GHCJS.DOM ( currentDocumentUnchecked )
-- This is how the ghcjs-dom hello-world does it.
-- It's boilerplate, so in the next shine version there
-- will probably be a ready-to-use run function
#if defined(ghcjs_HOST_OS)
run :: a -> a
run = id
#elif defined(MIN_VERSION_jsaddle_wkwebview)
import Language.Javascript.JSaddle.WKWebView ( run )
#else
import Language.Javascript.JSaddle.WebKitGTK ( run )
#endif
main :: IO ()
main = run $ do
doc <- currentDocumentUnchecked -- use currentDocument to handle failure
ctx <- fixedSizeCanvas doc 400 400
-- do something with ctx (and maybe doc)
Um ein Picture
in einem Kontext zu rendern, haben Sie drei Möglichkeiten:
render
Sie können es manuell mit render
von Graphics.Shine.Render
zeichnen
main :: IO ()
{- omitted: get the context, see before -}
render ctx concentricCircles
animate
Sie können ein Picture
zeichnen, das von der Zeit abhängt. Das heißt, ein Float -> Picture
.
-- An expanding-and-contracting circle.
animation :: Float -> Picture
animation = Translate 200 200
. Circle
. ( * 100 ) . ( + 1 ) -- bigger positive oscillation
. sin -- the circle's radius oscillates
main :: IO ()
main = do
{- omitted: get the context, see before -}
animate ctx 30 animation
play
Schließlich können Sie ein Picture
zeichnen, das von der Zeit, Eingaben (Tastatur und Maus) und einem internen Zustand abhängt. Dies ist besonders nützlich für Spiele, daher der Name.
-- this code draws a black rectangle in the center of the canvas only when the
-- left mouse button is pressed
main :: IO ()
main = do
{- omitted: get the context and the document, see before -}
play ctx doc 30 initialState draw handleInput step
where
-- our state represents the state of the left mouse button
initialState = Up
-- we draw a square only if the button is pressed
draw Up = Empty
draw Down = Translate 200 200 $ RectF 200 200
-- when an event is fired we store the button state
handleInput ( MouseBtn BtnLeft buttonState _) = const buttonState
handleInput _ = id -- catch-all for all other events
step _ = id -- our state does not depend on time
Siehe das Paket shine-examples
“ (Hackage).
Der Umgang mit Assets mit ghcjs und dem Browser ist etwas knifflig.
Die übliche Vorgehensweise für normale Haskell-Programme ist die Verwendung data-files
. Leider funktioniert dies entweder durch Festcodieren eines absoluten Pfads in der ausführbaren Datei, was nicht funktioniert, wenn das Programm auf einem Server in einem anderen Pfad abgelegt wird, oder durch eine Umgebungsvariablenüberschreibung ( $pkgname_datadir
), die nicht funktioniert, weil Umgebungsvariablen dies tun nicht im Browser vorhanden.
Daher besteht die Lösung vorerst darin, explizit absolute oder relative Pfade im Quellcode anzugeben und die Assets dann nach der Erstellung/Bereitstellung des Codes vollständig an den entsprechenden Speicherort zu kopieren. Das habe ich in der Demo animated-shapes
für das Paprikabild gemacht.
Relevantes Cabal-Ticket, in dem eine mögliche neue Art des Umgangs mit Vermögenswerten besprochen wird: haskell/cabal#6096 (Kommentar)