Blazor 에서 이동 가능한 div 구현(모바일 지원)

Published Mar 15, 2023 | Updated May 19, 2023 | 0 comments

개요

위 이미지에서 예를 들어 보면 페이지의 우측 하단에 위치하는 이미지 레이어(div)가 있을 때
해당 div의 기본 위치는 우측 하단에 표시를 하고 필요할 때만 사용자가 이동할 수 있게 구현해야 하는 상황이 생겼습니다.
드래그 기능을 구현하기 위한 코드를 검색해 보면

그리고, 여기서 필요한 건 그냥 저 div 만 드래그 해서 위치를 이동하면 되는데
목록에서 드래그 해서 순서를 변경하거나 서로 다른 div 간의 이동을 위한 drag and draop 에 대한 설명이 대부분입니다.

또한, 현재 Blazor 로 구현하는 중이라 이에 맞게 javascript 가 아닌 C# 코드로 구현하는 방법을 설명하고자 합니다.

프로젝트 생성

visual studio 에서 blazor webassembly 프로젝트를 생성합니다.

레이어 추가

FetchData.razor 파일에서 razor 구 하단에 다음 코드를 추가합니다.

<div style="cursor:pointer;" class="position-absolute end-0 bottom-0 me-1 mb-1 text-warning">
    <svg xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" fill="currentColor" class="bi bi-cloud-plus-fill" viewBox="0 0 16 16">
        <path d="M8 2a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 6.095 0 7.555 0 9.318 0 11.366 1.708 13 3.781 13h8.906C14.502 13 16 11.57 16 9.773c0-1.636-1.242-2.969-2.834-3.194C12.923 3.999 10.69 2 8 2zm.5 4v1.5H10a.5.5 0 0 1 0 1H8.5V10a.5.5 0 0 1-1 0V8.5H6a.5.5 0 0 1 0-1h1.5V6a.5.5 0 0 1 1 0z" />
    </svg>
</div>

Server 프로젝트를 실행해서 FetchData 페이지로 이동해 보면 다음과 같이 표시됩니다.
페이지 크기가 변경해 우측 하단에 항상 표시가 됩니다.

Component 파일 추가

재사용을 위해 별도 Component 로 구현하겠습니다.

Client 프로젝트의 Shared 폴더에 다음과 같이 DraggableDiv.razor 파일을 추가합니다.

마우스 드래그 구현

DraggableDiv.razor 파일의 소스 코드를 다음과 같이 작성합니다.

<div @onclick="InvokeClick" class=" @(Class)" width="@(WH)" height="@(WH)"
  style="@(Style);cursor:@(draging ? "grabing": "move");@(draged ? $"right:{Right}px!important" : "");@(draged ? $"bottom:{Bottom}px!important" : "");"
  draggable="true"
  @ondragstart="@((e) => OnDragStart(e))"
  @ondragend="@((e) => OnDragEnd(e))"
>
	<svg xmlns="http://www.w3.org/2000/svg" width="@(WH)" height="@(WH)" fill="currentColor" class="bi bi-cloud-plus-fill" viewBox="0 0 16 16">
		<path d="M8 2a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 6.095 0 7.555 0 9.318 0 11.366 1.708 13 3.781 13h8.906C14.502 13 16 11.57 16 9.773c0-1.636-1.242-2.969-2.834-3.194C12.923 3.999 10.69 2 8 2zm.5 4v1.5H10a.5.5 0 0 1 0 1H8.5V10a.5.5 0 0 1-1 0V8.5H6a.5.5 0 0 1 0-1h1.5V6a.5.5 0 0 1 1 0z" />
	</svg>
</div>
@code {
	[Parameter]
	public string WH { get; set; } = "50px";
	[Parameter]
	public string Class { get; set; } = string.Empty;
	[Parameter]
	public string Style { get; set; } = string.Empty;
	[Parameter]
	public EventCallback OnClick { get; set; }
	private async Task InvokeClick()
	{
		await OnClick.InvokeAsync();
	}

	bool draging = false;
	double Right = 0;
	double Bottom = 0;
	bool draged = false;
	private double startX, startY, offsetX, offsetY;

	private void OnDragStart(DragEventArgs args)
	{
		//Console.WriteLine($"Start : {args.ScreenX}, {args.ScreenY}");
		draged = true;
		draging = true;
		startX = args.ScreenX;
		startY = args.ScreenY;
	}

	private void OnDragEnd(DragEventArgs args)
	{
		//Console.WriteLine($"End : {args.ScreenX}, {args.ScreenY}");
		draging = false;
		offsetX = args.ScreenX - startX;
		offsetY = args.ScreenY - startY;
		Right -= offsetX;
		Bottom -= offsetY;
	}
}

Server 프로젝트를 실행해서 FetchData 페이지로 이동해 보면 다음과 같이 표시되며
마우스로 드래그 해서 위치 변경이 가능합니다.
하지만, 브라우저 개발자 도구에서 모바일로 전환해보면 드래그가 되지 않습니다.

