【AfterEffects】花びらを舞わせるアニメーションを作る

今回はこれを作ります。画像内に花びらを舞わせるアニメーションです。

まずは、花びらを舞わせたい画像を配置しましょう。

f:id:utr066:20200219121209j:plain

画像を配置したら、次は花びらを舞わせる用のコンポジションを作ります。

f:id:utr066:20200219121341j:plain

ここで作る花びらのコンポジションをさっき用意した画像のあるコンポジションに合成しようという感じです。

f:id:utr066:20200219121619j:plain

このコンポジションでは花びらを舞わせるので、舞わせたい花びらを取り込みます。 AdjustmentLayer(調整レイヤー)を新たに追加してエフェクトを加えていきます。 f:id:utr066:20200219121916j:plain

調整レイヤーを選択したらそのレイヤーにCC.ParticleWorldを適用します。 f:id:utr066:20200219122014j:plain

これが花びらを散らしてくれるやつなんですが、まだよくわからないですね。 Particle→ParticleType→TexturedQuardPolygonを選択します。ほかの選択肢もいろいろ試してみればわかるけど、これはどんな形でParticleを扱いたいかという選択。

f:id:utr066:20200219122253j:plain

次に舞わせたい対象を選択します。 Partile→Texture→TextureLayerから舞わせたいものを選択しましょう。今回はさっき追加した花びらの画像を舞わせたいので、取り込んだ画像を選択します。

f:id:utr066:20200219122305j:plain

この時点で再生するとこんな風になります。

f:id:utr066:20200219123112j:plain

片鱗が見えてきましたね。今のままの形だと「舞う」とは程遠いため、これを舞っているようにいろんな値をいじっていきます。 参考までにいじった値を載せておきます。

- BirthRate・・・0.1
- Longevity・・・5.91

- Procedure
PositionY・・・-0.34
RadiusX・・・1.745

- Physics
Animation・・・Twirl
Velocity・・・0.41
Gravity・・・0.1
Resistance・・・1.5

- Particle
BirthSize・・・0.22
DeathSize・・・0.34
SizeVariation・・・58
MaxOpacity・・・100

こういう値はいろいろいじってみて理想の動きになるか都度チェックしていくといいです。 値を調整したら、画像を配置したコンポジションに今作った花びらのコンポジションを入れましょう。

f:id:utr066:20200219124827j:plain

再生すると、画像内に花びらが舞っているはずです。 花びらの色味がちょっと微妙だったので、花びらの方のコンポジションに調整レイヤーを追加して色味を調整します。

f:id:utr066:20200219125112j:plain

普通にカラーバランスでいいかな。 f:id:utr066:20200219125124j:plain

f:id:utr066:20200219125138j:plain

これで完成です。

【AfterEffects】バチバチなテキストアニメーションを作る

f:id:utr066:20200219111726g:plain

今回はこれを作ります。 saberというプラグインを使うので、持っていない人はこれを機に使ってみるといいんじゃないでしょうか。プラグインを使うだけで簡単にかっこいいアニメーションを作ることができます。インストールは下記から。

flashbackj.com

まずは、バチバチにさせたいテキストを置きましょう。今回は「ART」でいきます! f:id:utr066:20200219112358j:plain

そしたらLayer→AutoTraceと進んで、文字をトレースしてやります。

f:id:utr066:20200219112439j:plain

f:id:utr066:20200219112441j:plain

そうすると、文字をかたどった線みたいなのができるはずです。

f:id:utr066:20200219112817j:plain

これにSaberを当てていきます。EffectsからSaberを選択して追加しましょう。

f:id:utr066:20200219113015j:plain

かっこいいですね。今のままだと、文字をかたどったエフェクトになっていないので、レイヤーマスクにします。 CustomizeCore→CoreType→LayerMaskを選択しましょう。

f:id:utr066:20200219114156j:plain

ちょっと見た目が変わったはずです。

f:id:utr066:20200219114322j:plain

デフォルトで選択されているPresetはSelectだけど、これをArcReactorにします。

f:id:utr066:20200219114434j:plain

