Shine оборачивает функции рисования JavaScript в декларативный API.
Сильно вдохновлен глянцем.
демо (скомпилированные примеры блеска) здесь
Вам нужен ghcjs
Отличный способ создания пакетов ghcjs — использовать платформу Reflex.
Picture
с Чтобы представить свой рисунок, вам необходимо построить дерево, используя тип данных Picture
.
pic :: Picture
pic = Rect 10 20 -- represents a 10x20 square
Чтобы составить несколько Picture
, вы можете использовать Over
, который принимает два Picture
и перекрывает их.
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
). Для этой цели в Shine предусмотрены две служебные функции: 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)
Чтобы отобразить Picture
в контексте, у вас есть три варианта:
render
Вы можете нарисовать его вручную, используя render
из Graphics.Shine.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
для изображения перца.
Соответствующий билет Cabal, в котором обсуждается возможный новый способ управления активами: haskell/cabal#6096 (комментарий)