모바일 지원

터치 이벤트 구현

모바일 환경 에서의 드래그 기능을 구현하기 위해 DraggableDiv.razor 파일의 소스 코드를 다음과 같이 수정합니다.

<div @onclick="InvokeClick" class=" @(Class)" width="@(WH)" height="@(WH)"
	style="@(Style);cursor:@(draging ? "grabing": "move");@(draged ? $"right:{Right}px!important" : "");@(draged ? $"bottom:{Bottom}px!important" : "");"
	draggable="true"
	@ondragstart="@((e) => OnDragStart(e))"
	@ondragend="@((e) => OnDragEnd(e))"
	@ontouchstart="@((e) => OnTouchStart(e))"
	@ontouchmove="@((e) => OnTouchMove(e))"
	@ontouchend="@((e) => OnTouchEnd(e))"
>
	<svg xmlns="http://www.w3.org/2000/svg" width="@(WH)" height="@(WH)" fill="currentColor" class="bi bi-cloud-plus-fill" viewBox="0 0 16 16">
		<path d="M8 2a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 6.095 0 7.555 0 9.318 0 11.366 1.708 13 3.781 13h8.906C14.502 13 16 11.57 16 9.773c0-1.636-1.242-2.969-2.834-3.194C12.923 3.999 10.69 2 8 2zm.5 4v1.5H10a.5.5 0 0 1 0 1H8.5V10a.5.5 0 0 1-1 0V8.5H6a.5.5 0 0 1 0-1h1.5V6a.5.5 0 0 1 1 0z" />
	</svg>
</div>
@code {
	[Parameter]
	public string WH { get; set; } = "50px";
	[Parameter]
	public string Class { get; set; } = string.Empty;
	[Parameter]
	public string Style { get; set; } = string.Empty;
	[Parameter]
	public EventCallback OnClick { get; set; }
	private async Task InvokeClick()
	{
		await OnClick.InvokeAsync();
	}

	bool draging = false;
	double Right = 0;
	double Bottom = 0;
	bool draged = false;
	private double startX, startY, offsetX, offsetY;

	private void OnDragStart(DragEventArgs args)
	{
		//Console.WriteLine($"Start : {args.ScreenX}, {args.ScreenY}");
		draged = true;
		draging = true;
		startX = args.ScreenX;
		startY = args.ScreenY;
	}

	private void OnDragEnd(DragEventArgs args)
	{
		//Console.WriteLine($"End : {args.ScreenX}, {args.ScreenY}");
		draging = false;
		offsetX = args.ScreenX - startX;
		offsetY = args.ScreenY - startY;
		Right -= offsetX;
		Bottom -= offsetY;
	}
	private void OnTouchStart(TouchEventArgs args)
	{
		//Console.WriteLine($"TouchStart : {args.Touches[0].ScreenX}, {args.Touches[0].ScreenY}");
		draged = true;
		draging = true;
		startX = args.Touches[0].ScreenX;
		startY = args.Touches[0].ScreenY;
	}

	private void OnTouchMove(TouchEventArgs args)
	{
		//Console.WriteLine($"TouchMove : {args.ChangedTouches[0].ScreenX}, {args.ChangedTouches[0].ScreenY}");
		offsetX = args.ChangedTouches[0].ScreenX - startX;
		offsetY = args.ChangedTouches[0].ScreenY - startY;
		Right -= offsetX;
		Bottom -= offsetY;
		startX = args.ChangedTouches[0].ScreenX;
		startY = args.ChangedTouches[0].ScreenY;
	}
	private void OnTouchEnd(TouchEventArgs args)
	{
		//Console.WriteLine($"TouchEnd : {args.ChangedTouches[0].ScreenX}, {args.ChangedTouches[0].ScreenY}");
		draging = false;
		offsetX = args.ChangedTouches[0].ScreenX - startX;
		offsetY = args.ChangedTouches[0].ScreenY - startY;
		Right -= offsetX;
		Bottom -= offsetY;
	}
}

수정해서 실행해 보면 터치를 해서 드래그를 해도 페이지에 대한 제스처가 반응하면서 자연스럽지 못합니다.

제스처 비활성화

모바일 환경에서는 기본적인 제스처 지원으로 인해 먼저 이를 비활성화 해야 합니다.
touch-action – CSS: Cascading Style Sheets | MDN (mozilla.org) 이 링크를 참조하시면 제스처에 대한 기본 동작에 대한 CSS 속성을 보실 수 있습니다.
해당 div style 에 touch-action:none 을 적용하면 해당 영역에서 터치시 기본 동작을 수정할 수 있습니다.
소스 코드를 다시 한번 수정하면 다음과 같습니다.