するとこんな風になる。

f:id:utr066:20200219114851j:plain

良い感じですね。ちなみに用意されているPresetはほかにもたくさんあるからいろいろ試してみると面白いです。 これだけでも良いですが、若干の色味も欲しいですね。ちょっと黄色っぽいものを追加しますかね。

f:id:utr066:20200219115201j:plain

Effectsからvegaを追加しました。 追加するとちょっとだけ黄色がかった何かが見て取れると思います。 これで完成。

f:id:utr066:20200219111726g:plain

【AfterEffects】水のようなテキストアニメーションを作る

作るもの

今回作るものはこれ。水のようなテキストアニメーションを作っていこうと思う。

f:id:utr066:20200219100307j:plain

まずは、表示させたいテキストを作っていきましょう。テキストレイヤーを新たに追加して、適当にTEXTにしておきます。

f:id:utr066:20200219100343j:plain

次に背景!白っぽいものにしたいので、真っ白のものを追加します。

f:id:utr066:20200219100309j:plain

テキストレイヤーの下に背景を入れてこんな感じです。後は、これにアニメーションをつけていけばいいだけですね。

Adjustment Layerを追加する。普段英語のもの使っているから日本語バージョンのあんまり覚えてないけど、確か調整レイヤーだった気がする。なので、日本語バージョンを使っているなら調整レイヤーを追加しましょう。 f:id:utr066:20200219102345j:plain

調整レイヤーを追加したら、「CC.MrMercury」を追加します。こいつが画像を液体チックにしてくれます。 f:id:utr066:20200219103528j:plain

追加した時点で再生するとこんな感じです。 f:id:utr066:20200219103745j:plain 画像全体に液体のアニメーションがいきわたっていないので、いきわたらせるようにします。

f:id:utr066:20200219104043j:plain

以下の項目なんかの数値をいじるとちょっと良い感じになります。いろいろ数値をいじってみるといいです。

  • RadiusX
  • RaduisY
  • velociy
  • Birthrate

このままでもよいですが、色味が欲しいので水っぽい画像を追加します。

f:id:utr066:20200219104504j:plain

水の画像を下の方のレイヤーにいれます。

f:id:utr066:20200219104507j:plain

白の後ろに青っぽい色のものが出ました。画像使わないでも普通にsolidLaterでいいかもしれないね。

f:id:utr066:20200219104912j:plain

あとは諸々の値を調整して終わりです。

動画で見たい方は動画もあります。

gqlgenチュートリアルを試す

今更な感じもあるけど、改めてgqlgenのチュートリアルをやってみる。ドキュメント通りにやればサクッと試せます。

チュートリアル

https://gqlgen.com/getting-started/ チュートリアル見ればできるので、端折って書く。ここではコメント的なことを書いておく。 チュートリアルで作るschema.graphqlは以下。

// schema.graphql
type Todo {
  id: ID!
  text: String!
  done: Boolean!
  user: User!
}
type User {
  id: ID!
  name: String!
}
input NewTodo {
  text: String!
  userId: String!
}
type Query {
  todos: [Todo!]!
}
type Mutation {
  createTodo(input: NewTodo!): Todo!
}

これを元に以下コマンドでgqlgenを実行する。

$ go run github.com/99designs/gqlgen init

すると、models_gen.goにschema.graphqlで定義したものから以下の構造体が作成される。

// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package gqlgen_todos
type NewTodo struct {
    Text   string `json:"text"`
    UserID string `json:"userId"`
}
type Todo struct {
    ID   string `json:"id"`
    Text string `json:"text"`
    Done bool   `json:"done"`
    User *User  `json:"user"`
}
type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

QueryやMutationは以下のように定義したので、resolver.goを見るとそれらの関数が定義されている。

// scheme.graphql
type Query {
  todos: [Todo!]!
}
type Mutation {
  createTodo(input: NewTodo!): Todo!
}
// resolver.go
func (r *mutationResolver) CreateTodo(ctx context.Context, input NewTodo) (*Todo, error) {
    panic("not implemented")
}
type queryResolver struct{ *Resolver }
func (r *queryResolver) Todos(ctx context.Context) ([]*Todo, error) {
    panic("not implemented")
}

