Skip to content

Instantly share code, notes, and snippets.

@rudrankriyam
Created June 12, 2024 22:54
Show Gist options
  • Save rudrankriyam/a48fe804aec3876048542484f89609e2 to your computer and use it in GitHub Desktop.
Save rudrankriyam/a48fe804aec3876048542484f89609e2 to your computer and use it in GitHub Desktop.
Creating animated music background using SwiftUI's new MeshGradient
//
// AnimatedMeshView.swift
// Fussion
//
// Created by Rudrank Riyam on 11/06/24.
//
import Foundation
import MusicKit
import ColorKit
import SwiftUI
import MusadoraKit
@available(iOS 18.0, *)
struct AnimatingMeshView: View {
let referenceDate: Date
let song: Song = Songs.fusionSongs[0]
@State private var colors: [Color] = []
@State private var currentEntry: ApplicationMusicPlayer.Queue.Entry?
@State var t: Float = 0.0
@State var timer: Timer?
var body: some View {
ZStack {
MeshGradient(
width: 3,
height: 3,
points: [
[0.0, 0.0],
[0.5, 0.0],
[1.0, 0.0],
[sinInRange(-0.8...(-0.2), 0.439, 0.342, t), sinInRange(0.3...0.7, 3.42, 0.984, t)],
[sinInRange(0.1...0.8, 0.239, 0.084, t), sinInRange(0.2...0.8, 5.21, 0.242, t)],
[sinInRange(1.0...1.5, 0.939, 0.684, t), sinInRange(0.4...0.8, 0.25, 0.642, t)],
[sinInRange(-0.8...0.0, 1.439, 0.442, t), sinInRange(1.4...1.9, 3.42, 0.984, t)],
[sinInRange(0.3...0.6, 0.339, 0.784, t), sinInRange(1.0...1.2, 1.22, 0.772, t)],
[sinInRange(1.0...1.5, 0.939, 0.056, t), sinInRange(1.3...1.7, 0.47, 0.342, t)]
],
colors: colors,
background: .black)
.ignoresSafeArea()
VStack {
AsyncImage(url: song.artwork?.url(width: 1024, height: 1024), content: { image in
image
.resizable()
.scaledToFit()
.cornerRadius(16)
}, placeholder: {
Color.red
.frame(maxWidth: .infinity)
.aspectRatio(contentMode: .fit)
.cornerRadius(8)
})
.padding()
VStack {
Text(song.title)
.bold()
.font(.title)
Text(song.artistName)
.font(.title2)
}
.foregroundStyle(.white)
Spacer()
}
}
.onAppear {
ApplicationMusicPlayer.shared.queue.currentEntry = .some(.init(Songs.fusionSongs[0]))
getBackgroundColor(for: ApplicationMusicPlayer.shared.queue.currentEntry?.artwork?.url(width: 1024, height: 1024))
timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { _ in
t += 0.02
})
}
}
func sinInRange(_ range: ClosedRange<Float>, _ offset: Float, _ timeScale: Float, _ t: Float) -> Float {
let amplitude = (range.upperBound - range.lowerBound) / 2
let midPoint = (range.upperBound + range.lowerBound) / 2
return midPoint + amplitude * sin(timeScale * t + offset)
}
private func getBackgroundColor(for url: URL?) {
Task {
do {
guard let artworkURL = url else { return }
self.currentEntry = ApplicationMusicPlayer.shared.queue.currentEntry
let (artworkImageData, _) = try await URLSession.shared.data(from: artworkURL)
guard let image = UIImage(data: artworkImageData) else { return }
let dominantUIColors: [UIColor] = try image.dominantColors()
if !dominantUIColors.isEmpty {
self.colors = dominantUIColors.map { Color($0) }
}
} catch {
debugPrint(error)
}
}
}
}
@available(iOS 18.0, *)
#Preview {
AnimatingMeshView(referenceDate: .now)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment