[SWIFT] I tried to introduce UI animation to Pokedex using Poké API

This article is the ** 24th day ** article of and factory Advent Calendar 2020. Yesterday was @yst_i's "I implemented an animation using MotionLayout's KeyCycle"!

Introduction

This time, it was written by @ fr0g_fr0g. "I made an iOS app for MVP + Clean Architecture using Poké API, so I will explain it" It is an article that I tried to introduce UI animation to the application called Pokedex explained in.

The development background of Pokedex is as follows.

Recently, when talking to people who have no experience in iOS work at study sessions, I was conscious of what I should learn to get a job as an iOS engineer and what I actually do in my work. I'm often asked if I'm making full use of design and tools, so I wish I could explain it.

When I saw this In my case, I've had a lot of opportunities to talk about UI animation in my career (both web front and iOS, but both), so there are quite a few people who are interested in that. I have felt that, so By adding that element to Pokedex, I thought it would be a product that more beginners would learn more.

So, this time I'm talking about introducing UI animation to Pokedex.

Kobanashi

In my opinion, both internal quality (design) and external quality (appearance) are important for a product. No matter how good the internal quality is, if the external quality is not enough, ** it may feel "difficult to use" or "poor" or you may not be attached to it **. No matter how good the external quality is, if the internal quality is not enough, ** it will take time to generate defects, repair and implement additional functions, and it will be difficult to deliver value. ** ** Therefore, by aiming for height in both cases, the absolute value of the product will increase, and it will become a ** "used" ** product for users. This absolute value will be an important factor, especially if there are products with similar themes out there, in order to win the user's ** "which one to use" choice **.

As an engineer It ’s good to have both internal and external quality, It is good to specialize in internal quality (external quality), I think both of them will be valuable human resources in team development.

Therefore, this time, in order to improve the external quality of Pokedex, which has solid internal quality, we added UI animation. It is a project to improve the value of Pokedex so that everyone who is interested in the inside and those who are interested in the outside can learn **. (Projected without permission)

Animation introduction of each screen

List screen

This is the screen. スクリーンショット 2020-12-11 16.37.59.png

It is a screen to see a list of Pokemon. Here we do the following:

  1. Roughly see various Pokemon
  2. Decide what to see detailed information

So, I will explain what each of them thought about and animated.

Deliverables

pokedex7.gif

Description of each animation

Roughly see various Pokemon

The aim is to create fun as a Pokédex by scrolling. Currently, we do not have a method to find a specific Pokemon such as sorting, narrowing down, free word search, etc., so scrolling tends to increase when using this screen, so it is likely to be more effective. Shin. Therefore, this time, we have incorporated an animation ** in which the UI flows from right to left when Pokemon appears so that Pokemon will appear one after another according to scrolling. The animation of each element is quite simple because it only flows in, but since multiple elements are displayed on one screen, the screen as a whole makes a comfortable movement. ** If you are too elaborate on each animation, your eyes will get tired when multiple animations appear **, so while keeping it moderate ** Adjust so that even if multiple elements move, they will not be isolated and the comfort will overlap ** I will continue to do it. Specifically, be aware of ** shifting the start timing for each element ** by starting the animation at the timing when the elements are displayed by scrolling, and ** making the animation time and easing familiar **. Did. It is important to see both the trees and the forest, as the balance between the individual elements and the entire screen affects the usability.

Decide what to see detailed information

If you want to see the details of the target Pokemon and tap the element, it will transition to the details screen. Well, this is what the user expected. The aim is to add another twist to this and make Pokemon more attached. It's more fun to think that Pokemon are alive! (What happened suddenly) So, when the user selects Pokemon, I wanted to make the Pokemon respond. (It's the one you decided on, yeah) I don't know if it's two-way communication, but it's more fun to be able to imagine that. Hopefully, I wish I could make a cry when I selected it, but the difference between games and apps also appears in these places, so it's not used because of the sound. I'm surprised to hear the sound without permission, stop. I thought that it was not suitable for this app because it is an environment that has up to, so this time I decided to express the answer by jumping Pokemon. Specifically, I settled down to the point that ** by jumping when pressed (when pushed), you can check the jump while making a transition **. If you jump when tapped (when pressed and released), it will transition before you see the jump, so it's a bit sad, but delaying the transition to show the jump will block the user's action, so refrain from doing so Without it, I settled down here. In the app, I think it's okay to make the animation at the destination reasonably flashy, but the important point is not to disturb the user until you get there.

Implementation

I will put the code concretely.

First, the Cell file.

--Move from the position before animation to the target position --Bounce Pokemon when pressed

PokemonListCell.swift


final class PokemonListCell: UITableViewCell {

    func abbreviate() {
        let x: CGFloat = UIScreen.main.bounds.width * 0.375 //How much is the initial position
        self.innerView.transform = .init(translationX: x, y: 0.0)
        self.innerView.alpha = 0.3
    }

    func expand() {
        self.innerView.transform = .identity
        self.innerView.alpha = 1.0
    }

    func animateImage() {
        let keyframeTranslateY      = CAKeyframeAnimation(keyPath: "transform.translation.y") //keyPath does not work unless you enter a fixed character string
        keyframeTranslateY.values   = [0.0, -5.0, 0,0, -2.5, 0.0]
        keyframeTranslateY.keyTimes = [0, 0.25, 0.4, 0.6, 1.0]
        keyframeTranslateY.duration = 0.2

        self.spriteImageView.layer.add(keyframeTranslateY, forKey: "jumping") //This forKey string is free
    }
}

And the view controller. It is a lot of excerpts. Since it appears according to scrolling, the Cell animation is run with willDisplay of UITableView.

PokemonListViewController.swift


final class PokemonListViewController: UIViewController {}

// MARK: - UITableViewDelegate
extension PokemonListViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard let cell = cell as? PokemonListCell else { return }
        cell.abbreviate() //Move to the initial position
        UIView.animate(withDuration: 0.4, delay: 0, options: [.allowUserInteraction, .curveEaseInOut], animations: {
            cell.expand() //Start animation
        }, completion: nil)
    }
}

Details screen

This is the screen. スクリーンショット 2020-12-11 16.38.08.png

This is the screen to see the detailed information of Pokemon. Here we do the following:

  1. View detailed information on the target Pokemon
  2. View the pre-evolution Pokemon of the target Pokemon

This time, I will explain the animation related to "View detailed information of the target Pokemon". (Those before evolution will be omitted this time)

Deliverables

pokedex5.gif

Description of each animation

View detailed information on the target Pokemon

See detailed information Since the target Pokemon has been decided and changed, we will focus on "charming" Pokemon. In words, the UI animation was designed by focusing on the part of ** animation to make people attach to it ** rather than the part of displaying information without waiting for the user. It takes more time than displaying it at once, but since this is one destination without doing anything from the Pokemon details screen, I thought it was important to increase the enjoyment of seeing Pokemon. On the other hand, excessive production can cause dissatisfaction, saying, "It takes time to see the details of Pokemon," so a sense of balance is important. If you divide the main important parts into phases

  1. Data is loading
  2. When displaying Pokemon
  3. When the status is displayed

There are three. I would like to write about each of them.

Data loading

From the screen transition to getting the detailed information of Pokemon with API, isn't it? If it is a general application, I think that it will display an indicator etc. to inform the user that data is being loaded. There is no difference with Pokedex, but I wanted to make it a loading display related to Pokemon instead of just an indicator, so this time I decided to shake the monster ball to create a ** Pokemon feeling that it will come out soon **. .. At the same time, I also wanted to make it so that it could be connected to the next Pokemon display phase. If there are multiple phases, ** continuous animation will make the flow feel more comfortable as a whole, rather than separate animations. ** **

When displaying Pokemon

How to get data and display Pokemon. I was shaking the monster ball while acquiring the data earlier, so I wanted to make it look like it came out of it, so I saw that scene many times in Pokemon's animation. Apparently, there seems to be a phase in which the monster ball opens and the Pokemon comes out, while something strange or hazy comes out of the monster ball. Then, I wanted to incorporate it based on that as much as possible. For those who like the original, it is better to reproduce it as much as possible or make it an animation that makes you feel it, as long as the original one already exists in the world. So, since the theme of this time is Pokemon, which has a lot of fans, I thought that I could not remove it, and it is no exaggeration to say that I designed the entire animation in that direction. Specifically, after the monster ball shook and a haze appeared there, the monster that had shrunk to the size of the monster ball appeared while scaling to the real size. So, I think that this is the most interesting point throughout, so I decided to execute the animation in the center of the screen so that I can focus only on the monsters without displaying any other information. This is also one point, it is easy to hide other elements to draw attention to something, but that alone does not explain the ** hidden margin * * May be. In this case, the Pokemon is placed at the top as the final state of the screen, so if you hide the part below the name, about half of the screen will be wrapped in a strange margin. Then, what is this margin? Therefore, it is important to be aware of such ** feelings that you do not want to embrace and psychology that you do not want to work, and focus on what you want to show **.

When the status is displayed

It is a scene to display the status of Pokemon after displaying the appearance of Pokemon. Since the Pokemon was displayed in the center of the screen in the previous phase, it is necessary to move it to the final state position of the screen from there. At the same time, it is necessary to display the status, so it is comfortable to ** shift the line of sight to it as smoothly as possible **. If you feel a break, you will feel uncomfortable, so be careful not to do so. Since the status part has a UI similar to the list screen, the movement should match it. As the overall design of the app, ** similar UIs will give a sense of unity by performing similar movements and functions, and users will be able to use them without hesitation **. The status is divided into two parts, the left side that displays the type and ability, and the right side that displays the parameters such as attack and defense, and the right side contains another animation of increasing values ​​and bar graphs. .. Attack and defense parameters are assigned to each Pokemon, and the higher the value, the stronger the **. For values ​​and graphs of these properties, animation that extends from 0 to the desired value makes it easier to understand because you can intuitively feel the size and amount.

Implementation

For those that originally have originals such as anime and games, I would like to do something about it because it will be a source of quality and attachment. However, I used ** SpriteKit ** this time because that movement cannot be dealt with by simple deformation.

I will put the code concretely.

As a point, in order to secure the minimum animation time of the monster ball, the appearance animation is executed with the timer of the number of seconds and the loading of the image as a trigger. I don't know what happened if the monster ball is displayed for a moment, so I have some time to create the feeling of coming out of the monster ball.

Animation is roughly divided into three.

  1. Pokemon image
  2. Monster ball
  3. White haze when Pokemon comes out

Let's look at each.

Pokemon image

Animation of Pokemon images is done by ** CA Basic Animation **. He is good at simple animation that moves from this value to this value in this time. This time Reduce the transparency and scale before it appears, By increasing the transparency and scale at the time of appearance and adding a movement animation in the middle It expresses the feeling of being three-dimensionally coming out of the monster ball.

Also, I want to express the feeling that comes out of the monster ball, but when I try to animate the element by default, the center of the element becomes the base point. If that is the case, the feeling of coming out of the monster ball cannot be expressed, so

self.imageView.layer.anchorPoint = .init(x: 0.5, y: 1.0)

As a starting point, the bottom center of the element.

PokemonDetailImageView.swift


final class PokemonDetailImageView: XibLoadableView {

    @IBOutlet private weak var imageView: UIImageView! {
        willSet {
            newValue.layer.anchorPoint = .init(x: 0.5, y: 1.0)
            newValue.alpha = 0.0
        }
    }

    private func animate() {
        Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { [weak self] _ in
            guard let self = self else { return }
            self.appearImage()
        })
    }

    private func appearImage() {
        let opacityAnimate                   = CABasicAnimation(keyPath: "opacity")
        opacityAnimate.fromValue             = 0.0
        opacityAnimate.toValue               = 1.0
        opacityAnimate.duration              = 0.2
        opacityAnimate.timingFunction        = Easing.EaseOut.quart.function
        opacityAnimate.isRemovedOnCompletion = false
        opacityAnimate.fillMode              = .forwards

        let scaleAnimate                   = CABasicAnimation(keyPath: "transform.scale")
        scaleAnimate.fromValue             = 0.2
        scaleAnimate.toValue               = 1.0
        scaleAnimate.duration              = 0.2
        scaleAnimate.timingFunction        = Easing.EaseOut.quart.function
        scaleAnimate.isRemovedOnCompletion = false
        scaleAnimate.fillMode              = .forwards

        let startYAnimate                   = CABasicAnimation(keyPath: "transform.translation.y")
        startYAnimate.fromValue             = 0.0
        startYAnimate.toValue               = -20.0
        startYAnimate.duration              = 0.15
        startYAnimate.timingFunction        = Easing.EaseInOut.circ.function
        startYAnimate.isRemovedOnCompletion = false
        startYAnimate.fillMode              = .forwards

        let endYAnimate                   = CABasicAnimation(keyPath: "transform.translation.y")
        endYAnimate.fromValue             = -20.0
        endYAnimate.toValue               = 0.0
        endYAnimate.duration              = 0.2
        endYAnimate.beginTime             = CACurrentMediaTime() + 0.15
        endYAnimate.timingFunction        = Easing.EaseInOut.circ.function
        endYAnimate.isRemovedOnCompletion = false
        endYAnimate.fillMode              = .forwards
        endYAnimate.delegate              = self

        self.imageView.layer.add(opacityAnimate, forKey: "opacity")
        self.imageView.layer.add(scaleAnimate, forKey: "scale")
        self.imageView.layer.add(startYAnimate, forKey: "translation.y.start")
        self.imageView.layer.add(endYAnimate, forKey: "translation.y.end")
    }
}
monster Ball

Animation of monster ball is done in ** CA Keyframe Animation **. It is repeatedly executed with keyframe animation. At this timing, you can finely set what you want to be in this state, and you are good at repeating the same movement.

By specifying values ​​and keyTimes, you can set which state at what timing. By setting repeatDuration to .infinity, it will be infinitely repeated.

Also, when rotating as standard, it also tries to rotate around the center point of the element. However, in order to express that the monster ball sways, I want you to rotate around the bottom center, so set the anchor point

self.monsterBallImageView.layer.anchorPoint = .init(x: 0.5, y: 1.0)

It is said.

I'm going to get data with viewDidLoad, but in the meantime, in order to shake the monster ball, I shake the monster ball with viewDidAppear. (Hit prepareLoading with viewDidAppear)

PokemonDetailImageView.swift


final class PokemonDetailImageView: XibLoadableView {

    @IBOutlet private weak var monsterBallImageView: UIImageView!

    func prepareLoading() {
        self.showMonsterBall()
    }

    private func animate() {
        self.hideMonsterBall()
    }
}

// MARK: - MonsterBall
extension PokemonDetailImageView {

    private func showMonsterBall() {
        let keyframeRotate            = CAKeyframeAnimation(keyPath: "transform.rotation.z")
        keyframeRotate.values         = [0, 20 * CGFloat.pi / 180, 0, -20 * CGFloat.pi / 180, 0]
        keyframeRotate.keyTimes       = [0, 0.25, 0.5, 0.75, 1]
        keyframeRotate.duration       = 1.2
        keyframeRotate.repeatDuration = .infinity

        let position = self.monsterBallImageView.layer.position
        self.monsterBallImageView.layer.anchorPoint = .init(x: 0.5, y: 1.0)
        self.monsterBallImageView.layer.position = .init(x: position.x, y: position.y + self.monsterBallImageView.bounds.height / 2)
        self.monsterBallImageView.layer.add(keyframeRotate, forKey: "transform.rotation.z")

        UIView.animate(withDuration: 0.1, delay: 0, options: .curveEaseOut, animations: { [weak self] in
            guard let self = self else { return }
            self.monsterBallImageView.alpha = 1.0
        }, completion: nil)
    }

    private func hideMonsterBall() {
        UIView.animate(withDuration: 0.2, delay: 0.2, options: .curveEaseOut, animations: { [weak self] in
            guard let self = self else { return }
            self.monsterBallImageView.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
            self.monsterBallImageView.alpha = 0.0
        }, completion: nil)
    }
}
White haze when Pokemon comes out

Animation of white moyamoya is done with ** SpriteKit **.

(Here, explanation of particle creation)

So, in order to move the created file, prepare SKView and specify SKScene as present. This is an image of the particles moving in the place called SKScene specified by the element called SKView.

When you're ready, create a SKEmitterNode based on the particle file you just created. After that, put it on the scene and the animation will start!

By combining these three animations We were able to realize a Pokemon appearance animation.

PokemonDetailImageView.swift


final class PokemonDetailImageView: XibLoadableView {

    private var skView: SKView?

    private func animate() {
        self.createEmitter()

        Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { [weak self] _ in
            guard let self = self else { return }
            self.hideEmitter()
        })
    }
}

// MARK: - Emitter
extension PokemonDetailImageView {

    private func createEmitter() {
        let size = self.bounds
        let doubleSize = CGSize(width: size.width * 2, height: size.height * 2)

        //Prepare the view itself in double size so that the particles do not break
        self.skView = SKView(frame: .init(origin: .init(x: -self.bounds.width / 2, y: -self.bounds.height / 2), size: doubleSize))
        self.skView?.backgroundColor = .clear
        self.addSubview(self.skView!)

        let scene = SKScene(size: self.skView?.bounds.size ?? .zero)
        scene.backgroundColor = .clear
        self.skView?.presentScene(scene)

        if let node = SKEmitterNode(fileNamed: "appearPokemon") {
            node.position = CGPoint(x: scene.frame.width / 2, y: scene.frame.height / 2 - self.bounds.height / 2 + 60)
            scene.addChild(node)
        }
    }

    private func hideEmitter() {
        UIView.animate(withDuration: 0.1, delay: 0.2, options: .curveEaseOut, animations: { [weak self] in
            guard let self = self else { return }
            self.skView?.subviews.forEach { $0.removeFromSuperview() }
            self.skView?.removeFromSuperview()
            self.skView = nil
        }, completion: nil)
    }
}

Finally the whole code.

PokemonDetailImageView.swift


import UIKit
import SpriteKit

protocol PokemonDetailImageViewDelegate: AnyObject {
    func finishedPokemonImageViewShowAnimation()
}

final class PokemonDetailImageView: XibLoadableView {

    @IBOutlet private weak var imageView: UIImageView! {
        willSet {
            newValue.layer.anchorPoint = .init(x: 0.5, y: 1.0)
            newValue.alpha = 0.0
        }
    }
    @IBOutlet private weak var monsterBallImageView: UIImageView!

    private var skView: SKView?
    private var isLoading: Bool = false
    weak var delegate: PokemonDetailImageViewDelegate?

    func prepareLoading() {
        self.showMonsterBall()
    }

    func setImage(_ imageUrl: URL?) {
        self.isLoading = false

        Timer.scheduledTimer(withTimeInterval: 0.8, repeats: false, block: { [weak self] _ in
            guard let self = self else { return }
            self.animate()
        })

        self.imageView.loadImage(with: imageUrl, placeholder: nil, completion: { [weak self] _ in
            guard let self = self else { return }
            self.animate()
        })
    }

    private func animate() {
        //Delay and image loading for monster ball animation, start Pokemon appearance animation triggered by the slower one
        if !self.isLoading {
            self.isLoading = true
            return
        }

        self.hideMonsterBall()
        self.createEmitter()

        Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { [weak self] _ in
            guard let self = self else { return }
            self.hideEmitter()
            self.appearImage()
        })
    }

    private func appearImage() {
        let opacityAnimate                   = CABasicAnimation(keyPath: "opacity")
        opacityAnimate.fromValue             = 0.0
        opacityAnimate.toValue               = 1.0
        opacityAnimate.duration              = 0.2
        opacityAnimate.timingFunction        = Easing.EaseOut.quart.function
        opacityAnimate.isRemovedOnCompletion = false
        opacityAnimate.fillMode              = .forwards

        let scaleAnimate                   = CABasicAnimation(keyPath: "transform.scale")
        scaleAnimate.fromValue             = 0.2
        scaleAnimate.toValue               = 1.0
        scaleAnimate.duration              = 0.2
        scaleAnimate.timingFunction        = Easing.EaseOut.quart.function
        scaleAnimate.isRemovedOnCompletion = false
        scaleAnimate.fillMode              = .forwards

        let startYAnimate                   = CABasicAnimation(keyPath: "transform.translation.y")
        startYAnimate.fromValue             = 0.0
        startYAnimate.toValue               = -20.0
        startYAnimate.duration              = 0.15
        startYAnimate.timingFunction        = Easing.EaseInOut.circ.function
        startYAnimate.isRemovedOnCompletion = false
        startYAnimate.fillMode              = .forwards

        let endYAnimate                   = CABasicAnimation(keyPath: "transform.translation.y")
        endYAnimate.fromValue             = -20.0
        endYAnimate.toValue               = 0.0
        endYAnimate.duration              = 0.2
        endYAnimate.beginTime             = CACurrentMediaTime() + 0.15
        endYAnimate.timingFunction        = Easing.EaseInOut.circ.function
        endYAnimate.isRemovedOnCompletion = false
        endYAnimate.fillMode              = .forwards
        endYAnimate.delegate              = self

        self.imageView.layer.add(opacityAnimate, forKey: "opacity")
        self.imageView.layer.add(scaleAnimate, forKey: "scale")
        self.imageView.layer.add(startYAnimate, forKey: "translation.y.start")
        self.imageView.layer.add(endYAnimate, forKey: "translation.y.end")
    }
}

// MARK: - MonsterBall
extension PokemonDetailImageView {

    private func showMonsterBall() {
        let keyframeRotate            = CAKeyframeAnimation(keyPath: "transform.rotation.z")
        keyframeRotate.values         = [0, 20 * CGFloat.pi / 180, 0, -20 * CGFloat.pi / 180, 0]
        keyframeRotate.keyTimes       = [0, 0.25, 0.5, 0.75, 1]
        keyframeRotate.duration       = 1.2
        keyframeRotate.repeatDuration = .infinity

        let position = self.monsterBallImageView.layer.position
        self.monsterBallImageView.layer.anchorPoint = .init(x: 0.5, y: 1.0)
        self.monsterBallImageView.layer.position = .init(x: position.x, y: position.y + self.monsterBallImageView.bounds.height / 2)
        self.monsterBallImageView.layer.add(keyframeRotate, forKey: "transform.rotation.z")

        UIView.animate(withDuration: 0.1, delay: 0, options: .curveEaseOut, animations: { [weak self] in
            guard let self = self else { return }
            self.monsterBallImageView.alpha = 1.0
        }, completion: nil)
    }

    private func hideMonsterBall() {
        UIView.animate(withDuration: 0.2, delay: 0.2, options: .curveEaseOut, animations: { [weak self] in
            guard let self = self else { return }
            self.monsterBallImageView.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
            self.monsterBallImageView.alpha = 0.0
        }, completion: nil)
    }
}

// MARK: - Emitter
extension PokemonDetailImageView {

    private func createEmitter() {
        let size = self.bounds
        let doubleSize = CGSize(width: size.width * 2, height: size.height * 2)

        //Prepare the view itself in double size so that the particles do not break
        self.skView = SKView(frame: .init(origin: .init(x: -self.bounds.width / 2, y: -self.bounds.height / 2), size: doubleSize))
        self.skView?.backgroundColor = .clear
        self.addSubview(self.skView!)

        let scene = SKScene(size: self.skView?.bounds.size ?? .zero)
        scene.backgroundColor = .clear
        self.skView?.presentScene(scene)

        if let node = SKEmitterNode(fileNamed: "appearPokemon") {
            node.position = CGPoint(x: scene.frame.width / 2, y: scene.frame.height / 2 - self.bounds.height / 2 + 60)
            scene.addChild(node)
        }
    }