このresolverに作られた関数をいじってやればOK。 チュートリアルを眺めてみると、このtodoのAPIの要件としては、ユーザーのIDが渡された時だけユーザー情報を返したいらしい。 だけど、このschemaから作られるのは以下の関数。

func (r *mutationResolver) CreateTodo(ctx context.Context, input NewTodo) (*Todo, error) {
    panic("not implemented")
}

戻り値の*TodoのTodoはmodels_gen.goにある以下のTodo構造体なので、Userを返さないといけない。

type Todo struct {
    ID   string `json:"id"`
    Text string `json:"text"`
    Done bool   `json:"done"`
    User *User  `json:"user"`
}
// Userはこれ
type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

でもUser情報を持っているUser構造体はユーザーIDが渡された時にだけ返したいっぽいので、schme.graphqlで定義されたTodo構造体ではなく、別のTodo構造体を作る。resolverでは、その新たに作った構造体を使う。 追加するには、gqlgen.ymlにmodelを追加すれば使えるようになるらしいので追加。

models:
  Todo:
    model: github.com/gqlgen-todos.Todo

後はこのパスにTodo構造体を作る。

package gqlgen_todos
type Todo struct {
    ID     string
    Text   string
    Done   bool
    UserID string
}

コマンド実行

$ go run github.com/99designs/gqlgen

models_gen.goには新たに既存のTodo構造体がなくなっています。自分で新しくTodo構造体を作成したためですね。

// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package gqlgen_todos
type NewTodo struct {
    Text   string `json:"text"`
    UserID string `json:"userId"`
}
type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

Resolverを新たな定義によって更新するため、以下コマンドを実行

rm resolver.go
go run github.com/99designs/gqlgen

これを実行すると以下のresolverが作成されます。 最終的にAPIとして返すのは、schema.graphqlで定義したTodoだけど、resolverとして扱うのはさっき新たに作ったTodoです。

// schema.graphql
type Todo {
  id: ID!
  text: String!
  done: Boolean!
  user: User!
}
// todo.go
type Todo struct {
    ID     string
    Text   string
    Done   bool
    UserID string
}

返す時の流れとしてはresolver→schema.graphqlとなるはずなので、どこかで新たに作った構造体を元にschema.graphqlのTodoに合うようにしなければなりません。 新たに作成されたresolverを見てみると以下のようになっています。

package gqlgegn_todos
import (
    "context"
) // THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES.
type Resolver struct{}
func (r *Resolver) Mutation() MutationResolver {
    return &mutationResolver{r}
}
func (r *Resolver) Query() QueryResolver {
    return &queryResolver{r}
}
func (r *Resolver) Todo() TodoResolver {
    return &todoResolver{r}
}
type mutationResolver struct{ *Resolver }
func (r *mutationResolver) CreateTodo(ctx context.Context, input NewTodo) (*Todo, error) {
    panic("not implemented")
}
type queryResolver struct{ *Resolver }
func (r *queryResolver) Todos(ctx context.Context) ([]*Todo, error) {
    panic("not implemented")
}
type todoResolver struct{ *Resolver }
func (r *todoResolver) User(ctx context.Context, obj *Todo) (*User, error) {
    panic("not implemented")
}

注目すべきは、新たに追加されたUser関数です。

func (r *todoResolver) User(ctx context.Context, obj *Todo) (*User, error) {
    panic("not implemented")
}

このUser関数がschema.graphqlで定義したTodo構造体のフィールドUserを担ってくれるようです。自動生成されるモデルじゃなくて自分で作ったものモデル使うのもいいけど、最終時にはschema.graphqlで定義したものに合うように実装しろということですね。 generated.goをみるとinterfaceとして定義されています。

type TodoResolver interface {
    User(ctx context.Context, obj *Todo) (*User, error)
}

