Elm boundingClientRect获取元素相对于文档的位置
我正在尝试实现一个拖放程序,使用DOM包中的Elm boundingClientRect获取元素相对于文档的位置,elm,Elm,我正在尝试实现一个拖放程序,使用DOM包中的boundingClientRect获取要移动的元素的尺寸,并使用鼠标中的position跟踪鼠标在拖动时的移动 在我滚动之前,程序运行良好,但当我向下滚动时,拖动元素在视图中的显示高度高于单击它之前的显示高度。我怀疑发生的是,boundingClientRect获取元素相对于视点的位置,然后我使用这些值设置top和left值。但是,top和left与文档或父元素相关。但是,我不知道我可以使用什么来代替或补充boundingClientRect,以获得
boundingClientRect
获取要移动的元素的尺寸,并使用鼠标中的position
跟踪鼠标在拖动时的移动
在我滚动之前,程序运行良好,但当我向下滚动时,拖动元素在视图中的显示高度高于单击它之前的显示高度。我怀疑发生的是,boundingClientRect
获取元素相对于视点的位置,然后我使用这些值设置top
和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
和movableItem
字段将分别使用鼠标位置和movableItem
的尺寸(矩形)进行更新。但是,这些尺寸是相对于视点的movingItemDiv
然后调用getCurrentMovingRectangle
,根据模型中的movingItem
和拖动
的尺寸,设置movingItemDiv
的左侧和top
样式。因为movingItem
的尺寸基于movableItemDiv
相对于视点的尺寸,而不是相对于文档的尺寸,而movingItemDiv
的顶部和左侧的值设置的值建立了元素相对于文档的位置(或者是父元素,老实说,我不确定),movingItemDiv
的位置不正确。我希望这是清楚的!更新为elm-0.18
下面是一个包含可拖动项的列表的快速而肮脏的示例
(您可以将其复制到以查看其运行情况)
- 每个项目都有一个相对定位
transform:translate()
用于定位要拖动的项
- 我们不知道物品的绝对位置,但我们知道它相对于(未知)起始位置移动了多少
下一步是确定当拖动结束时,我们是否在下降区上方。
要进行计算,您需要知道:
- 放置区域相对于列表容器左上角的相对位置
- 每个下降区的大小(宽度、高度)
- 相对于列表容器左上角拖动的项的原始位置
- 为此,您需要知道每个项目的实际高度(我总是在每个项目上使用固定高度)
- 列表容器中的滚动量(使用elm lang/Dom中的Dom.y)
希望这将帮助你在正确的方向
导入Html公开(…)
导入Html.Attributes(..)
导入Html.Events(打开)
导入Json。解码为Json
导入鼠标(位置)
主要=
Html.program
{init=init
,视图=视图
,update=update
,订阅=订阅
}
--模型
类型别名模型=
{位置:位置
,项:列表字符串
拖拉:也许拖拉
}
类型别名拖动=
{id:Int
,开始:位置
,现时:职位
}
初始化:(模型,Cmd Msg)
初始化=
模型
(职位200)
[“苹果”、“香蕉”、“樱桃”、“爸爸”]
没有什么
! []
--更新
输入味精
=DragStart Int位置
|德拉格位置
|德拉根位置
更新:Msg->Model->(Model,Cmd-Msg)
更新msg模型=
(更新帮助消息模型,Cmd.none)
更新帮助:Msg->Model->Model
updateHelp msg({位置,项目,拖动}作为模型)=
味精案例
DragStart id xy->
模型位置项目(仅(拖动id xy))
DragAt xy->
模型位置项(Maybe.map(\{id,start}->Drag id start xy)拖动)
德拉根德->
模型位置项目无
--订阅
订阅:模型->子消息
订阅模式=
案例模型
没有->
无
只是-->
Sub.batch[Mouse.moves DragAt,Mouse.ups DragEnd]
--看法
(=>) = (,)
查看:模型->Html消息
视图模型=
分区[]
Int->String->Html消息
itemView模型索引项=
让
锌试剂=
案例模型
只是{id}->
如果index==id,那么
"99"
其他的
"0"
没有->
"0"
在里面
div
[onMouseDown索引
风格
[“背景色”=>“#3C8D2F”
,“边框”=>“2倍纯色橙色”
,“光标”=>“移动”
,“位置”=>“相对”
,“transform”=>(getOffset模型索引)
,“z指数”=>zIndex
,“宽度”=>“100px”
,“高度”=>“100px”
,“边界半径”=>“4px”
,“颜色”=>“白色”
,“显示”=>“弹性”
,“对齐项目”=>“居中”
,“对齐内容”=>“中心”
,“用户选择”=>“无”
]
]
[文本项目
]
px:Int->String
px数=
toString编号+++“px”
getOffset:Model->Int->String
getOffset{位置,项,拖动}索引=
案例拖动
没有->
翻译0
只需{id,start,current}->
如果index==id,那么
翻译(current.x-start.x)(current.y-start.y)
其他的
翻译0
翻译:Int->Int->String
平移x y=
翻译(“++toString x++”px,++toString y++”px)
onMouseDown:Int->attributemsg
onMouseDown id=
在“mousedown”(Json.map(DragStart id)Mouse.po上