20200116のvue.jsに関する記事は8件です。

Vue.jsの時間入力にvue2-timepickerを使ってみた

Vue.jsの時間入力にvue2-timepickerを使ってみた

下記に全て書いてある(英語)
https://www.npmjs.com/package/vue2-timepicker

インストールする

$ yarn add vue2-timepicker
or 
$ npm install vue2-timepicker --save

利用例

<template>
  <div>
    <!-- 文字列"15:00:00"などをv-modelでバインドできる --!>
    <vue-timepicker 
      :format="format"
      :minute-interval="minInterval"
      v-model="stringTime"
    />
    <!-- オブジェクトのパターン --!>
    <vue-timepicker 
      :format="format"
      :minute-interval="minInterval"
      v-model="objectTime"
    />
  </div>
</template>
<script>
import VueTimepicker from 'vue2-timepicker'
import 'vue2-timepicker/dist/VueTimepicker.css'

const vm = new Vue({
   // 色々省略
   data: {
     format: 'HH:mm', // 形式 AMなどの指定もできる
     minInterval: 5, // 分の間隔 これは5分ごとの設定
     stringTime: "15:00",
     objectTime: {
       HH: '15',
       mm: '00'
     }
   },
   components: {
     VueTimepicker,
   }
})

export default vm
</script>

注意: 文字列形式の場合のNULL

v-modelなどでバインドさせる値をstring形式("15:00"など)にした場合
DB側からNULLなどを貰うと正しくNULLで認識されるが、そこから入力すると

想定していた文字列形式"15:00"ではなく
オブジェクトの{HH: "15", mm: "00"}になるので注意

対策1: 初期値NULLを文字列にする

初期値のNULLを空文字列 ""にする

対策2: オブジェクトを文字列に変換する

フォーマットが指定されているので文字列に戻しやすい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue-chartjsで棒グラフの左に追加した時にアニメーションが変にならないようにする

vue-chartjsChart.jsVue.jsで使用するためのラッパーです。ちょっと使い方に工夫が必要だったところを見つけたので書いておきます。

工夫が必要だったのは、棒グラフの左にデータを追加した時のアニメーションです。

まずは、きれいに動くようになったアニメーションを見てください。左にデータを5個追加して、その後追加したデータを消しています。
chartjs_animation_after.gif

次に、修正前の、アニメーションがちょっと変な方をみてください。なんか、、よくわからないけど、変ですよね。
chartjs_animation_before.gif

vue-chartjsでグラフを更新したい場合、vue-chartjsのグラフコンポーネントに渡したchartDataを変更すれば自動的に書き換わります。
ですが、アニメーションさせる時に基準にするのがデータの配列のインデックスのようで(下に追記あり)、データの配列の追加や削除をする場合にはアニメーションが変になるらしいです。
配列の一番後ろ(グラフ的には右)に追加や削除をするような場合は問題にならないのですが、配列の先頭(グラフ的には左)に追加する場合は、こうなってしまいます。

解決策として、chartDataを再代入するのではなく、chartDataの要素を変更するようにしました。

つまり、

this.chartData = newChartData;

↑こんな感じに再代入する部分を、↓こんなふうに変えます。

this.chartData.labels.unshift('added');
this.chartData.datasets[0].data.unshift(Math.floor(Math.random() * 100));
this.$refs.chart.$data._chart.update();

chartDataの要素を変更するだけだと、vue-chartjs的にはchartDataの変更とみなされないので、Chart.jsの更新を明示的に呼んでいます。

サンプルコードは以下をみてください。

See the Pen vue-chartjs barchart "add left" animation by mizukoshi akiya (@akiyah) on CodePen.

追記

『アニメーションさせる時に基準にするのがデータの配列のインデックスのようで』と書きましたが、少し間違えているようでした。データの配列のインデックスのみを基準にするのであれば、配列の先頭に追加した場合、どうやっても前の配列と比べて対応付けができないですからね。
『chartDataに新しいデータを再代入した場合』に限る話だと思います。chartDataの配列オブジェクトを維持したまま追加や削除をすると、前後での配列の対応付けができていて、アニメーションできるのだと思います。

ちょっとおまけですが、vue-chartjsではなくChart.jsで左に追加するサンプルもおいておきます。

See the Pen qBEMzMZ by mizukoshi akiya (@akiyah) on CodePen.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.jsでvue-awesome-swiperの使い方と注意

swiperをずっと使ってたんですが、Nuxtで使う時に困ったところがあったので、
実装方法と一緒に、ハマりポイントについても書いていきます。
TypeScriptで書いてます。

