Я пытаюсь реализовать программу перетаскивания, используя boundingClientRect
из пакета DOM, чтобы получить размеры перемещаемого элемента и position
от мыши, чтобы отслеживать движение мыши при перетаскивании.boundingClientRect, чтобы получить положение элемента относительно документа
Программа отлично работает, прежде чем прокручивать, но когда я прокручиваю вниз, элемент перетаскивания появляется выше в представлении, чем до того, как я щелкнул его. Я подозреваю, что происходит: boundingClientRect
получает позицию элемента относительно точки обзора, а затем я использую эти значения для установки значенийи left
. Однако top
и left
относятся к документу или к родительскому элементу. Однако я не знаю, что я мог бы использовать вместо или в дополнение к boundingClientRect
, чтобы получить значения left
и top
относительно документа или родительского элемента.
Вот код, вероятно, яснее, чем мой бессвязный.
type alias Model =
{ movableItemsList : List Item
, originalMovableItems : List Item
, movingItem : Maybe (Item, Rectangle)
, receivingItemsList : List Item
, updatedItemsList : List (Item, Rectangle)
, drag : Maybe Drag
, scrollTop : Float
}
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
DragAndDelete deleteMsg xy movingItem movingRectangle ->
model
! [ command (DragStart xy movingItem movingRectangle)
, command (deleteMsg movingItem)
]
DragStart xy selectedItem movingRectangle ->
let
movingItem =
List.head (List.filter (\i -> i.id == selectedItem.id) model.originalMovableItems)
|> Maybe.withDefault (Item "" "" 0 "")
in
{ model
| drag = Just (Drag xy xy)
, movingItem = Just (movingItem, movingRectangle)
}
! []
DragAt xy ->
{ model
| drag =
(Maybe.map (\{ start } -> Drag start xy) model.drag)
}
! []
DragEnd _ ->
{ model
| movingItem = Nothing
, drag = Nothing
}
! []
DeleteFromUpdatedList movingItem ->
let
isKeepable iteratingItem =
iteratingItem.id /= movingItem.id
updatedItemsData =
List.filter (\(i, _) -> isKeepable i) model.updatedItemsList
in
{ model
| updatedItemsList = updatedItemsData
}
! []
DeleteFromMovableList movingItem ->
let
isKeepable iteratingItem =
iteratingItem.id /= movingItem.id
movableItemsData =
List.filter isKeepable model.movableItemsList
in
{ model
| movableItemsList = movableItemsData
}
! []
UpdateReceivingItemsOnOverlap receivingRectangle receivingItem ->
let
receivingItemsData =
if (checkOverlap (getCurrentMovingRectangle model) receivingRectangle) then
List.map (\i -> updateItemColor i receivingItem) model.receivingItemsList
else
model.receivingItemsList
in
{ model | receivingItemsList = receivingItemsData } ! []
RestoreReceivingItemsListColor _ ->
let
receivingItemsData =
List.map restoreReceivingItemColor model.receivingItemsList
in
{ model | receivingItemsList = receivingItemsData } ! []
AddValues receivingRectangle receivingItem ->
let
movingItem =
movingItemMaybe model.movingItem
updatedItemsData =
if (checkOverlap (getCurrentMovingRectangle model) receivingRectangle) then
({ movingItem
| value = receivingItem.value + movingItem.value
, color = "#1A6B0D"
}
, receivingRectangle
)
:: model.updatedItemsList
else
model.updatedItemsList
in
{ model
| updatedItemsList = updatedItemsData
}
! [ command (DeleteFromMovableList movingItem)
]
RestoreListContent ->
let
movingItem =
movingItemMaybe model.movingItem
listItems =
movingItem :: model.movableItemsList
in
{ model | movableItemsList = listItems } ! []
getCurrentMovingRectangle : Model -> Rectangle
getCurrentMovingRectangle model =
let
movingItemTuple =
Maybe.withDefault (Item "" "" 0 "0", Rectangle 0 0 0 0) model.movingItem
(_, movingRect) =
movingItemTuple
in
case model.drag of
Nothing ->
movingRect
Just { start, current } ->
Rectangle
(movingRect.top + toFloat (current.y - start.y))
(movingRect.left + toFloat (current.x - start.x))
(movingRect.width)
(movingRect.height)
-- VIEW
view : Model -> Html Msg
view model =
div
[]
[ receivingAndUpdatedItemsLayersDiv model
, movableItemsListDiv model
, if model.movingItem /= Nothing then
movingItemDiv model
else
div [] []
]
receivingAndUpdatedItemsLayersDiv : Model -> Html Msg
receivingAndUpdatedItemsLayersDiv model =
div
[ style [ ("position", "relative") ] ]
[ div
[ style
[ ("position", "relative")
, ("top", "10px")
, ("left", "80px")
]
]
[ div
[ style
[ ("z-index", "3")
, ("position", "absolute")
]
, attribute "class" "drag-here-overlay"
]
(List.map receivingItemOverlay model.receivingItemsList)
, div
[ style
[ ("z-index", "0")
, ("position", "absolute")
]
, attribute "class" "drag-here-underlay"
]
(List.map receivingItemUnderlay model.receivingItemsList)
]
, div
[]
[ div
[ style
[ ("position", "absolute")
, ("z-index", "1")
]
, attribute "class" "drag-here-updated"
]
(List.map updatedItemUnderlay model.updatedItemsList)
, div
[ style
[ ("position", "absolute")
, ("z-index", "4")
]
]
(List.map updatedItemOverlay model.updatedItemsList)
]
]
movableItemsListDiv : Model -> Html Msg
movableItemsListDiv model =
div
[ style
[ ("position", "relative")
, ("top", "10px")
, ("left", "800px")
]
]
(List.map movableItemDiv model.movableItemsList)
updatedItemUnderlay : (Item, Rectangle) -> Html Msg
updatedItemUnderlay (item, rectangle) =
div
[ attribute "class" "drag-here-updated-underlay-item"
, sharedStyles
, style
[ ("background-color", item.color)
, ("border", "1px solid #000")
, ("position", "absolute")
, ("left", px rectangle.left)
, ("top", px rectangle.top)
]
]
[ text item.text
, br [] []
, text (toString item.value)
]
updatedItemOverlay : (Item, Rectangle) -> Html Msg
updatedItemOverlay (item, rectangle) =
div
[ onDragStart DeleteFromUpdatedList item
, attribute "class" "drag-here-updated-overlay-item"
, sharedStyles
, style
[ ("background-color", "transparent")
, ("position", "absolute")
, ("left", px rectangle.left)
, ("top", px rectangle.top)
]
]
[]
receivingItemUnderlay : Item -> Html Msg
receivingItemUnderlay item =
div
[ attribute "class" "drag-here-underlay-item"
, sharedStyles
, style
[ ("background-color", item.color)
-- , ("border", "1px solid #1A6B0D")
]
]
[ text item.text
, br [] []
, text (toString item.value)
]
receivingItemOverlay : Item -> Html Msg
receivingItemOverlay item =
div
[ on "mouseenter" (Decode.map (\d -> UpdateReceivingItemsOnOverlap d item) (DOM.target DOM.boundingClientRect))
, on "mouseleave" (Decode.map (\d -> RestoreReceivingItemsListColor d) (DOM.target DOM.boundingClientRect))
, on "mouseup" (Decode.map (\d -> AddValues d item) (DOM.target DOM.boundingClientRect))
, attribute "class" "drag-here-overlay-item"
, sharedStyles
, style
[ ("background-color", "transparent") ]
]
[]
movableItemDiv : Item -> Html Msg
movableItemDiv item =
div
[ onDragStart DeleteFromMovableList item
, attribute "id" ("drag-me " ++ toString item.value)
, sharedStyles
, style
[ ("background-color", item.color)
, ("border", "1px solid #DD0848")
, ("position", "relative")
]
]
[ text "Drag Me!"
, br [] []
, text (toString item.value)
]
movingItemDiv : Model -> Html Msg
movingItemDiv model =
let
movingItem =
movingItemMaybe model.movingItem
realRectangle =
getCurrentMovingRectangle model
in
div
[ onMouseUp RestoreListContent
, sharedStyles
, style
[ ("background-color", "#FF3C8C")
, ("border", "1px solid #DD0848")
, ("position", "absolute")
, ("top", px (realRectangle.top))
, ("left", px (realRectangle.left))
, ("z-index", "2")
]
]
[ text movingItem.text
, br [] []
, text (toString movingItem.value)
]
sharedStyles : Attribute a
sharedStyles =
style
[ ("width", "100px")
, ("height", "100px")
, ("border-radius", "4px")
, ("color", "white")
, ("justify-content", "center")
, ("align-items", "center")
, ("display", "flex")
]
onDragStart : (Item -> Msg) -> Item -> Attribute Msg
onDragStart deleteMsg item =
on "mousedown"
(Mouse.position
`Decode.andThen`
(\posit ->
DOM.target DOM.boundingClientRect
`Decode.andThen`
(\rect ->
Decode.succeed (DragAndDelete deleteMsg posit item rect)
)
)
)
px : countable -> String
px number =
toString number ++ "px"
Итак, как вы можете видеть, когда один щелкает movableItemDiv
, модель в drag
и movingItem
поля обновляются с положением мыши и размеры (Rectangle) в movableItem
соответственно. Однако эти измерения относятся к точке зрения. movingItemDiv
затем звонит getCurrentMovingRectangle
, который устанавливает left
и top
стили movingItemDiv
в соответствии с размерами movingItem
и drag
в модели. Поскольку размеры movingItem
основаны на размерах movableItemDiv
относительно точки обзора, а не относительно документа, тогда как значения, установленные для значенийи left
, определяют положение элемента относительно документа (или родительский элемент, я не уверен, если честно), movingItemDiv
не установлен правильно. Надеюсь, это ясно!
Вы, вероятно, с помощью [Дебуа/вяз-РОМ] (http://package.elm-lang.org/packages/debois/elm-dom/1.2.2/DOM) пакета. Как объяснено [здесь] (http://package.elm-lang.org/packages/debois/elm-dom/1.2.2/DOM#boundingClientRect), 'boundingClientRect' является приблизительным и не работает должным образом при прокрутке. – wintvelt
И что работает правильно при прокрутке: -D? –
Альтернативный подход может заключаться в использовании абсолютного позиционирования для движущихся объектов. например, в примере [mouse drag] (http://elm-lang.org/examples/drag) и использовать [эту функцию из elm-lang/dom] (http://package.elm-lang.org/packages /elm-lang/dom/1.1.0/Dom-Scroll#y), чтобы компенсировать прокрутку, когда выясняется, находится ли ваш движущийся объект по намеченной цели. – wintvelt