おそらくこのUser関数の呼び出しは自動生成されたgenerated.goでやってくれるのかと思いますが、中身を見てもどうなっているのかよくわからん。 で、チュートリアルではこんな感じになっています。

package gqlgen_todos
import (
    context "context"
    "fmt"
    "math/rand"
)
type Resolver struct {
    todos []*Todo
}
func (r *Resolver) Mutation() MutationResolver {
    return &mutationResolver{r}
}
func (r *Resolver) Query() QueryResolver {
    return &queryResolver{r}
}
func (r *Resolver) Todo() TodoResolver {
    return &todoResolver{r}
}
type mutationResolver struct{ *Resolver }
func (r *mutationResolver) CreateTodo(ctx context.Context, input NewTodo) (*Todo, error) {
    todo := &Todo{
        Text:   input.Text,
        ID:     fmt.Sprintf("T%d", rand.Int()),
        UserID: input.UserID,
    }
    r.todos = append(r.todos, todo)
    return todo, nil
}
type queryResolver struct{ *Resolver }
func (r *queryResolver) Todos(ctx context.Context) ([]*Todo, error) {
    return r.todos, nil
}
type todoResolver struct{ *Resolver }
func (r *todoResolver) User(ctx context.Context, obj *Todo) (*User, error) {
    return &User{ID: obj.UserID, Name: "user " + obj.UserID}, nil
}

これでgo run server/server.goしてplaygroundでクエリを打ってみると結果が得られます。 自動生成されたファイルが結構色々やっちゃってくれちゃうから何をどうやっているのかは中身の実装を詳しく見ないとわからないね。

webpackで本番ビルド時にfirebaseの環境変数を設定する

f:id:utr066:20190729161125j:plain

reactのアプリケーションを確認していたら、webpackの環境変数設定でおかしなことになっていたからメモ。

このアプリケーションはfirebaseで運用しているんだけど、firebaseで設定した環境変数をうまく取得できていなかった。やりたいこととしてはfirebase(本番環境)用にbuildする時にはfirebaseで設定した環境変数を参照してbuildできること。

続きを読む

vueのサンプルテストを書いてみる

vueとgoでポートフォリオ作ったんだけど、vueのテストってどうやって書くんだろう・・・ということでちょっと見てみる。

vue-test-utilsを使ってみる

以下のvue公式のサンプルを試してみよう。

vue-test-utils.vuejs.org

vue-test-utils は Vue コンポーネントを隔離してマウントし、必要な入力(プロパティ、注入、そしてユーザイベント)をモックし、そして出力(描画結果、カスタムイベントの発行)を検証することでテストします。

なるほどどうやらモックを作ってそのモックを通してテストするようだ。

マウントされたコンポーネントは Wrapper の内部に返されます。これは、基の Vue コンポーネントインスタンスを操作、トラバース、クエリ処理するための多くの便利なメソッドを公開しています。

Wrapperってやつがコンポーネントをなんかうまくやってくれるらしい。

vue-test-utils.vuejs.org

なんかWrapperっていうのを使うとコンポーネントをラップしてくれるのかな。で、テストではそのラップされたものを使えと。コード見る感じそんな気がする 。

import { mount } from '@vue/test-utils'
import Counter from './counter'

// コンポーネントがマウントされ、ラッパが作成されます。
const wrapper = mount(Counter)

test-utilsのmount関数を使ってコンポーネントをラップ。そしてこのラップされえたwrapperをいじっていくんやな。

import { mount } from '@vue/test-utils'
import Counter from './counter'

describe('Counter', () => {
  // コンポーネントがマウントされ、ラッパが作成されます。
  const wrapper = mount(Counter)
  console.log(wrapper.html())
  // => <div><span class="count">0</span> <button>Increment</button></div>
  
})

mountで作成されたラッパーはラッパーとしての関数を持っているから例えば、.html()なんかすると、そのコンポーネントが持っているhtmlを返してくれる。

記事通りに進めてこんな感じに書くと、テストは通るね。

import { mount } from '@vue/test-utils'
import Counter from './counter'