前提条件

Nuxt.js v2.11.0
pug
TypeScript

で、説明していきます。

準備

yarn add vue-awesome-swiper

pulguinsの設定

vue-awesome-swiper.tsを追加

plugins/vue-awesome-swiper.ts
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'

Vue.use(VueAwesomeSwiper)

ここで、TypeScriptで書いている場合、多分import VueAwesomeSwiper from 'vue-awesome-swiper'でエラーが出ます。
なので、@types/vue-awesome-swiper.d.tsというファイルを作って。
次のように書いたファイルを設置しておいてください。これで消えます。

@types/vue-awesome-swiper.d.ts
declare module 'vue-awesome-swiper'

nuxt.config.tsにpuluginsの設定を追記します。

nuxt.config.ts
  plugins: [
    { src: '~plugins/vue-awesome-swiper', ssr: false },
  ],

ここまでで準備完了です。

実装

components

まず、swiperだけのcomponentsを作ります。
例として、親componentsから画像のパスの配列を渡してsliderをつくようなコードで説明します。

components/swiper.vue
<template lang="pug">
  div
    swiper(:options="swiperOption" ref="swiper")
      swiper-slide(
        v-if='Array.isArray(images)'
        v-for='(image,key,index) in images'
        :key='index'
        )
        img(:src='image')
      .swiper-button-prev(ref='swiperButtonPrev',slot='button-prev')
        img(src='~/assets/images/arrow_prev.png')
      .swiper-button-next(ref='swiperButtonNext',slot='button-next')
        img(src='~/assets/images/arrow_next.png')
</template>

<script lang="ts">
import { Component, Vue, Prop, Provide } from 'nuxt-property-decorator'
import 'swiper/dist/css/swiper.css'

@Component({})
export default class SliderCommon extends Vue {
  $refs!: {
    swiper: any
    swiperButtonPrev: any
    swiperButtonNext: any
  }
  @Prop()
  images: any

  @Provide() swiperOption: any = {
    init: false
  }
  mounted() {
    this.$refs.swiper.swiper.params.spaceBetween = 10
    this.$refs.swiper.swiper.params.autoHeight = true
    this.$refs.swiper.swiper.params.navigation.nextEl = this.$refs.swiperButtonNext
    this.$refs.swiper.swiper.params.navigation.prevEl = this.$refs.swiperButtonPrev
    this.$refs.swiper.swiper.init()
  }
}
</script>

<style scoped lang="scss">
.swiper-button-next,
.swiper-button-prev {
  img {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;
  }
}
</style>

この書き方で注意して欲しい点ですが、

  • swiperOptionで、init:false にしている。
  • swiper-button-prevとnext にそれぞれrefを設定している。
  • mountedでSwiperのオプションを設定している。

です

vue-awesome-swiperの公式のドキュメントの書き方は

<script>
  swiperOption: any = {
    spaceBetween:10,
    autoHeight:true,
    navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev'
    }
  }
</script>

こんな感じで、data()で記述してtemplateで使っています。
静的サイトで、swiperを一つだけ、とか数が固定されているならそれぞれクラス変えるなどで対応できるんですが、
動的なサイトや、今後再利用する場合を考えると、
init:falseで勝手に初期化されないようにして、
mountedでオプションを設定すると、refの値が取れるのでcomponents内のprev、nextをrefで設定が可能になって便利じゃないかと思ってこのようにしています。

親componentからは、特別なこともなく呼び出します。

index.vue
<template lang="pug">
div
    Swiper(:images='images')
</template>

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import Swiper from '~/components/swiper.vue'

@Component({
  components: {
    Swiper
  }
})
export default class Index extends Vue {
  get images() {
    return ['hoge.png','fuga.png']
  }
}
</script>

<style scoped lang="scss"></style>

おわり

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者】Vue.jsでタブメニューを作ってみた

最近、Vue.jsを勉強しています。
練習として、タブメニューをつくってみました。

Vue.jsの導入

以下のコードでVue.jsを導入します。
HTMLファイルのbody内に記述します。
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

JavaScriptを記述する

HTMLファイルのbody内のscriptタグ内に、以下のコードを記述します。

new Vue({
  el: '#app',
  data: {
    active: 1
  },
  methods: {
    change: function(num) {
      this.active = num;
    }
  }
})

変数

active という変数を用意しています。
どのタブが選択されているのかを代入します。

関数

change という関数を用意しています。
他のタブが選択されたときに用います。
active に、選択されたタブの番号を代入します。

HTMLを記述する

