Vueでもっと幸せになりたいあなたへ。VueのUIコンポーネントライブラリVuetifyのススメ

どうもみなさまおはようございます。あるいはこんにちは。あるいはこんばんは。 KAYAC Advent Calendar 2018の12日目の記事を担当します、今年1月に中途入社しましたエンジニアのたがみです。

前職ではサーバーサイドのSEとして業務系のwebアプリを開発したり運用したりしていましたが、今はクライアントワークのフロントのエンジニアとしてwebサイトを作ったり動かしたりぶっ飛んだものにしたりしなかったりしています。

今回は、そんな私がフロントエンドになったばかりの頃に仲良くなった思い入れのある言語、Google App Scriptの話を!・・・・・・と、つい二日前までは、思っていたのですが。

Google先生を訪ねて色々と検索していたところ「あれ・・・なんか・・・Vuetifyについての日本語記事、実は少ないのでは・・・?」と気づいてしまったのです。 (もしかしたら気のせいかもしれないんですけどそこはまあ横に置いといて)

というわけで、急遽話題を変更して「Vuetifyみんな使ってみて!」な話を検索結果の供給バランスを支えるためにしたいと思います。

この記事の対象読者

  • Vueを日頃から使っていてVuetifyをよく知らない方
  • Vueを日頃から使っていてCSSを書くのが正直だるい方
  • Vue使ってないしVuetifyもよくわからないけどとりあえず技術の話に興味がある方
  • いやReactの方がいいに決まってるだろ!と過激派になって怒ったりしない方

Vuetifyってなに?

Vuetify

Vue.js Material Component Framework — Vuetify.js

  • Vueをベースに構築されたUIフレームワークです。Googleが提唱したマテリアルデザインの考えにのっとって構成された直感的で使いやすいコンポーネントを手軽に扱うことができます。

  • ブラウザの対応範囲も現状のモダンブラウザであれば問題なく動きます。最低限動いて欲しいIE11にもちゃんと対応できます。

  • とにかくドキュメントのサンプルが豊富。「こんなことできないかな・・・?」と探してみると何かしらヒントとなるサンプルに巡り会えます。ぱない。

  • ちなみにVuetifyのドキュメントは一応日本語選択できるのですが、Vue公式ドキュメントとは違ってほとんど翻訳されていない状態(2018年12月現在)ので、素直に英語を読むかGoogle先生に助けを求めましょう

Vuetifyの何が良いのか

とりあえず私が実際に使って見て感じてみたことをあげるとこんな感じです。

  • 質のいいコンポーネントがあらかじめ用意されているため、マークアップの手間を省ける

  • ビューを素早く構築できるので、その分ロジックの記述に集中できる

  • いい感じの見た目をしているので、デザインとかないけどいい感じの画面作ってねヨロ!と言われてもビビらない

百聞は一見にしかず、とりあえずVuetifyが提供しているボタンのページをみてみましょう。

f:id:taga-min:20181211142852p:plain

Button Component — Vuetify.js

サンプルを見ればわかるように、<v-btn color="success">Success</v-btn>みたいに最短1行書けばサクッといい感じのボタンが作れちゃったり、ちょっと丸っこいボタンを作ったり、何ならアイコンだけのボタン、というのだってCSS書かずに作ることもできます。

javascriptで処理書くのは得意だけどCSSがちょっと苦手。。。と感じているロジック強い系な人には強い味方になってくれます。

どういったケースに相性がいいのか

  • 管理画面のUIに

  • 業務で扱うwebアプリに

  • ものによっては普段のWebサイト制作にも。。。

実際私が初めてVuetifyを使ったのはLPサイトで表示する情報を裏側から登録したり更新したりする管理画面を作った時でした。 サイトが公開しても公には目に触れられることのない管理画面などの場合、そこまではデザインや時間の工数かけられないしフロントの人がサクッとよしなにお願いします!なパターンがしばしばあったりします。その上、管理画面や業務などで利用するアプリなどは使う人が誤った操作をしないように、パッとみて少し触るだけで正しく操作方法がわかる見た目であることが大事です。

