Fay, рендер цикла и мыши

Прелюдия. Я новичок в Haskell и в функциональном программировании вообще, но я не могу не программировать в Haskell, потому что это так увлекательно! Таким образом, мой вопрос может быть очень простым и / или тривиальным. Я все еще читаю "Учим тебя Хаскеллу за великое благо", и Монады все еще мешают.

Рассмотрим следующее поведение:

  • Создать стек событий (просто массив)
    • Слушай mousemove события, когда событие сработало, поместите их в стек
  • Запустите цикл рендеринга:
    • использование requestAnimationFrame начать цикл
    • каждый раз проверяйте, сложились ли какие-либо события
    • получить последнее событие из стека, сделать что-то с ним и сделать
    • пустой стек
    • запросить следующий кадр

Чего я не могу понять, так это как получить интересующее значение от события, например, pageX или clientX, похоже, что мне нужно что-то делать с определениями данных, и я застрял с этим. Пожалуйста помоги.

PS Я также не могу понять, как проверить результат сторонней функции, например, когда я пытаюсь получить элемент DOM, результатом может быть элемент или null так сказать, getElById нечист, и мне нужно проверить, что я получил в результате. Как это реализовать? Кажется, я могу использовать что-то вроде: data Element = Null | Element ... и затем шаблон соответствует результату, но как заставить стороннюю функцию быть в курсе ожидаемого результата?

ОБНОВИТЬ
Вот рабочий пример желаемой логики:

module MouseTracker where

import FFI
import Prelude

data Element
data Event
type EventStack = [Event]
data Frame


consLog :: a -> Fay ()
consLog = ffi "console.log(%1)"


reqFrame :: (Frame -> Fay ()) -> Fay ()
reqFrame = ffi "requestAnimationFrame(%1)"


getDoc :: Fay Element
getDoc = ffi "document"


createEl :: String -> Fay Element
createEl = ffi "document.createElement(%1)"


elById :: String -> Fay Element
elById = ffi "document.getElementById(%1)"


elSetId :: Element -> String -> Fay ()
elSetId = ffi "%1.setAttribute('id',%2)"


elSetHtml :: Element -> String -> Fay ()
elSetHtml = ffi "%1.innerHTML = %2"


docBodyAppend :: Element -> Fay ()
docBodyAppend = ffi "document.body.appendChild(%1)"


elListenWith :: Element -> String -> (Event -> Fay ()) -> Fay ()
elListenWith = ffi "%1.addEventListener(%2, %3)"


winListenWith :: String -> (Event -> Fay ()) -> Fay ()
winListenWith = ffi "window.addEventListener(%1,%2)"


docListener :: String -> (Event -> Fay ()) -> Fay ()
docListener evtType fn = do
    doc <- getDoc
    elListenWith doc evtType fn




createTrace :: Fay ()
createTrace = do
    div <- createEl "div"
    docBodyAppend div
    elSetId div "trace"


createStack :: Fay ()
createStack = ffi "window._eventStack = []"


getStack :: Fay EventStack
getStack = ffi "window._eventStack"


updateStack :: EventStack -> Fay ()
updateStack = ffi "window._eventStack = %1"


emptyStack :: Fay ()
emptyStack = createStack




listenMice :: Fay()
listenMice = docListener "mousemove" trackMouse
    where trackMouse :: Event -> Fay ()
          trackMouse evt = do
              stk <- getStack
              updateStack (evt:stk)


renderFrame :: Frame -> Fay ()
renderFrame frame = do
     stack <- getStack
     if length stack > 0
           then (do
                   let e = head stack
                   traceEventCoord e)
           else (do return ())
     reqFrame renderFrame
     emptyStack
     where traceEventCoord :: Event -> Fay ()
           traceEventCoord e = do
               eX <- pageX e
               eY <- pageY e
               let str = show eX ++ " x " ++ show eY
               trace <- elById "trace"
               elSetHtml trace str


pageX :: Event -> Fay Int
pageX = ffi "%1['pageX']"
pageY :: Event -> Fay Int
pageY = ffi "%1['pageY']"


main = do
    docListener "DOMContentLoaded" $ \ _ -> do
        putStrLn "DOM ready"
        createTrace
        createStack
        listenMice
        reqFrame renderFrame

1 ответ

Решение

Посмотрите этот ответ для различных способов объявления типов данных и их компромиссов. Вы сами создаете события или оборачиваете события DOM? Если вы упаковываете, я бы предложил использовать EmptyDataDecl. Это означает, что у вас нет конструкторов для каждого типа события, но вы можете добавить функции, которые проверяют этот тип для вас. Если вы создаете свои собственные события, имеет смысл использовать обычное объявление данных с конструкторами.

При переносе событий DOM вы должны получить:

data Event
pageX :: Event -> Fay Int
pageX = ffi "%1['pageX']"

Fay имеет тип Nullable для работы со значениями Nullable:

getElementById :: String -> Nullable Element
getElementById = ffi "document['getElementById'](%1)"

Это описано более подробно в вики

Другие вопросы по тегам