HTMLファイルのbody内に、以下のコードを記述します。

<div id="app">
  <ul id="tabMenu">
    <li v-on:click="change(1)" v-bind:class="{'active': active === 1}">タブ1</li>
    <li v-on:click="change(2)" v-bind:class="{'active': active === 2}">タブ2</li>
    <li v-on:click="change(3)" v-bind:class="{'active': active === 3}">タブ3</li>
  </ul>
  <p v-if="active === 1">コンテンツ1</p>
  <p v-else-if="active === 2">コンテンツ2</p>
  <p v-else-if="active === 3">コンテンツ3</p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>

v-on , v-bind , v-if , v-else-if を使いました。

v-on

v-on:click="change(2)" は、その要素がクリックされたとき、
関数 change に値 2 を渡して処理を行います。

v-bind

v-bind:class="{'active': active === 2}" は、
active === 2 が真のとき、その要素で class="active" を有効にします。

v-if, v-else-if

v-else-if="active === 2" は、
そのすぐ上に書いてある v-if="active === 1" の続きになります。
active === 1 が偽で、active === 2 が真のとき、実行します。

CSSを記述する

見た目を整えます。

#tabMenu {
  padding: 0;
  display: flex;
  list-style-type: none;
}

#tabMenu li {
  width: auto;
  padding: 10px 20px;
  color: black;
  border: 1px solid black;
  background-color: white;
  cursor: pointer;
}

#tabMenu li.active {
  color: white;
  background-color: black;
  transition: .3s;
}

ぶっちゃけ、CSSが一番難しかったです。笑
flexbox は便利なので、使いこなせるようになりたいです。

終わりに

Vue.jsのアウトプットの練習として、タブメニューを作ってみました。
割と簡単に作ることができたので、Vueってスゴいなと思いました(小並感)
引き続き、Vueの勉強をがんばります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像をアップロード前に圧縮する流行りの方法【Vue.js x Firebase x 令和】

こんにちは。年末年始はずっとFirebaseを触っていました @ykhirao です。たぶん3年くらい遅れて流行に乗り始めています。Typescriptお正月にはじめました。

今日は、仕事がお休みなのでスマホぽちぽちしていたらすごく参考になる記事を見かけましたのですが、こちらの記事 Firebaseで作る!リアルタイム画像変換CDN【Firebase Hosting + Cloud Functions】 - AppBrew Tech Blog で書かれている アップロードされた画像をそのまま表示する時代は平成とともに終わりを告げたわけですが[※要出典] という文言を見てくすっと笑ってしまい、少し前に 君はまだ平成のアーキテクチャを使ってるのか?僕はFirebaseと令和の時代に行くぞ。 という記事がたくさんの方に読まれていたこととか、Twitterの擬人化された #令和ちゃん を思い出したりして、エンジニアのみなさんは令和ちゃんもFirebaseも大好きっぽいので私も記事を書こうかなーーと思って書き始めました。ネタなんだけど誰かの役にたつ記事を書きたい次第です。

さて前置きが長くなりましたが、本文は、画像をアップロードする前にゴリッと処理する方法を書いて行きます。

画像をアップロード前に圧縮する流行りの方法

今回紹介すること、しないこと

# 画像アップロードについての手法

1. 画像処理をしない(平成)