そういった場合に、数々のユーティリティ系webアプリツールを生み出しているGoogle提唱のマテリアルデザインに基づいたUIコンポーネントはとても相性がよいのです。

(下手したらGoogle系のアプリを使い慣れてる人からすると見た目だけで「なんか使いやすそう」と感じてくれる場合があります。見た目の印象大事)

さらに、詳しくは後述しますがVuetifyは画面を構成するコンポーネント以外にも様々な便利な機能を提供してくれています。使い方によっては人様の目に見えるサイトの方でも活かせるものもあります。

・・・まあ色々御託並べましたが、Vue使ってる人は一度使って知っていればどっかで使える!ということです!

使ってみる

そういうわけなので早速導入してみましょう。

インストール

Vue-CLI使いの人

VuetifyはVue-CLI-3と相性がいいよ!とのことでVue-CLI-3での利用をオススメしているようです。

// vue-cliインストール
$ yarn global add @vue/cli
// または
$ npm install @vue/cli -g

// プロジェクト作成
$ vue create app-hoge
$ cd app-hoge

// Vuetify入れ込み
$ vue add vuetify

webpack使いの人

もちろんもともとwebpack + vueで環境構築している場合でもVuetify導入できます。

// package.jsonに追加
$ npm install vuetify --save-dev
// または
$ yarn add vuetify -D
// Vueを読み込む時に一緒にVuetifyも読み込む
import Vue from 'vue';
import Vuetify from 'vuetify';
 
Vue.use(Vuetify);

new Vue({
    el: "#app",
    render: h => h(App)
});

その場合、VuetifyのCSSファイルも別途読み込みが必要です。 ご自身の環境に合わせて適宜持って来ましょう。

// js:css-loder噛ませて持ってくるとき
import 'vuetify/dist/vuetify.min.css'

// css:scssなどのファイルに吐き出したいとき
@import "../../node_modules/vuetify/dist/vuetify.min.css";

また、コンポーネントでアイコンを使いたいという場合にはこちらも別途インストールが必要です。別途lorderを噛ませて読み込むか、CDNで呼び出しましょう。

// webpackの設定でlorderを噛ませて持ってくる場合こんな感じで記述
 module: {
        // 各ファイル形式ごとのビルド設定
        rules: [
            {
                test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
                use: [{
                    loader: 'url-loader?mimetype=image/svg+xml'
                }],
            },
            {
                test: /\.woff(\d+)?(\?v=\d+\.\d+\.\d+)?$/,
                use: [{
                    loader: 'url-loader?mimetype=application/font-woff'
                }],
            },
            {
                test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
                use: [{
                    loader: 'url-loader?mimetype=application/font-woff'
                }],
            },
            {
                test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
                use: [{
                    loader: 'url-loader?mimetype=application/font-woff'
                }],
            },
       // (以下省略)
      ]
}

IE11/Safari9を対応させるときはpolyfillが必要

何もしていないとIE11などで真っ白な画面になっちゃいます(経験談)。babel-polyfill を読み込んであげましょう。

$ npm install babel-polyfill --save
// or
$ yarn add babel-polyfill
// VueやVuetifyを読み込む時に一緒にpolyfillも読み込む
import 'babel-polyfill';
...
Vue.use(Vuetify);

new Vue({
    el: "#app",
    render: h => h(App)
});

思わず使いたいコンポーネント・機能6選

さてさて、ここからは私が個人的にぜひ試してみて欲しいorまだ試せてないけど試したいと思うVuetifyのコンポーネントや機能を紹介したいと思います!

v-app(Pre-defined layout)

f:id:taga-min:20181211143050p:plain

とりあえず管理画面を作る際はこいつをぶち込んでおけば6割くらいは完成したようなもんです。

Application layouts — Vuetify.js

Vuetify側であらかじめ用意されたレイアウトです。このレイアウト、なんかみたことある。。。!という親しみのあるUIを手早く構築できます。記述するだけで勝手にレスポンシブ対応してくれるので思わず涙が溢れますね。