describe('Counter', () => {
  // コンポーネントがマウントされ、ラッパが作成されます。
  const wrapper = mount(Counter)

  it('renders the correct markup', () => {
    expect(wrapper.html()).toContain('<span class="count">0</span>')
  })

  // 要素の存在を確認することも簡単です
  it('has a button', () => {
    expect(wrapper.contains('button')).toBe(true)
  })
})
$ npm test

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.527s

このexpectとかtoBeなんていうメソッドはJestに備わっているものっぽい。

jestjs.io

テストするときにはこのJestのメソッドとvue-test-utilsのメソッドを組み合わせてやっていく感じか。なんかテストコード見てるとRspec感がなくもない。

jestは拡張子でテストファイル判断。

デフォルトでは、Jest はプロジェクト全体で .spec.js または .test.js 拡張子を持つすべてのファイルを再帰的に取得します。これがあなたのニーズに合わない場合は、package.json ファイルの config セクションでtestRegex を変更することが可能です。

ユーザのインタラクションをシミュレーションする

このサンプルのコードにはボタンをクリックするとdataとして持っている数値をincrementするっていう実装があるんだけど、こんなのもテストできるらしい。

import { mount } from '@vue/test-utils'
import Counter from './counter'

describe('Counter', () => {
  // コンポーネントがマウントされ、ラッパが作成されます。
  const wrapper = mount(Counter)

  it('button click should increment the count', () => {
    expect(wrapper.vm.count).toBe(0)
    const button = wrapper.find('button')
    button.trigger('click')
    expect(wrapper.vm.count).toBe(1)
  })
})

wrapper.vm.countでvueインスタンスのdataであるcountにアクセスしてるのね。インクリメントするために、ボタンのラッパである .trigger() を呼び出すことでクリックしている。

gomailを使ってローカルではmailhog・本番(Heroku)ではsendgridでメール送信する

メール送信機能をgoのアプリケーションにつけようと思ったけど、どうやってやろうかなー、と。で、ローカルではdockerでメールサーバー用意して、本番ではsendgridで行うようにした。

メール送信のパッケージにはgomailというものを使ってやるかな。

続きを読む

elmでハローワールド

elmでハローワールドしよ。

elmインストール

下記のページから自分のパソコンにあったバイナリを入れる。

guide.elm-lang.org

elmプロジェクト作成

$ elm init

これでelmのプロジェクトが作成される。

.
├── elm.json
└── src

ローカルサーバー起動

$ elm reactor

これでローカルのサーバーが立ち上がる。localhost:8000にアクセスするとどんなもんか見れる。

f:id:utr066:20190629122104p:plain

パッケージインストール

パッケージのインストールは以下のコマンドで行える。パッケージはelm.jsonに記述されていくよ。

$ elm install elm/http
$ elm install elm/json

これ依存関係あった場合、知らせてくれるのね。親切。

I found it in your elm.json file, but in the "indirect" dependencies.
Should I move it into "direct" dependencies for more general use? [Y/n]: Y

HelloWorld表示

src配下にファイルを作ってハローワールドする。

import Html exposing (..)
// Htmlパッケージの全ての関数をimport

main =
    text "Hello world!"

これで、elm reactorして画面を確認。

f:id:utr066:20190629123755p:plain

画面上に表示されているのって、ディレクトリなんだね。で、そのディレクトリに今回作成したファイル名(画像でいえばhello_world.elm)があるからそれをクリックするとHello world!と表示された。

f:id:utr066:20190629123810p:plain

go・gin・gormとdockerで作ったAPIをHerokuで動かす

f:id:utr066:20190614163626p:plain

個人的にAPIを作ったけど、やっぱり作ったらどこかにデプロイしたい。

当初は、 GKE + CloudSQLの構成でやろうと思ったけど、結構料金かかるみたいだから断念。Kubernetes試してみたかったんだけどな。そもそも、CloudSQLが割と料金かかる。

RDBは無料で使えるようなものはほとんどないっぽい・・悲しい・・・

ということで、APIはHerokuに置いてしまうことにした。開発環境はDockerで作ったから、HerokuでもDocker運用する。

続きを読む