Shine は、JavaScript の描画関数を宣言型 API でラップします。
光沢から大きなインスピレーションを受けています。
デモ(コンパイルされた Shine サンプル) はこちら
GHCJが必要です
ghcjs パッケージを構築する優れた方法は、Reflection プラットフォームを使用することです。
Picture
描画を表現するには、 Picture
データ型を使用してツリーを構築する必要があります。
pic :: Picture
pic = Rect 10 20 -- represents a 10x20 square
複数のPicture
を構成するには、 2 つのPicture
を受け入れてそれらをオーバーラップするOver
使用できます。
Picture
モノイドです。 <>
Over
の別名で、 mempty
は空の画像です。
-- 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. "
Foldable
使用すると、次のようなことができます
concentricCircles :: Picture
concentricCircles = foldMap Circle [ 1 , 10 .. 100 ]
Picture
を描く何かを描画する前に、 CanvasRenderingContext2D
(場合によってはDocument
) を取得する必要があります。この目的のために、shin はfullScreenCanvas
とfixedSizeCanvas
2 つのユーティリティ関数を提供します。
{-# 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)
コンテキスト上でPicture
をレンダリングするには、次の 3 つのオプションがあります。
render
Graphics.Shine.Render
のrender
を使用して手動で描画できます。
main :: IO ()
{- omitted: get the context, see before -}
render ctx concentricCircles
animate
時間に応じてPicture
ができます。つまり、 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
最後に、時間、入力 (キーボードとマウス)、および内部状態に依存するPicture
を描画できます。これは特にゲームに役立つため、この名前が付けられています。
-- 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
shine-examples
パッケージ (Hackage) を参照してください。
ghcjs とブラウザでアセットを処理するのは少し難しいです。
通常の Haskell プログラムでこれを行う通常の方法は、 data-files
使用することです。残念ながら、これは、実行可能ファイルに絶対パスをハードコーディングすることで機能しますが、プログラムがサーバー上の別のパスにある場合は機能しません。あるいは、環境変数のオーバーライド ( $pkgname_datadir
) を使用するか、環境変数がオーバーライドするため機能しません。ブラウザには存在しません。
したがって、現時点での解決策は、ソース コードで絶対パスまたは相対パスを明示的に指定し、コードのビルド/デプロイ後にアセットを適切な場所に何度もコピーすることです。これは、ピーマンの画像のanimated-shapes
デモで行ったことです。
関連する陰謀団のチケット、資産を処理する新しい方法の可能性について議論: haskell/cabal#6096 (コメント)