[Compose] 카드에 광원 효과 주기 삽질기

sute·2023년 10월 19일
0

안드로이드

목록 보기
1/3
post-thumbnail

네이버 블로그에서 이관
+) 이거 왜 gif 한개밖에 안올라가는거지


1. 계기

갑자기 compose로 장난감을 만들어보고 싶어졌다.
뭐 할만한게 없을까 하다가 최근에 유튜브에서 마술 관련 영상을 몇개 봤던게 생각나서 카드 관련된거 만들면 재밌을 것 같아 장난감을 만들어 보려고 한다.

2. 삽질시작

우선 compose로 진행했다. 일반 View 쓰는건 노잼이잖아~

2.1. 카드 만들기

카드를 가지고 놀기 위해선 먼저 카드를 만들어야 한다.
compose에는 Card라고 만들어져 있긴 한데, 그냥 새로 만들었다.
딱히 별다른 이유는 없음

카드를 만들기 위해선 먼저 카드 크기를 정해야 한다.
카드의 황금 비율은 1:1.58 이라는 얘기를 들어서, width, height 중 1개의 값과 orientation 값을 받아서 가로 혹은 세로방향의 카드 사이즈를 계산하도록 해 봤다.

val (width, height) = if (orientation == CardOrientation.LANDSCAPE) {
    (size * 1.58f) to size
} else {
    size to (size * 1.58f)
}

방향에 따라 Portrait 방향이면 세로가 1.58 이고 Landscape이면 가로가 1.58 이 된다.

그 다음엔 카드를 그려보았다.
카드 그리는건 별거 없다. size 지정해주고 뺵그라운드 설정해주면 끝

Box( 
    modifier = Modifier
       .size(width,height)
       .background (
           color = Color.DarkGray,
           shape = RoundedCornerShape(10.dp)
       )
) {

}

카드가 완성되었다. 아직 만진다고 뭐 동작하는건 없다.

2.2. 카드 회전시키기

이제 카드를 문질문질 하면 돌아가게 만들어보자

찾아보니 Modifier 에 보면 pointerInput 이라는 게 있다고 한다. 이 안에서 dragGesture를 통해 드래그를 감지할수 잇는 것 같다.

이제 드래그 감지는 하는법 알았으니 카드를 마구 문대기 위해서 회전을 시켜보도록 하자.
또odifier에 보면 graphicsLayer 라는 놈이 있다.
graphicsLayerScope 람다 내부에서 graphicsLayer 관련 객체들을 참조할 수 있다.

투명도, 회전값, 크기, 카메라 거리? 등 여러개가 있는데, 카드 회전시킬꺼니까 회전값만 사용하면 될것 같다.
회전축은 X Y Z가 있는데, Z는 안써도 될것 같아서 X Y만 사용했다.

드래그 하는 부분에서 드래그 한 값? 을 가져와서 회전축에 더해주면 카드가 돌아가지 않을까?
한번 해보자.

Modifier.pointerInput("drag") {
    detectDragGestures { change, dragAmount ->
        offsetX += dragAmount.x
        offsetY += dragAmount.y
    }
}
.graphicsLayer {
     val newRotationX = (rotationX + offsetY)
     val newRotationY = (rotationY + offsetX)

     this.rotationX = newRotationX
     this.rotationY = newRotationY
}

드래그 할때 offset을 더해놧다가 그래픽 단에서 사용하도록 햇다.