v-dialog

f:id:taga-min:20181211145608g:plain

Dialogs Component — Vuetify.js

私がVuetifyで一番幸せを感じるのはv-dialogを使うときと言っても過言じゃないです。

いわゆるモーダルです。モーダルはウェブサイトでものすごくよく使われるパーツの一つですが、出現アニメーションをいい感じに付けつつ、領域外クリックしたら勝手にモーダル閉じるようにしたい。。。!などとこだわりだすと割と実装が面倒になったりする場合がありますよね。 v-dialogは書くだけでそこらへんの配慮も全部よしなにやってくれます。また思わず涙が溢れますね。

v-alert

f:id:taga-min:20181211143623p:plain

Alert Component — Vuetify.js

ユーザーの処理が無事に完了したか否か、何かしら伝えた方が親切な場合もあります。そんな時のためのコンポーネントもちゃんとあります。 個人的にはクリックで閉じれるv-alertがいじらしくて好きです。

v-formのバリデーション

f:id:taga-min:20181211144254g:plain

Text fields — Vuetify.js

管理画面やフォーム送信系のUIでは必須レベルのバリデーション。必須チェック、文字数チェック、正しいメールアドレスなど、入力レベルのチェックであればVuetifyに備わっているバリデーションで判定できるんです!:rules でバリデーションルールを設定したり、エラーが起こった際のエラー文言の表示も可能です。涙が止まりませんね。

Vuetify側で用意している機能だけでできることはもちろん、ほかのvue系のバリデーションライブラリ(VuelidateVeeValidationなど)とも組み合わせて実装することもできます。

Form Component — Vuetify.js

スクロールアニメーション

f:id:taga-min:20181211145201g:plain

Scrolling — Vuetify.js

あ、今度からVueでスクロールアニメーションさせたいならこいつを使おうと胸が熱くなりました。

$vuetifyに紐づいているgoTo メソッド。イージングの種類や何秒で移動するかなどもオプションで指定することが可能なので、シーンに合わせて調整可能です。jQuery使わず自前実装するとなるとなかなか大変ですが、その苦労をVuetifyが背負ってくれます。健気すぎて涙で視界が完全に霞みますね。

v-parallax

f:id:taga-min:20181211144719g:plain

Parallax Component — Vuetify.js

そんなものまでコンポーネントであるの?!?!と驚いたのがこれ。

単純な背景ずらし演出をしたい場合にサクッと入れられちゃうので、実案件でアクセント的に使いたい場合に効果を発揮してくれそうです。いよいよ涙腺決壊ものです。

まとめ

いかがでしたでしょうか?

ここまで書いといてアレですが、私自身Vuetifyの存在を知ったのが3ヶ月くらい前だったのでまだまだ手足のようには使いこなせていないところもあったりします。そんなひよっこな私でも実案件で使えるくらいには仲良くなれて、ついでになんか幸せな気分になれるVuetifyは、Vue使いなら一度は試してみてほしいライブラリです。

カヤックでは良さげなライブラリや技術を見つけたらとりあえず使ってみたい!遊んでみたい!とハイになっちゃう系エンジニアも募集しています

明日のアドベントカレンダーの担当はdozenさんです。この後の記事もぜひお楽しみに〜

【Unity】Android,iOS端末のバッテリー残量と充電状態の取得

はじめに

はじめまして、Unityエンジニアの泉川です。
この記事はカヤックUnityアドベントカレンダー2018の12日目の記事になります。
今回はUnityで端末のバッテリー残量と充電状態の取得について書いていこうと思います。

f:id:izumikawa-yuichi:20181127182447j:plain

この画面の右上のやつです。

経緯とか

アプリで遊んでいて唐突に画面の上から出てくる「バッテリー残量が低下しています」の通知。
心臓に悪いですね。
避けるためにちょいちょい端末の頭をフリックするのも馬鹿らしい。
そうなった時にどうするかは取りあえず3つあると思います。
1. ステータスバーを常に表示する
2. アプリ内で表示できるようにする
3. 諦める