2. 画像処理をする(令和)
  - 2.1 サーバーで処理する (https://tech.appbrew.io/entry/2020/01/15/120000 で紹介されている)
    - Firebase Extension(まだβっぽいですが公式の拡張があるっぽい!)
    - CloudFunctionsを自分でNode.JSごりごり書く
  - 2.2 クライアント側で処理する( <---- 今日の記事はここ!!!)

本文いらないからスクショとかGistみたいひと

以下のスクショのようになるGistはこちら
https://gist.github.com/ykhirao/1d36aeca9abb02709cc9dd3d676040ef

スクリーンショット 2020-01-16 13.32.29.png

スクリーンショット 2020-01-16 14.17.23.png

(最大 100x100 px にしているので2kbくらい)

処理して簡単に解説する

Canvasでごりっと処理するとか、ライブラリに頼るとか方法はあると思いますが今回は compressorjs を紹介したいっす。

component.js
import Compressor from 'compressorjs'

OR

index.html
<script src="https://cdn.jsdelivr.net/gh/fengyuanchen/compressorjs/dist/compressor.min.js"></script>

CDNはなかなか見つからなかったのですがぐぐったら このブログ で見つかった。まさとらんさんありがとうです。

画像は input タグで選択してもらって

<input
  type="file"
  id="file"
  multiple
  accept="image/*"
  @change="handleFiles"
/>

multiple で複数選択可にして accept で画像以外を一応弾いています。それで @change イベントのフックで画像を圧縮しています。

handleFiles(e: Event) {
  const images = this.images
  const files: FileList | null = (<HTMLInputElement>e.target).files
  if (files === null) return
  ;[...files].forEach((file: File) => {
    // 複数ファイルを受け入れているので、この中で処理している
  })
}

複数処理やfileが取得できなかった場合の処理は上のような感じで、Typescript的にエラーが出ていないのでこれでOKかなと思ってます(認識あってますか?)

const payload: Compressor.Options = {
  quality: 0.8,
  maxWidth: 100,
  maxHeight: 100,
  mimeType: 'image/jpeg',
  success(blob: Blob): void {
    // ここに成功時の処理を書く。次。
  },
  error(err: Error): void {
    console.log(err.message)
  }
}
new Compressor(file, payload)

実際の処理はこんな感じで横幅・縦幅の最大とか圧縮率とかたくさんの オプション があります。また成功時と失敗時は successerror のコールバックに書くみたいです。

success(blob: Blob): void {
},

サクセスのコールバックは Blob を受け取るのでFirebaseではこの段階でファイルのアップロードができます!!!ドキュメントはこちら。 Blobからアップロードできるとかすごくいいですね。(試してないけどたぶんそちらでUploadできる)

今回はアップロードする前に読み込みを確認したかったので、DataUrlという形式に変換しています。

success(blob: Blob): void {
  var reader = new FileReader()
  reader.onloadend = () => {
    const result = <string | ArrayBuffer | null>reader.result
    if (result instanceof ArrayBuffer || result === null) return
    images.push(result)
  }
  reader.readAsDataURL(blob)
},

images.push(result) する段階でDataUrlという形式の data:image/jpeg;base64,XXXXXXXXみたいな文字列になっています。ドキュメントの文字列からアップロードするを見ると

var message = 'data:text/plain;base64,5b6p5Y+344GX44G+44GX44Gf77yB44GK44KB44Gn44Go44GG77yB';
ref.putString(message, 'data_url').then(function(snapshot) {
  console.log('Uploaded a data_url string!');
});

これでアップロードできそうですね。

ドキュメントを読むと data:text/plain;base64, という文字列を削除したら ref.putString(message, 'base64url')でアップロードできそうです。たぶん。

サンプルファイルは <script lang="ts"> にしているのでVSCodeでひらいて、 CMD + Click とかで定義元とか飛んで行って自分で触ってみてください!!

スクリーンショット 2020-01-16 14.09.43.png

終わりに

ブラウザのAPIを叩くときにTypeScriptすごく開発体験良かったです。MDNを見に行かなくても何が返ってくるかわかるので、null制御とかそういうのがやりやすかったです。コード書いてて楽しかった。

なにか間違い見つけたら編集リクエストください!!!

最後まで読んでいただきありがとうございます。!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

勉強のためにスマホでweb開発したら意外と快適だった

はじめに

web開発未経験な画像処理のアルゴリズムエンジニアです。勉強のためQiitaの記事を眺めていますが、web関係の開発が今非常に進化が激しく、面白い技術がいろいろありそうなので勉強かねて一本アプリでも作ろうかと、始めました。web開発は全くの素人です。
ただ、会社と自宅の往復で1日が終わってしまい、安定してまとまった時間がとれるのは通勤時間の往復のみ、さて困りました。そんなこと考えていたら、クラウド開発環境が最近充実してきた、と言う記事を読み、これ使えばスマホで開発できるのでは、と淡い期待を胸に調査と開発を試して行きました。ある程度の感触がつかめたので、ここで調べた内容をまとめたいと思います。エンジニアならスマホゲームなんてやってないで、スマホプログラミング楽しみましょう!!

使用した環境

  • スマホ(simフリー)
  • たまにpc -- 操作性の関係上、一気に修正したいときはpcでないとさすがに不便。 この記事もスマホで書こうと思います。できるだけ…

作ったもの

打合せの日程を調整するサイトを作りました。

開発に使用したモジュール、サービスなど

  • vuejs
  • vuex
  • vuerouter
  • vuetify
  • firebase hosting
  • firebase firestore
  • vuefire
  • webpack
  • webpack-dev-server
  • babel

と、一通りのweb開発体験ができた気がします。やっぱり記事読むだけと実際に手動かすのとでは理解度合いが違います。開発内容については機会があったら別記事にまとめたいと思います。ここでは開発環境についてまとめて行きます。

開発環境変遷

初期検討 ~ codepenに触れてみる

最初はcodepenでサンプルコードをコピペしてお試ししました。動作を理解するならこれで十分です。スマホでも問題なく動作します。ただ、規模がだんだん大きくなると、対応困難になり、別の環境を探すことにしました。

開発着手 ~ glitchとの出会い

glitchクラウド開発環境に出会い使い始めました。
https://glitch.com/
スマホでの操作性は若干難ありですが、十分対応可能です。また、firebaseを使用する事を想定していたので、firebaseのデプロイ機能があるのも決めてになりました。他にも以下の機能がありスマホ上でも一通りの作業ができました。

  • nodejs実行結果プレビュー機能。修正結果がリアルタイム反映されて確認できます。
  • firebaseデプロイ
  • nodeモジュールのインストール
  • コンソールによるコマンド実行
  • githubコミット機能
  • プライベートプロジェクト作成可能
  • バージョン管理機能があり過去コードに簡単に戻せる
  • 何より、無料と言うのが素晴らしいです。
  • 他の人が作った環境からのforkして開発可能。
  • disk要領は200MByteまで。(多いように感じるが、module増やしてくと、これがなかなかシビア) 制約もありそこをどう対処したかを重点において以下記載します。

vue環境構築 ~ サンプル探し

glitch vue で検索すると沢山のサンプル環境が出てきます。非常にシンプルに実装されていた、以下をforkして作り始めました。
https://glitch.com/~vuejs-template

glitchではremixボタンでforkできます。
他のものはwebpackなど使っていて、仕組みがわかっていない素人には取っつきづらく。。。
このサンプルは、getlibsと言う仕組みでmoduleを引っ張ってくる作りになっているようです。
https://www.npmjs.com/package/getlibs
ロードは遅いですが限られたクラウド環境の少ないdisk要領を圧迫せず、コンパイルの時間もかからないので、クラウド開発とは非常に相性がいいと思います。またここからwebpackに乗り換えするのも自分で調べながら変更加えて行ったので、理解を深めることかも出来ました。

コーディング ~ スマホとの戦い

サンプルをremixして修正していきます。もちろんスマホで。ここからがスマホでの操作性の戦いになります。

キー操作

予測変換があるので、変数などのキーワード入力はストレス少なく入力できます。記号の入力は難あり。使っているキーボードアプリにもよるとおもいますが。私はsharp端末なのでs-shoin使いました。一部記号がqwertyからフリック入力できるので、gboardよりは使いやすかったです。ただ、もっとプログラマに最適化されたキーボードがほしいところです。

コピペ

非常に重要です。長文をタイプするのは困難なのでコピペ多用します。外部サイトからのコピペは簡単です。普通にやるだけ。
問題はglitchのエディタ、選択機能がうまく動かない。。。三回タップで一行選択できるので、一行はそれでオッケー!
もっといっぱい一気にコピーしたいときは、仕方がないので、一回githubのコミットしてからgithub上のページからコピーする方法で対処しました。

削除

削除もおなじで、選択できないので一気に削除が非常に大変。間違えて長文張り付けちゃったときは泣きそうになります。何か良い方法ごあるといいのですが今のところ地道に消すしか方法見つかってません。派手に間違えたときは過去のリビビョンにロールバックするのが良いと思います。左下のrewindボタンで行けます。

デバッグ

ここがやり方が分からず苦労しました。まだ正解が見えておらず、スマホ上では環境が整っていない感じがします。何が原因で落ちているのかわからないため、コメントアウトして問題箇所を探すと言う古典的なデバッグ方法で頑張りました。

webpack導入 ~ disk要領との戦い

ある程度形になったところで、ページのロードに時間がかかっていたのでgetlibsが原因かと考えwebpackへの移行を始めました。
うまくいかず試行錯誤繰り返しました。webpackの解説は他サイトに良いものが沢山あるので、ここではglitchで導入する際のはまりどころだけ記載します。

module沢山入れるとdisk要領が溢れる

200MByteの壁に悩みました。自分のやり方が悪いだけかもしれませんがfirebase環境のmoduleが大きく入りきらなくなったため一部(firebase,vuefire)をcdnからのロードとしました。クラウドでの開発はcdnの活用が重要と思われます。

webpackとcdnの併用がうまくいかない

webpackでcdnを使う場合、webpack.config.jsにexternals設定が必要とのことです。見よう見まねで以下の記載を追加しました。

webpack.config.js
  externals: { 
    'firebase/app':'firebase/app',
    vuefire: 'vuefire',
  },

index.htmlにcdn読み込む記載を追加。

    <script src="https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/6.2.0/firebase-firestore.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vuefire@2.2.1/dist/vuefire.min.js"></script>

以下のように利用します。importはいらない様子。エディタ上はエラー表示出ますが特に問題なく動きます。マニュアルに記載が見つけられず、importされるモジュール名はリンク先ソースのexportから判断しましたが。他に良い方法ないのだろうか。。

firebase.js
import Vue from "vue";

Vue.use(Vuefire);

const firebaseApp = firebase.initializeApp({
  // ここにfirebaseのapi情報をコピペします。
 });

export const db = firebaseApp.firestore();

また、使用するモジュールにはESのバージョンによって違いがあるようなので注意が必要てす。最初ES6用使っていて動かずにかなり時間費やしました。
具体的には以下vuefireですが、普通に見るとvuefire.esm.min.jsしか出てきません。
https://www.jsdelivr.com/package/npm/vuefire
しかしこれはes6形式のファイルです。(esmがそれを意味)distのフォルダをのぞくとしっかりとvue.min.jsがありこれを使うとうまく動きました。
なんとなくbabel使用しなければes6のファイルでも動くのではないかと思いますが、試せていません。

感想

javascript最初はカッコだらけなのがよくわからず、取っつきずらさを感じましたが使ってみると、このカッコが非常に便利。文法もcに近いのでc言語使いにはpythonより簡単に理解できそうです。
vueなどオープンソースライブラリが非常に強力なのが魅力ですが、ライブラリによってはまとまったマニュアルが少なかったり、バージョンによって微妙に挙動が変わってコピペで動かないなどはちょっと初学者泣かせかと思います。vueはマニュアル豊富だったのでその点はあまりこまらず。バージョン間の差分ははまりましたが。。

まとめ

glitch使ってスマホでweb開発、意外と快適でした。
文字入力はあと一歩。
実行環境は問題無し。
バージョン管理も問題なし。
デバッグ環境はまだまだ。

以上、駄文失礼しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue.js] ケバブケースとかキャメルケースとかパスカルケースとか

