Shine enveloppe les fonctions de dessin de Javascript dans une API déclarative.
Fortement inspiré du gloss.
démo (exemples de brillance compilés) ici
Tu as besoin de ghcjs
Un excellent moyen de créer des packages ghcjs est d'utiliser la plateforme reflex
Picture
Pour représenter votre dessin, vous devez construire un arbre en utilisant le type de données Picture
.
pic :: Picture
pic = Rect 10 20 -- represents a 10x20 square
Pour composer plusieurs Picture
s, vous pouvez utiliser Over
, qui accepte deux Picture
s et les chevauche.
Picture
est un monoïde : <>
est un alias pour Over
et mempty
est l'image vide.
-- 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. "
En utilisant Foldable
vous pouvez faire des choses comme
concentricCircles :: Picture
concentricCircles = foldMap Circle [ 1 , 10 .. 100 ]
Picture
Avant de dessiner quoi que ce soit, vous devez obtenir un CanvasRenderingContext2D
(et parfois un Document
). À cet effet, Shine fournit deux fonctions utilitaires : fullScreenCanvas
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)
Pour restituer une Picture
dans un contexte, vous disposez de trois options :
render
Vous pouvez le dessiner manuellement en utilisant render
de Graphics.Shine.Render
main :: IO ()
{- omitted: get the context, see before -}
render ctx concentricCircles
animate
Vous pouvez dessiner une Picture
qui dépend du temps. Autrement dit, un 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
Enfin, vous pouvez dessiner une Picture
qui dépend du temps, des entrées (clavier et souris) et d'un état interne. Ceci est particulièrement utile pour les jeux, d'où son nom.
-- 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
Voir le package shine-examples
(Hackage).
La gestion des actifs avec ghcjs et le navigateur est quelque peu délicate.
La manière habituelle de procéder pour les programmes Haskell normaux consiste à utiliser data-files
. Malheureusement, cela fonctionne soit en codant en dur un chemin absolu dans l'exécutable, ce qui ne fonctionnera pas lorsque le programme est placé sur un serveur dans un chemin différent, soit en remplaçant une variable d'environnement ( $pkgname_datadir
), ce qui ne fonctionnera pas car les variables d'environnement le font. n'existe pas dans le navigateur.
Donc, pour l'instant, la solution consiste à spécifier explicitement des chemins absolus ou relatifs dans le code source, puis à copier manuellement les ressources à l'emplacement approprié après avoir créé/déployé le code. C'est ce que j'ai fait dans la démo animated-shapes
pour l'image des poivrons.
Billet de cabale pertinent, discutant d'une nouvelle façon possible de gérer les actifs : haskell/cabal#6096 (commentaire)