<div @onclick="InvokeClick" class=" @(Class)" width="@(WH)" height="@(WH)"
	style="touch-action:none;@(Style);cursor:@(draging ? "grabing": "move");@(draged ? $"right:{Right}px!important" : "");@(draged ? $"bottom:{Bottom}px!important" : "");"
	draggable="true"
	@ondragstart="@((e) => OnDragStart(e))"
	@ondragend="@((e) => OnDragEnd(e))"
	@ontouchstart="@((e) => OnTouchStart(e))"
	@ontouchmove="@((e) => OnTouchMove(e))"
	@ontouchend="@((e) => OnTouchEnd(e))"
>
	<svg xmlns="http://www.w3.org/2000/svg" width="@(WH)" height="@(WH)" fill="currentColor" class="bi bi-cloud-plus-fill" viewBox="0 0 16 16">
		<path d="M8 2a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 6.095 0 7.555 0 9.318 0 11.366 1.708 13 3.781 13h8.906C14.502 13 16 11.57 16 9.773c0-1.636-1.242-2.969-2.834-3.194C12.923 3.999 10.69 2 8 2zm.5 4v1.5H10a.5.5 0 0 1 0 1H8.5V10a.5.5 0 0 1-1 0V8.5H6a.5.5 0 0 1 0-1h1.5V6a.5.5 0 0 1 1 0z" />
	</svg>
</div>
@code {
	[Parameter]
	public string WH { get; set; } = "50px";
	[Parameter]
	public string Class { get; set; } = string.Empty;
	[Parameter]
	public string Style { get; set; } = string.Empty;
	[Parameter]
	public EventCallback OnClick { get; set; }
	private async Task InvokeClick()
	{
		await OnClick.InvokeAsync();
	}

	bool draging = false;
	double Right = 0;
	double Bottom = 0;
	bool draged = false;
	private double startX, startY, offsetX, offsetY;

	private void OnDragStart(DragEventArgs args)
	{
		//Console.WriteLine($"Start : {args.ScreenX}, {args.ScreenY}");
		draged = true;
		draging = true;
		startX = args.ScreenX;
		startY = args.ScreenY;
	}

	private void OnDragEnd(DragEventArgs args)
	{
		//Console.WriteLine($"End : {args.ScreenX}, {args.ScreenY}");
		draging = false;
		offsetX = args.ScreenX - startX;
		offsetY = args.ScreenY - startY;
		Right -= offsetX;
		Bottom -= offsetY;
	}
	private void OnTouchStart(TouchEventArgs args)
	{
		//Console.WriteLine($"TouchStart : {args.Touches[0].ScreenX}, {args.Touches[0].ScreenY}");
		draged = true;
		draging = true;
		startX = args.Touches[0].ScreenX;
		startY = args.Touches[0].ScreenY;
	}

	private void OnTouchMove(TouchEventArgs args)
	{
		//Console.WriteLine($"TouchMove : {args.ChangedTouches[0].ScreenX}, {args.ChangedTouches[0].ScreenY}");
		offsetX = args.ChangedTouches[0].ScreenX - startX;
		offsetY = args.ChangedTouches[0].ScreenY - startY;
		Right -= offsetX;
		Bottom -= offsetY;
		startX = args.ChangedTouches[0].ScreenX;
		startY = args.ChangedTouches[0].ScreenY;
	}
	private void OnTouchEnd(TouchEventArgs args)
	{
		//Console.WriteLine($"TouchEnd : {args.ChangedTouches[0].ScreenX}, {args.ChangedTouches[0].ScreenY}");
		draging = false;
		offsetX = args.ChangedTouches[0].ScreenX - startX;
		offsetY = args.ChangedTouches[0].ScreenY - startY;
		Right -= offsetX;
		Bottom -= offsetY;
	}
}

다시 실행해서 결과를 확인해 보면 모바일 환경에서도 제대로 동작하는 것을 확인하실 수 있습니다.
touch-action:none 적용 전 과 후를 비교해서 확인이 가능합니다.

컴퍼넌트로 대체

이제 FetchData.razor 페이지에서 해당 컴퍼넌트를 적용하려면 다음과 같이 기존의 div 엘리먼트를 다음 코드로 대체하시면 됩니다.

<DraggableDiv Class="position-absolute end-0 bottom-0 me-1 mb-1 text-warning" />

Github source code

Learn more on this topic

Related Blog Posts

Join in the conversation

Leave a Comment

0 Comments

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

무료 온라인 전광판

전광판

텍스트를 입력하고 텍스트 효과 및 배경효과 를 변경해서 전체화면으로 표시할 수 있는 전광판 용도로 사용하실 수 있습니다. 각종 스포츠 및 공연 관람시 응원 용도로 사용이 가능합니다.

Carousel

여러개의 슬라이드를 추가하여 프레젠테이션 및 이미지 슬라이드 용도로 사용하실 수 있습니다. 브라우저가 포함된 IT 기기로 큰 모니터에 연결하여 매장 내 공지사항 및 메뉴소개를 이미지로 표시할 수 있습니다.

Pin It on Pinterest

Shares
Share This

Share This

Share this post with your friends!

Shares