Vueでのケースの書き方って場所によって何がいいか若干悩みますよね。(私だけ?)
なので、まとめてみました。

そもそも○○○ケースって?(復習)

一言で言うと「クラス名や変数名等の名前の付け方の総称」
覚えておくべきケースをざっと説明すると。。

ケバブケース

文字と文字の区切りを-で表現するやり方。「チェインケース」とも言う。
ケバブのお肉をぶっさしている感じからきているとか。:meat_on_bone:

this-case-is-god

キャメルケース

文字と文字の区切りを大文字で表現するやり方。
よく見るやつです。ラクダのこぶですね。:camel:

thisCaseIsGod

パスカルケース

文字と文字の区切りを大文字で表現+先頭の文字も大文字にする。
それもそのはず、「アッパーキャメルケース」とも呼ばれるのだから。
これもよく見ますね。

ThisCaseIsGod

どこにどのケースを書くべきか?

基本的に下記の方針でOK

  • コンポーネントは「パスカルケース」
  • JavaScriptでは「キャメルケース」
  • HTMLでは「ケバブケース」

記述に迷う主な例を挙げたいと思います。

コンポーネントは「パスカルケース」

<template>
  <MyComponent></MyComponent>
</template>

因みに、componentsに指定する際にパスカルケースであれば、ケバブケースでも記述できるが
ケバブケースで指定するとケバブケースでしか動かない。