    private func hideEmitter() {
        UIView.animate(withDuration: 0.1, delay: 0.2, options: .curveEaseOut, animations: { [weak self] in
            guard let self = self else { return }
            self.skView?.subviews.forEach { $0.removeFromSuperview() }
            self.skView?.removeFromSuperview()
            self.skView = nil
        }, completion: nil)
    }
}

extension PokemonDetailImageView: CAAnimationDelegate {

    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        self.delegate?.finishedPokemonImageViewShowAnimation()
    }
}

Finally

People are good and bad, some are good at design and some are good at animation. There is no right answer, and it is worthwhile for those who are overwhelmingly strong in one of them and those who can do it all evenly. That's why I hope that this Pokedex for beginners will be a project that can meet various needs, so I implemented animation this time. To do this kind of animation, you need this kind of technology. And There are various implementation methods for one animation. I hope it will be helpful for you. What if, The fun of Pokemon appearing! I'm glad if you are interested in animation from here. I would like to conclude with the hope that it will lead to an increase in the value of products in the world and an improvement in the quality of life of users.

Recommended Posts

I tried to introduce UI animation to Pokedex using Poké API
I tried to draw animation with Blazor + canvas API
I tried using Java8 Stream API
I tried using Elasticsearch API in Java
I tried to introduce CircleCI 2.0 to Rails app
I tried using Realm with Swift UI
I tried to summarize the Stream API
[API] I tried using the zip code search API
I tried to implement a server using Netty
I tried using Gson
I tried using TestNG
I tried using Galasa
I tried using Google Cloud Vision API in Java
I tried to operate SQS using AWS Java SDK
Rails6 I tried to introduce Docker to an existing application
[Java] I tried to implement Yahoo API product search
I tried to build an environment using Docker (beginner)
[Rails] How to connect to an external API using HTTP Client (I tried connecting to Qiita API)
I tried to introduce Bootstrap 4 to the Rails 6 app [for beginners]
I tried to make Venn diagram an easy-to-understand GIF animation
I tried using azure cloud-init
I tried using Hotwire to make Rails 6.1 scaffold a SPA
I tried using Apache Wicket
I tried to link chat with Minecraft server with Discord API
I tried to build the environment little by little using docker
I tried using Java REPL
I tried using Dapr in Java to facilitate microservice development
I tried to verify yum-cron
I was addicted to using Java's Stream API in Scala
[Rails] I tried to implement "Like function" using rails and js
I tried to get started with Swagger using Spring Boot
[Android] [Library] I tried using an animation library called "Before After animation".
[Metal] I tried to figure out the flow until rendering using Metal
I tried to integrate Docker and Maven / Netbean nicely using Jib
I tried connecting to MySQL using JDBC Template with Spring MVC
I tried to build a simple application using Dockder + Rails Scaffold
I tried to make it an arbitrary URL using routing nesting
I tried to display the calendar on the Eclipse console using Java.
I tried using anakia + Jing now
I tried to chew C # (indexer)
I tried using Spring + Mybatis + DbUnit
I tried using JOOQ with Gradle
I tried to summarize iOS 14 support
I tried to interact with Java
I tried to explain the method
I tried using JWT in Java
I tried to summarize Java learning (1)
I tried to understand nil guard
[Android] I tried using Coordinator Layout.
I tried using Pari gp container
I tried using WebAssembly Stadio (2018/4/17 version)
I tried to summarize Java 8 now
I tried to chew C # (polymorphism: polymorphism)
I tried using Java memo LocalDate
I tried to explain Active Hash
I tried using GoogleHttpClient of Java
Rails API mode I tried to implement the keyword multiple search function using arrays and iterative processing.
I introduced WSL2 + Ubuntu to Window10 and tried using GDC, DMD, LDC
A story I was addicted to when testing the API using MockMVC
I tried to make a Web API that connects to DB with Quarkus
I tried to make my own transfer guide using OpenTripPlanner and GTFS