上記3つのパターンで考えた時のメリットデメリットを考えてみましょう。

方法 メリット デメリット
ステータスバーを常に表示する ついでにいろんな情報が見れる
見慣れている
画面が狭くなる
デザインが浮く可能性がある
アプリ内で表示できるようにする デザイナーがデザインできる
端末依存が少ない
実装が必要
諦める 工数がかからない 問題が解決していない

で、「アプリ内で表示できるようにする」という方法を取る場合ですが、Unity 2018以前でAndroid向けに実装するには、javaのコードをプラグインとしてビルドした上でプロジェクトに入れる必要がありました。
ビルドのためにAndroid Studioの環境を用意したり、コード修正のたびにプラグインの再ビルドが必要になったりと、手間のかかるものでした。
しかし、Unity2018からはjavaファイルを直接Unityに置いておくだけで済むようになり作業がだいぶ減りました。
というわけで今回はUnity2018を使って、アプリ内にバッテリ残量を表示してやろうという流れです。

バージョン

今回はUnity2018.2.14f1を対象に記事を書いています。

やり方

各ファイルの場所

.csは何処でもいいです。
f:id:izumikawa-yuichi:20181127182423p:plain

BatteryOverseer.java

package com.kayac.uac;

import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
import android.app.Activity;
import android.content.IntentFilter;

import com.unity3d.player.UnityPlayer;

public class  BatteryOverseer
{
    /**
     * バッテリーレベルは現在の充電量に当たります
     */
    public static int BatteryLevel(){
        final Intent batteryStatus = GetBatteryStatus();

        return batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
    }

    /**
     * バッテリースケールは最大の充電量に当たります
     */
    public static int BatteryScale(){
        final Intent batteryStatus = GetBatteryStatus();

        return batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
    }

    /**
     * バッテリーステータスは現在の充電ステータスに当たります
     */
    public static int BatteryStatus(){
        final Intent batteryStatus = GetBatteryStatus();

        return batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
    }

    /**
     * バッテリー状態を取得するためのIntentを取得するメソッドです
     */
    private static Intent GetBatteryStatus(){
        // アプリのContextを取得する
        final Activity activity = UnityPlayer.currentActivity;
        final Context context = activity.getApplicationContext();
        // バッテリー状態を取得するためのIntentを取得
        final IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        final Intent batteryStatus = context.registerReceiver(null, ifilter);

        return batteryStatus;
    }
}

ref:
https://developer.android.com/training/monitoring-device-state/battery-monitoring?hl=ja

BatteryOverseer.mm

extern "C"
{
    /**
     * バッテリーレベルは現在の充電量に当たります(0.0~1.0f)
     */
    float BatteryLevelNative() 
    {
        [UIDevice currentDevice].batteryMonitoringEnabled = YES;
        float batteryLevel = [UIDevice currentDevice].batteryLevel;
        
        return batteryLevel;
    }

    /**
     * バッテリーステータスは現在の充電ステータスに当たります
     */
    int BatteryStatusNative() 
    {
        [UIDevice currentDevice].batteryMonitoringEnabled = YES;
        if ([UIDevice currentDevice].batteryState == UIDeviceBatteryStateUnknown) {
            // 不明
            return -1;
        }
        if ([UIDevice currentDevice].batteryState == UIDeviceBatteryStateUnplugged) {
            // 非充電状態
            return 0;
        }
        if ([UIDevice currentDevice].batteryState == UIDeviceBatteryStateCharging) {
            // 充電中状態
            return 1;
        }
        if ([UIDevice currentDevice].batteryState == UIDeviceBatteryStateFull) {
            // フル充電
            return 2;
        }
        return -1;
    }
}

BatteryOverseer.cs

ここはサンプル色が強いです。

using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;