<template>
  <MyComponent></MyComponent>     <!-- OK -->
  <my-component></my-component>   <!-- OK -->
  <MyComponent2></MyComponent2>   <!-- NG -->
  <my-component2></my-component2> <!-- OK -->
</template>

<script>
export default {
  components: {
    MyComponent,
    'my-component2': MyComponent2,
  }
}
</script>

props 属性は「ケバブケース」 その他は「キャメルケース」

<template>
  <MyComponent :my-data="hoge"></MyComponent>
</template>

<script>
export default {
  props: ['myData'],
}
</script>

emitは「ケバブケース」

JavaScript内だが文字列として定義しているだけで、
カスタムイベント名として使われるためケバブケース推奨

<script>
export default {
  methods: {
    hogeFunc() {
      this.$emit('my-emit')
    }
  }
}
</script>

最後に

書き終わった後に気づいたのですが、とても分かりやすい記事がありました。
参考にさせていただきます。
https://qiita.com/ngron/items/ab2a17ae483c95a2f15e

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

静岡県の点群データDBをVue+Leafletで表示してみた

今回作ったもの

静岡県点群データマップ
GitHub - shizokaPCDB
スクリーンショット 2020-01-16 1.35.33.png

注意

この記事はLIDARデータをLeafletに表示する記事ではありません。
点群データベースの情報(データの取得地や工事日など)を表示するものです。

