
Olá pessoal! Há cerca de seis meses, lançamos um dos recursos mais interessantes do Badoo:
transmissão ao vivo . Uma de suas principais funcionalidades é que os espectadores podem enviar presentes para seus streamers favoritos para expressar sua gratidão. Queríamos tornar os presentes os mais chiques e atraentes possíveis, por isso foi decidido deixar alguns deles realmente animados, e com isso quero dizer animados. E para envolver ainda mais as pessoas, nós, a equipe do Badoo, planejamos atualizar esses presentes e animações a cada poucas semanas.
Como engenheiro do iOS, você já deve ter adivinhado o desafio que enfrentamos aqui: a necessidade de adicionar novas animações e remover as antigas exigia uma quantidade considerável de trabalho do lado do cliente. Precisávamos das equipes de desenvolvimento do Android e do iOS para cada versão - o que, combinado com o tempo que as revisões e a aprovação da App Store costumam levar, levaria alguns dias para que cada atualização fosse lançada. Mas resolvemos o problema e vou explicar como.
Visão geral da solução
Nesta fase, já sabíamos como
exportar animações do Adobe After Effects (AAE) para o formato legível pelo nosso aplicativo iOS usando a biblioteca Lottie. Desta vez, porém, fomos um pouco mais longe: decidimos criar um tipo de serviço de armazenamento de animação, disponível via internet. Em outras palavras, armazenaríamos todas as animações reais no servidor e as entregaríamos aos aplicativos clientes sob demanda:

Aqui está a aparência da solução final no simulador iOS na máquina do desenvolvedor:
No entanto, neste post, o exemplo que vou usar é uma animação muito simples que eu mesmo criei. Não é tão sofisticado quanto o do Badoo, mas é bom o suficiente para demonstrar o potencial da abordagem descrita.
Exportando animações
O projeto de animações do Adobe After Effects (AAE) que estou usando aqui pode ser encontrado junto com outros
arquivos de origem no github . Portanto, depois de abrir o projeto de animação AAE localizado em
_raw/animations/Fancy/Fancy.aep
, você deverá ver uma janela como esta:

Neste ponto, não vou explicar como as animações estão sendo criadas no AEE, mas o que vou explicar é como importar animações já existentes do AAE para um formato legível por aplicativo iOS usando o
plug-in Bodymovin .
Depois de verificar se o plug-in está instalado, abra-o selecionando a opção
Window / Extensions / Bodymovin no menu:

Agora você deve ver a janela Bodymovin, onde pode selecionar a animação que deseja exportar, especificar o caminho do arquivo de saída e abrir as configurações de exportação:

Após selecionar e abrir as configurações de animação, agora podemos pedir ao Bodymovin para incorporar os ativos no arquivo JSON resultante, marcando a opção
Assets / Include in json :

