본문 바로가기

IOS

SwiftUI Metaball Animation - Kavsoft (iOS 인터렉티브 애니메이션)

728x90
반응형

결과물🤟🏻: 

원 도형 두개를 사용하여 Metaball 효과를 보여주는 Metaball menu & 여러개의 RoundedRectangle을 사용하여 Metaball 효과를 보여주는 Clubbed 메뉴

result.gif

이번 애니메이션을 학습하게된 계기 🔫:

Canvas와 mask를 공부하며 이전에 학습한 다이나믹 아일랜드와 이미지가 합쳐지는 효과 및 아이콘 전환시 Blur를 활용한 효과에 응용된 Metaball Animation을 파고파고 🚣🏻‍♂️

Reference ✂️ : 

https://www.youtube.com/watch?v=hfKGLJejAEw 

과정 🎩:

Step 1. Asset에 Gradient 색상으로 사용할 색상 설정

Color Setting.jpg

동일한 계열의 색상을 연한색, 진한색으로 설정하여 그라데이션 효과를 줄 수 있다.

Step 2. Metaball Animation을 적용시킬 도형 설정

Metaball Animation_1.jpg

현재 도형이 하나인 것처럼 보이지만 두개의 원을 겹쳐놓은 상태이다.

한개의 원을 위치를 고정시켜놓고 다른 원 하나는 Gesture의 조작에 따라 위치를 바꾸면서 고정된 원과 Interaction 효과를 보여줄 것이다.

Step 3. 두 원에 canvas를 사용하여 상호작용하도록 설정

Metaball Animation_2.gif

Canvas { context, size in
    // MARK: Adding Filters
    context.addFilter(.alphaThreshold(min: 0.5, color:.white))
    // MARK: This blur Radius determines the amount of elasticity between two elements
    context.addFilter(.blur(radius: 30))

    // MARK: Drawing Layer
    context.drawLayer { ctx in
        // MARK: Placing Symbols
        for index in [1,2] {
            if let resolvedView = context.resolveSymbol(id: index) {
                ctx.draw(resolvedView, at: CGPoint(x: size.width/2, y: size.height / 2))
            }
        }
    }
} symbols: {
    Ball()
        .tag(1)

    Ball(offset: dragOffset)
        .tag(2)
}

Canvas를 사용하여 두개의 원이 Interactive Animation을 할 수 있도록 설정한다.

여기서 blur효과를 적절히 사용하여 두개의 원이 찹쌀떡 마냥 들러붙는 시각 효과를 만들어준다.

Step 4. gesutre를 사용하여 제스쳐에따른 원의 위치 변화 설정

.gesture(
    DragGesture()
        .onChanged({ value in
            dragOffset = value.translation
        })
        .onEnded({ _ in
            withAnimation(.interactiveSpring(response: 0.6, dampingFraction: 0.7, blendDuration: 0.7)){
                dragOffset = .zero
            }
        })

)

DragGesture()를 사용하여 원 하나를 터치 후 위치를 이동 시킬 수 있다.

.onChanged 메소드를 사용하여 위치가 별하면 offset의 위치를 변화시켜 화면 View 위의 원 위치를 옮겨주고

.onEnded 메소드를 사용하여 Gesture가 종료되면 offset의 위치를 zero로 초기화하여 원위치로 설정한다.

원위치로 돌아오는 효과를 주기위해 interactiveSpring효과를 설정한다.

Step 5. mask를 사용하여 두 원에 색상 설정

Metaball Animation_3.gif

Rectangle()
	.fill(.linearGradient(colors: [Color("Gradient1"), Color("Gradient2")], startPoint: .top, endPoint: .bottom))
    .mask{
        ...
    }

시각 효과는 원에 색상을 설정한것처럼 보이지만 뒤 배경을 한개의 Rectangle을 만들어 주고 top에서 bottom까지 그라데이션 효과를 준뒤 mask를 활용해 원의 색상이 바뀌는 것처럼 보이게한다.

원을 화면의 상단으로 가져갈 수록 연한색, 하단으로 가져갈 수록 진한색으로 바뀌는 것을 확인할 수 있다.

Step 6. Clubbed 효과를 만들기 위한 원의 랜덤위치 설정

Metaball Animation_4.gif

Clubbed Animation은 이용자의 제스처가 아닌 도형들의 위치가 랜덤으로 변하며 생기는 효과이므로 도형의 위치가 랜덤으로 변할 수 있도록 설정한다. 이 때, 도형의 위치는 TimelineView를 사용하여 시간의 변화에 따라 위치를 초기화 시킨다.

Step 7.  animation 효과로 이동 애니메이션 설정

Metabll Animation_6.gif

TimelineView(.animation(minimumInterval: 3.6, paused: false))

이전 위치에서 다음 위치로 offset이 랜덤으로 변화될때 이전 위치에서 다음 위치로 도형들이 천천히 이동하는 것처럼 보이도록 animation 옵션을 설정한다.

Step 8.  도형들의 초기 위치를 화면 중앙으로 설정

 

Metaball Animation_7.gif

@State var startAnimation: Bool = false

 

 

앱 실행 초기 도형들의 위치는 화면 중앙(.zero)에 위치하도록 한 후 Bool을 사용하여 화면을 터치 혹은 클릭시 도형들의 위치가 랜덤으로 변화하기 시작하도록 설정한다.

ForEach(1...2, id:\.self) { index in
    let offset = (startAnimation ? CGSize(width: .random(in: -180...180), height: .random(in: -240...240)) : .zero)
    ClubbedRoundedRectangle(offset: offset)
        .tag(index)
}

Step 9.  여러개의 도형을 추가

Metaball Animation_8.gif

ForEach(1...15, id:\.self) { index in
    let offset = (startAnimation ? CGSize(width: .random(in: -180...180), height: .random(in: -240...240)) : .zero)
    ClubbedRoundedRectangle(offset: offset)
        .tag(index)
}

2개의 도형에서 15개(마음껏 설정 가능)로 설정하여 많은 도형이 서로 상호작용하며 퍼지고 모이는 효과를 설정한다.

이제 마지막으로 mask를 사용하여 도형에 그라데이션 색상을 넣어주고 Picker를 사용하여 Metabll, Clubbed 메뉴를 만들어 주면 끝!!!!

Result.gif

학습하며 느낀점 🧑🏻‍🏫:

시각효과라는 것이 점점 "그렇게 보이도록~"이라는 느낌이든다. 코드를 보기전 애니메이션을 보기만 했을때에는 도형에 색상을 넣는다고 생각했지만, 사실 그 뒤에 색상을 가진 다른 무언가가 있고 그 위에서 움직인다는.... 

반응형