はじめに

スクリーンショット 2020-01-16 1.49.59.png
Rockな静岡県庁さんは、自前のウェブサイトShizuoka Point Cloud DB(以下「静岡PCDB」)で工事成果品のLIDARデータをオープンデータとして多数公開しています。興味本位でソースを眺めてみると、外部からでもデータを取得出来そうだなとわかりました(htmlにJavaScriptが直書きだったため)。ただこのサイト、上記画像を見るとわかるとおり①マーカーの数が多くパフォーマンスに影響が出ている事と、②地図領域が変わるたびにその領域に含まれるデータをサーバーから取得しておりさらにパフォーマンスが低下している事、という2つの問題があると思いました。データが外部からでも取得出来るなら改善の余地があるかもと思い、開発に至った次第です。

そんな訳でデータベースへのアクセス・整形とフロントでの表示の二本立てでお送りします。

使用技術

  • Flask
  • Vue.js
  • Vue2-leaflet

データベースへのアクセス・整形

アクセス

静岡PCDBのソースを見ると、サーバーへのアクセス・Leafletでの表示は100行程度で実装されていました。
サーバーからデータを取得する部分を以下に示します。

var data = { request : "MarkerSet",
                Xmax : map.getBounds().getNorthEast().lng,
                Xmin : map.getBounds().getSouthWest().lng ,
                Ymax : map.getBounds().getNorthEast().lat,
                Ymin : map.getBounds().getSouthWest().lat };
    $.ajax("ankenmapsrc",{
        type: "GET",
        data: data,
        success: function(data, dataType){
            var myIcon = L.icon({
                iconUrl: 'http://cdn.leafletjs.com/leaflet-0.5/images/marker-icon@2x.png',
                iconSize: [15,25],
                iconAnchor: [5, 5],
            });
//以下処理が続きます

dataというクエリでankenmapsrcにアクセスすれば、データが得られそうです。
ここでdataはリクエスト名"MarkerSet"と取得する領域を示すXmax,Xmin,Ymax,Yminを持ちます。
領域の4つの変数には、Leafletで表示している領域が格納されます。
この処理はLeafletの表示領域が更新されるたびに呼ばれます。つまりその都度サーバーからデータを取得している訳です。

さてこのリクエストの返り値を見てみましょう。

30XXX01010001:平成30年度韮山反射炉計測業務:138.96214537214:35.03962001009?
28XXX00030007:白糸の滝滝見橋周辺整備事業 その7:138.58870495572:35.312506370532?
28XXX00030008:白糸の滝滝見橋周辺整備事業 その8:138.58881502806:35.312596432406?
28XXX00030009:白糸の滝滝見橋周辺整備事業 その9:138.58892510063:35.312686494178?
29C2001011361:平成29年度[第29-C2001-01号] 伊豆半島の屋外広告物の実態調査業務委託(函南町道_1-2号線):138.93794860595:35.083520492945
...

本来は改行はありませんが、説明のため追加しています。
このデータを見ると、工事ごとの区切り文字は"?"で、データ要素ごとの区切りは":"である事がわかります。

整形

Flaskで静岡PCDBにアクセスし、整形したデータをレスポンスするAPIサーバをつくります。

import urllib.request, urllib.parse
import json
@app.route('/markers')
def getMarkers():
    #全件取得するために、静岡県全域が含まれる緯度経度を整数値で設定
    xMax = 140
    xMin = 137
    yMax = 36
    yMin = 33

    params = {
        'request':'MarkerSet',
        'Xmax':xMax,
        'Xmin':xMin,
        'Ymax':yMax,
        'Ymin':yMin
    }
    p = urllib.parse.urlencode(params)
    url = "https://pointcloud.pref.shizuoka.jp/lasmap/ankenmapsrc?" + p

    #上記で生成したURLパラメータでSIZUOKA POINT CLOUD DBにリクエストし案件一覧文字列を取得
    allAnkenStr = ""
    with urllib.request.urlopen(url) as res:
        allAnkenStr = res.read().decode()

    #returnするjsonを作成
    ankensObj = {
        "ankenList":[]
    }

    ankenList = allAnkenStr.split('?')
    for anken in ankenList:
        ankenInfo = anken.split(':')
        #不適切なデータがあった場合、スキップする
        if len(ankenInfo) != 4:
            continue

        #和暦を西暦に変換
        yy = int(ankenInfo[0][:2])
        #令和
        if yy < 24:
            yyyy = 2018 + yy
        else:
            yyyy = 1988 + yy

        ankenObj = {
            "no":ankenInfo[0],
            "name":ankenInfo[1],
            "lon":ankenInfo[2],
            "lat":ankenInfo[3],
            "year":yyyy
        }
        ankensObj['ankenList'].append(ankenObj)
    return jsonify(ankensObj)

冒頭で説明した問題点②地図領域が変わるたびにその領域に含まれるデータをサーバーから取得しておりさらにパフォーマンスが低下している事については、静岡県の領域を全て含むXmax,Xmin,Ymax,Yminとする事で解決しました。というのも、全件でも1400件程度であり、実際に全件取得してみても遅くはなかったため、最初に全部取得しておけば良いという結論になりました。

取得したankenデータをパースして、工事番号、工事名、経度・緯度、工事年度をjsonとしてreturnする事としました。

フロントでの表示

残る問題点①マーカーの数が多くパフォーマンスに影響が出ている事ですが、動作の早いMapbox GL JSで表示すれば解決すると考えていました。しかしながら実装してみたところ、マーカーの表示はLeafletより遅くなってしまいました。という事で、LeafletのMarkerClusterを採用する事でこの問題を解決しました。

LeafletとVueの環境構築についてはTry #027 – Vue.jsでLeafletとMapbox GL JSの開発環境を構築してみたが詳しいので割愛します。
くわえて、vue2-leaflet-markerclusterをインストールします。

npm install vue2-leaflet-markercluster

VueでAPIサーバに非同期通信

FetchAPIを使います。
App.vueでデータを取得しMapPane.vueへ渡します。

App.vue
<MapPane :ankens="ankens"/>
App.vue
  data() {
    return {
      ankens:[],
    }
  },
  created() {
    let vm = this
    fetch("/markers")
    .then(response => {
        return response.json()
    })
    .then(data => {
        //sortしてからdataを渡しています
        vm.ankens = data.ankenList.sort(function (a, b) {
            if (a.no < b.no) {
                return 1
            }
            if (a.no > b.no) {
                return -1
            }
            return 0 
        }).sort(function (a, b) {
            if (a.year < b.year) {
                return 1
            }
            if (a.year > b.year) {
                return -1
            }
            return 0 
        })
    })
    .catch(error => {
        console.log(error)
        alert("エラーが発生しました。")
    });
  }
MapPane.vue
<template>
    <div class="mapPane">
        <l-map
            :zoom="zoom"
            :center="center"
            :preferCanvas="true"
        >
            <l-control-scale
                position="bottomleft"
                :imperial="false"
                :metric="true"
            ></l-control-scale>

            <l-tile-layer
                :name="tileProvider.name"
                :visible="tileProvider.visible"
                :url="tileProvider.url"
                :attribution="tileProvider.attribution"
            ></l-tile-layer>

            <Vue2LeafletMarkerCluster :options="clusterOptions" >
                <LMarker v-for="anken in ankens" :key="anken.no" :lat-lng="makeLatLng(anken)" @click="onMarkerClick(anken.no)">
                    <LPopup :content="makeMarkerContent(anken)" ></LPopup>
                </LMarker>
            </Vue2LeafletMarkerCluster>
        </l-map>
    </div>
</template>
MapPane.vue
props: {
    ankens:Array
},
MapPane.vue
<style scoped>
    @import "~leaflet.markercluster/dist/MarkerCluster.css";
    @import "~leaflet.markercluster/dist/MarkerCluster.Default.css";

</style>

LMarkerをVue2LeafletMarkerClusterで包めば良きにはからってくれます。
以上により、密接したマーカーはクラスター(束)となり、拡大するまでまとめてひとつの地物として表示されます。結果問題点①も解決しました。

おわりに

今後はデータの一覧表示をソート出来たり検索出来たり、ローディング中のアニメーションを表示したいなと思っています。LIDARデータがアツいらしく調べていると静岡PCDBに辿り着きましたが、思わぬ大脱線となりました。これほど多数の貴重なデータをオープンデータとする静岡県さんはマジでRockであり、最近では兵庫県さんもRockerの仲間入りをしたようです。オープンデータ活用の本流とは若干逸れている気がする今回の案件ですが、少なくとも自己研鑽にはなったかなと思います。これからもオープンデータ界隈にはアンテナを張っておきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む