![](https://velog.velcdn.com/images/suteeee/post/68575434-7888-4223-9173-c16be795201a/image.gif)

돌아가긴 하는데 좀 몇개 이상한 문제가 있다.
첫번째로 수평으로 문지르는데 카드가 수직으로 돌아간다. (반대 경우도 마찬가지)
두번째는 너무빨리돌아간다. 문지른 만큼만 돌아가면 참 좋은데..

일단 단계별로 수정을 해보자.
일단 문지르는 방향과 돌아가는 방향이 다르니까 offset 값을 반대로 줘 봤다.
rotation XYZ 가 축 자체가 돌아가는게 아닌, 축을 기준으로 놓고 돌아가는 듯 하다.
즉 rotation X 값이 변하면, X축을 기준으로 Y가 회전하니까 세로로 움직이는 듯.

근데 수평으로 문지르면 offsetX가 값이 변동되고, rotationX에 합산되니까 수평으로 문지르면 수직으로 돌아가는 괴현상이 발생하는것 같다.

그럼 반대로 해보자. 잘 되려나?
수평 방향은 잘 된다. 근데 수직방향이 거꾸로움직인다. 카드를 아래로땅기는데 위로 돌아감.
보통 방향 관련된거는 반대로 값 넣으면 반대로 움직이니까 rotation 값에 더하지말고 빼보자.
(동작 짤 미첨부, 어차피 짤로보면 똑같음)

잘 된다..!

그럼 이제 두번째 문제를 해결해보자..
카드가 너무빨리 돌아가는게 문제니까, 카드 회전 변화 수치를 줄이면 되지않을까?

detectDragGestures { change, dragAmount ->
    if (width > height) {
        offsetX += dragAmount.x / 4
        offsetY += dragAmount.y / 2
    } else {
        offsetX += dragAmount.x / 2
        offsetY += dragAmount.y / 4
    }
}

가로세로 길이 체크해서 카드가 세로인지 가로인지 확인하고, 적당히 offset 변화값을 조절했다.
더 긴 부분인데 똑같이 주니까 한쪽방향은 개빨리 움직이더라.

2.3. 카드에 광원 효과 주기

이제 해보니 잘 되긴 하는데, 뭔가 밋밋하다.
3D느낌으로 회전하긴 하는데 아무 명암같은게 없으니 뭔가 심심한 느낌?
그러므로 광원효과 비스무리한걸 넣어서 3D효과를 야매로 내보자.

제대로 된 광원효과를 내는건 좀 어려워보인다. OpenGL이라느니 뭐라느니.. 근데 그런건 너무 오바다.
대충 비스무리하게만 하면 되니까, 그라데이션을 써서 구현해보자.

카드에 하얀색 원형 그라이데이션을 주고, 돌릴때마다 방향에맞게 움직이면 비슷하게 되지 않을까 싶다.
마침 구글에 dragGesture 이용해서 원형 그라데이션을 드래그하는 포인터로 움직이는, 손전등 기능같은걸 예제로 올려놨길래 이거다 싶어서 복붙했다 ㅋㅋ

modifier에 drawWithContent를 쓰면, content 위 혹은 아래에 원하는걸 그릴수 있다.
이걸 이용해서 content를 먼저 그리고 그위에 그라데이션 도형을 그리면 될 것 같다.

.drawWithContent {
    drawContent()

    drawRect(
        Brush.radialGradient(
        listOf(
            Color(0xAAFFFFFF),
            Color.Transparent
        ),
            center = pointerOffset,
            radius = (min(width, height)).toPx()
        )
    )
}

content를 아래에 그리고 brush를 이용해서 AA투명도 흰색 그라데이션을 그려준다.
centerOffset은 현재 그라데이션 도형의 중앙 위치를 나타내는 값이다. 저게 바뀌면 움직임.
radius 는 그라데이션의 반지름을 설정한다. 카드의 가로 세로중 짧은부분에 꽉차게 설정했다.

center offset이 초기값이 0,0 이므로 좌상단에 그려진다.

원래는 AAFFFFFF가 아닌 그냥 흰색으로했엇는데 너무 쨍한게 별로여서 투명도를 조금 줬다.
다른색 배경으로는 어떻게 보일지 모르겟네

이제 그라데이션을 움직이게만 하면 된다.

detectDragGestures { change, dragAmount ->
    ...
    pointerOffset += dragAmount
}

dragAmount도 offset 값이고, 움직이는 양 뱉어주는 것 같으니 해당 값을 더해보자.

문제가 발견되었다..! 한방에 되는게 없다 ㅡㅡ

그라데이션이 반대로 움직이고 너무 조금움직인다...
그래도 간단하게 고칠수 잇을것 같으니, 값을 수정해보자

pointerOffset -= dragAmount * pointerMoveWeight * 4f

dragAmount 값을 더하는게 아니라 빼봤다. 반대로 움직이니깐 ㅋㅋ
그리고 너무 조금움직여서 임의의 수(4f) 를 곱해줬다. 더 빨리 움직이라고!
pointerMoveWeight은 카드 크기에 따른 그라데이션 이동 가중치이다.

val pointerMoveWeight = min(width, height).value / 100f

사이즈 100dp를 기준으로 비율에 맞게 가중치가 설정된다. 이거 안하니까 크기따라 제각각으로 움직이더라.

2.4. 중간 결과

광원효과 비슷한거까지 구현이 완료되긴 햇는데 문제가 좀 있다.
지금은 카드 막 돌리면 그라데이션이 저 밖으로 도망가는 문제도 있고, 좀 뭔가 매끄럽지 못한 느낌.

다음 목표는 카드에 앞뒷면 표시하는거하고, 앞뒷면 다 광원효과 잘 동작하는거다.

profile
고인물이 되고싶다

2개의 댓글

comment-user-thumbnail
2024년 1월 8일

고수님 다음글 안싸지르시나요?

1개의 답글

관련 채용 정보