Xamarin | Transições com SkiaSharp

Além de controles personalizados, podemos utilizar o SkiaSharp para montar transições e muito mais. O poder de utilizar um canvas em branco e interagir com ele nos abre um leque gigante de possibilidades.
Decidi trazer, nesse post, uma idéia que encontrei no Github de uma library de transição para IOS chamada Concentric Onboarding.

1. Iniciando projeto
Eu já escrevi uma breve introdução de utilização do SkiaSharp, então vou deixar a parte de instalação do Nuget e explicação da library de lado. Se você tiver interesse, nesses detalhes, pode acessar aqui.
2. Criando a View
Assim como no outro post, vamos criar uma view apenas para a implementação dessa nossa personalização, assim podemos reaproveitar essa lógica em vários lugares e fica fácil de fazer manutenção.
Aqui eu optei por herdar do próprio SKCanvasView, apenas para mostrar uma abordagem um pouco diferente do post anterior.
Como estamos herdando de SKCanvasView, podemos sobrescrever o método ´OnPaintSurface´ e utilizar as informações da surface para criar nossos elementos.
De inicio eu criei um SKRect para persistir a área ocupada pelo botão, quase como um Bounding-Box. Esse elemento vai ser útil para interação com touch.
Mais abaixo criei um método para desenhar o background do elemento, desenhando um retângulo do tamanho total da view.
Em seguida temos o método que desenha o botão circular, na parte inferior da tela. Aqui a lógica trabalha em incrementar o diâmetro do botão conforme o progresso da animação for prosseguindo, até a metade do progresso total e, logo após, reduzir o diâmetro até o tamanho original. O método utilizado para criar o circulo, no SkiaSharp é o ArcTo (elíptico), que nos possibilita criar uma curva passando como parâmetros o raio (x/y), ponto final (x/y), rotação e direção (sentido horário ou anti-horário).
Iniciamos o desenho à partir do ponto esquerdo-central do circulo, fazendo um arco para o ponto esquerdo-central.

Para mais detalhes de como funciona o arco elíptico, acesse a documentação oficial.
3. Bindable properties
Como todo controle reaproveitável, é bom criarmos parâmetros para alterar alguns detalhes da nossa view, quando formos utilizá-la.
Optei por criar propriedades para definir o diâmetro do botão, um comando a ser executado após o botão ser acionado e um evento ao iniciar a animação de transição. Além disso criei um evento que atualiza o canvas, quando alteramos o diâmetro do botão.
4. Capturando Touch
Para criarmos a ação de interação do usuário com o botão, precisamos capturar o Touch e validar onde ele está ocorrendo dentro do canvas.
Vamos sobrescrever o método OnTouch, do SKCanvasView, e validar se o o ponto está dentro da àrea do SKRect, que indica o espaço ocupado pelo botão.
Importante! Para que os eventos de toque sejam disparados é necessário habilitar, esses eventos, através da propriedade EnableTouchEvents.
Toda vez que um Touch Event ocorrer, e for uma action “Pressed”, vamos verificar se a localização do toque está na área que pertence ao botão. Quando isso ocorrer, iremos disparar o método ActionButtonClicked, onde buscaremos uma nova cor para o botão e iniciaremos a animação de transição de forma assíncrona. A animação, por si só, é basicamente um while até atingirmos o progresso total, e ficamos incrementando a variável CurrentProgress, que é utilizada com parâmetro para redimensionar o botão.
Ao final, invertemos as cores do botão e de fundo, redefinimos o progresso para zero e liberamos a view para receber novamente o evento de touch.
À essa altura já possuímos esse resultado:

5. CarouselView
Optei por utilizar, nesse exemplo, o CarouselView, mas você pode usufruir da CollectionView que foi liberada oficialmente na versão 4.3 do Xamarin.Forms.
Escrevi um post sobre as alterações que a versão 4.3 trouxe e as melhorias do CarouselView, você pode acessar aqui.
Utilizei o evento OnAnimationStart, que criamos no ConcentricOnboardView.cs, para iniciar o scroll do CarouselView.
Adicionei no mesmo grid, sobrepondo ao canvas, o carouselview utilizando apenas 80% da altura total da página, liberando 20% do espaço para o botão receber o touch.
No evento OnAnimationStart eu busco qual o item que está sendo apresentado atualmente na tela e tento buscar o próximo índice, caso esteja fora da coleção eu busco o primeiro, garantindo um retorno a posição inicial.
Por fim, o resultado:

Se você quiser ver com mais detalhes, acesse o repositório que eu fiz esse exemplo:
https://github.com/felipebaltazar/Xam-ConcentricOnboarding

Referências
- Exyte ConcentricOnboarding
- Documentação da Microsoft para criação de elementos gráficos;