public class BatteryOverseer : MonoBehaviour
{
#if UNITY_ANDROID && !UNITY_EDITOR
    // Javaのオブジェクトを作成
    private AndroidJavaClass _overseer;
#elif UNITY_IOS && !UNITY_EDITOR
    [DllImport("__Internal")]
    private static extern float BatteryLevelNative();
    [DllImport("__Internal")]
    private static extern int BatteryStatusNative();
#endif
    [SerializeField]
    private Image _battery;
    [SerializeField]
    private Image _chargeIcon;

    private Coroutine _timer;

    /// <summary>
    /// バッテリーの状態のenumです。
    /// </summary>
    private enum BatteryStatus
    {
        Unplug = 0,
        Charge = 1,
    }

    /// <summary>
    /// コンポーネントの表示時に表示の更新を始めます
    /// </summary>
    private void OnEnable()
    {
        UpdateBattery();
        _timer = StartCoroutine(CoUpdateTimer());
    }

    /// <summary>
    /// コンポーネントの非表示時に表示の更新を止めます
    /// </summary>
    private void OnDisable()
    {
        if (_timer != null)
        {
            StopCoroutine(_timer);
            _timer = null;
        }
    }

    /// <summary>
    /// 1秒に1回状態を更新するためのコルーチンです
    /// </summary>
    private IEnumerator CoUpdateTimer()
    {
        var wfs = new WaitForSeconds(1f);
        while (true)
        {
            yield return wfs;
            UpdateBattery();
        }
    }

    /// <summary>
    /// バッテリーの状態を更新する処理です
    /// </summary>
    private void UpdateBattery()
    {
        float chargeRate = 0f;
        float batteryLevel = 0;
        int batteryScale = 0;
        BatteryStatus batteryStatus = BatteryStatus.Unplug;
#if UNITY_ANDROID && !UNITY_EDITOR
        // Javaのオブジェクトを作成
        if (_overseer == null)
        {
            _overseer = new AndroidJavaClass("com.kayac.uac.BatteryOverseer");
        }

        batteryLevel = _overseer.CallStatic<int>("BatteryLevel");
        batteryScale = _overseer.CallStatic<int>("BatteryScale");
        var androidBatteryStatus = _overseer.CallStatic<int>("BatteryS[f:id:izumikawa-yuichi:20181127183028p:plain]tatus");
        // AC or USB
        if (androidBatteryStatus == 1 || androidBatteryStatus == 2)
        {
            batteryStatus = BatteryStatus.Charge;
        }
#elif UNITY_IOS && !UNITY_EDITOR
        batteryLevel = BatteryLevelNative();
        batteryScale = 1;
        var iOSBatteryStatus = BatteryStatusNative();
        // Charge or Full
        if (iOSBatteryStatus > 0)
        {
            batteryStatus = BatteryStatus.Charge;
        }
#else
        // 端末上の実行でなければゲージはMAX、非接続状態
        batteryLevel = batteryScale = 1;
        batteryStatus = BatteryStatus.Unplug;
#endif
        chargeRate = (float)batteryLevel / (float)batteryScale;
        _battery.fillAmount = chargeRate;
        _chargeIcon.gameObject.SetActive(batteryStatus == BatteryStatus.Charge);
    }
}

Hierarchy

f:id:izumikawa-yuichi:20181127182426p:plain

用意した画像

  Background Gauge ChargeIcon
背景塗りつぶし f:id:izumikawa-yuichi:20181127182433p:plain f:id:izumikawa-yuichi:20181127182437p:plain f:id:izumikawa-yuichi:20181127182440p:plain
実際の画像 f:id:izumikawa-yuichi:20181127182404p:plain f:id:izumikawa-yuichi:20181127182417p:plain f:id:izumikawa-yuichi:20181127182420p:plain

結果

あとはビルドして実行すれば充電中はChargeIconが表示され、
GaugeのFillAmountは充電状態に対応します。

f:id:izumikawa-yuichi:20181127182443p:plain

おわりに

というわけでバッテリー残量と充電状態の取得についてでした。 Unity2018になり組み込みもだいぶ簡単になったので是非トライしてみてください。 アプリ内に組み込んでいただけるとまず私が喜びます。

明日はクアンによるuGUIのマスクの形状をアニメーションさせる話になります。