Por fim, a composição de animação selecionada é exportada e salva no arquivo especificado, clicando no botão
Renderizar .
Armazenando animações no servidor
Vamos supor que movemos nossos arquivos JSON de animações renderizadas para o servidor da Web preferido via Internet. No nosso caso, por uma questão de simplicidade, eu os carreguei no repositório github deste projeto. As animações estão disponíveis aqui:
URL base:
https://raw.githubusercontent.com/chupakabr/server-provided-animations/master/_raw/rendered-animations/IDs específicos da animação:
clouds.json
fireworks.json
Nota: Procurando um servidor Web de provedor de animações escrito em Swift? Encontre a solução aqui no github e uma explicação detalhada neste artigo .
Neste ponto, temos um servidor provedor de animações totalmente funcional, então é hora de passar para a parte mais interessante: apresentar as animações aos nossos usuários.
Buscando e apresentando animações
Neste ponto, recomendo fortemente abrir nosso exemplo de projeto de aplicativo para iOS localizado em
Client/ServerProvidedAnimation.xcworkspace
pois ele já possui todo o código e configurações necessários.
Carregando dados de animações
Dado que os pontos de extremidade da API REST para obter dados de animação agora estão em funcionamento, é hora de introduzir o protocolo do provedor de dados e adicionar a implementação do servidor:
import Lottie protocol AnimationsProviderProtocol { typealias Completion = (_ animation: LOTComposition?) -> Void func loadAnimation(byId id: String, completion: @escaping Completion) } final class ServerAnimationProvider: AnimationsProviderProtocol { private let endpoint: URL init(endpoint: URL) { self.endpoint = endpoint } func loadAnimation(byId id: String, completion: @escaping Completion) { let path = "/\(id).json" guard let animationUrl = URL(string: path, relativeTo: self.endpoint) else { completion(nil) return } URLSession.shared.invalidateAndCancel() let task = URLSession.shared.dataTask(with: animationUrl) { (data, response, error) in guard error == nil, let data = data, let json = self.parseJson(from: data) else { completion(nil) return } let animation = LOTComposition(json: json) completion(animation) } task.resume() } private func parseJson(from data: Data?) -> [AnyHashable : Any]? { guard let data = data else { return nil } do { let json = try JSONSerialization.jsonObject(with: data, options: []) as? [AnyHashable : Any] return json } catch { return nil } } }
Essa classe de provedor de dados nos permite carregar animações do servidor no formato JSON sob demanda e mantê-las na memória para renderização na interface do usuário. Supondo que estamos seguindo o padrão MVVM, ele pode ser facilmente usado na entidade
ViewModel
da seguinte maneira:
// ... private let animationProvider: AnimationsProviderProtocol private(set) var animationModel: LOTComposition? // … func loadAnimation(byId animationId: String) { self.animationProvider.loadAnimation(byId: animationId) { [weak self] (animationModel) in self?.animationModel = animationModel } } // ...
O
ViewModel
atualiza a propriedade de dados de animação selecionada quando recebe uma resposta HTTP válida do servidor com um objeto JSON não vazio dentro. Esses dados são usados pela camada de apresentação para agendar a renderização da animação.
Camada de apresentação
Agora podemos usar nosso ViewModel para acessar os dados da animação e apresentá-los através da interface do usuário no manipulador de ações "no toque" anexado ao botão:
class ViewController: UIViewController { // ... @IBOutlet weak var animationContainer: UIView! override func viewDidLoad() { super.viewDidLoad() // ... self.animationView = { let view = LOTAnimationView(frame: self.animationContainer.bounds) self.animationContainer.addSubview(view) return view }() } @IBAction func onPlayAnimationAction(_ sender: Any) { self.animationView.stop() self.animationView.sceneModel = self.viewModel.animationModel self.animationView.play() } }
Basicamente, o que temos aqui é um manipulador de botão que aciona uma atualização da instância LOTAnimationView com os dados de animação mais recentes provenientes do
ViewModel
.
Veja como é o resultado final:
É isso mesmo. Agora, as animações estão sendo carregadas do terminal de API REST preparado e renderizadas no cliente sob demanda.
Dicas e Limitações
Dicas e truques:
- O AAE permite o uso da maioria dos tipos de ativos, incluindo gráficos de varredura e vetoriais;
- O Bodymovin torna possível incorporar todos os ativos em um arquivo de animação JSON de saída (usando a codificação base64) - isso significa que podemos evitar o carregamento de ativos separados no lado do cliente;
- Para as animações, você pode optar entre desenhar diretamente no vetor no AAE ou simplesmente importar gráficos vetoriais do Adobe Illustrator.
Infelizmente, ainda não é possível importar gráficos vetoriais SVG para o AAE (tentei!).
Mais truques e possíveis problemas são descritos neste
incrível artigo escrito pelo meu colega
Radoslaw Cieciwa .
Conclusões
Então, o que o uso de animações fornecidas pelo servidor nos fornece? O benefício mais óbvio dessa abordagem é a capacidade de dissociar todos os envolvidos no fluxo de atualização das animações. Em outras palavras, para lançar uma nova animação sofisticada, todos os designers precisam fornecer a representação JSON da animação à equipe do servidor. E para remover uma, a equipe do servidor apenas precisa remover essa animação específica do serviço de descoberta. Sem tempo perdido!
Outra coisa interessante é que a mesma funcionalidade pode ser implementada em todas as plataformas de clientes suportadas (iOS, Android, Web, etc.) sem precisar ajustar a funcionalidade do servidor ou animações brutas existentes.
É isso por hoje! Obrigado pela leitura
Recursos