20200218のvue.jsに関する記事は10件です。

html2canvas で 画像生成

vue.js で html2canvas 決定版

ラブレターみたいなのを作りたい。
ラブレターではphp側で画像を生成して出力している。

参考
https://love-letter.club/
https://wood-roots.com/web/vue-js/2253

特徴

PHP側

○ ユーザーの環境に依存しない

X サーバー重くなる
X 絵文字使えない (強引にライブラリ入れりゃ使えるが・・・ユーザー側の絵文字更新に応じ要変更)

JS側で生成

○ サーバーに負荷をかけない
○ 絵文字使えない

X ユーザーの環境によってバグったりする
X 環境がバラバラなのでデバッグに手間がかかる

ただ、これからの時代は jsで 生成したい。

インストール

α版を指定してインストールする

npm install html2canvas@1.0.0-alpha.12

注意

・CSSで body に フォントを設定しない。
・バージョンは アルファ版を使う。
・でかいモニターで出力すると線が入る ( 画像がにじむ )
これはしょうがないので諦める。

hoge.vue
<style>


    #preview {
        overflow: auto;
        width: 100%;
    }

    #preview_inner {
        background-size: 600px auto;
        position: relative;
        width: 600px;
    }


    #letter_body {
        position: relative;

        border-left: 17px solid #fbbfc8;
        border-right: 17px solid #fbbfc8;

        display: inline-block;
        font-size: 1.5rem;
        color: #444;
        margin-top: -30px;
        padding: 10px 10px 10px 10px !important;
        min-width: 600px;
        text-align: center;

        height:200px;

        display: table-cell; /* IE8から使用可能 */
        vertical-align: middle;
        white-space: pre-wrap;
    }

</style>



<template>
    <div>


        <img v-if="imgData" :src="'/gcp-storage/' + imgData" style="width: 95%;">

        <el-input type="textarea" v-model="message" :rows=5></el-input>
        <el-button type="primary" @click="generate">画像を生成</el-button>


            <div id="preview" style="display: block;">
                <div id="preview_inner">
                    <div id="letter_top"><img src="/img/tegami-top.png"></div>
                    <div id="letter_body">--</div>
                    <div id="letter_bottom"><img src="/img/tegami-bottom.png"></div>
                </div>
            </div>


            <div id="text_body">{{ message }}</div>


            <h2>完成品</h2>

            <div id="result"></div>


    </div>
</template>

<script>

    import html2canvas from 'html2canvas'//古いと、文字のサイズがずれるので、 vue.blade で 読みコンで使う

    export default {

        data() {
            return {
                imgData:'',
                message:'',
            };
        },
        created () {
            this.message = 'つけめんそばざるうどん恋をした夜はすべてがうまくいきそうで。';
        },
        watch: {

        },
        methods: {

            generate(){


                    //まずはテキスト部分だけ生成させる
                    let vc = this;

                    html2canvas(document.querySelector("#text_body"),{
                        scale: 2 //文字がぼやけないように
                    }).then(function(canvas){


                        //まずは一旦テキストを画像に
                        var letter_body = document.querySelector("#letter_body");
                        letter_body.innerHTML = '';
                        letter_body.appendChild(canvas);
                        vc.letterHeight = document.getElementById('letter_body').offsetHeight;

                        //その後、メイン画像として生成
                        html2canvas(document.querySelector("#preview_inner"),{
                            scale: 2
                        }).then(function(canvas){

                            var result = document.querySelector("#result");
                            result.innerHTML = '';
                            result.appendChild(canvas);

                            // document.getElementById('preview').style.display = "none";
                            // document.getElementById('result').style.display = "none";


                            // 以下、サーバーに送る処理
                            // var dataURI = canvas.toDataURL('image/png');
                            // let dataform = new FormData();
                            // //
                            // const message_tmp = document.getElementsByName("message");
                            // dataform.append('id',vc.$route.params.id);
                            // dataform.append('body',message_tmp[0].value);
                            // dataform.append('img',dataURI);
                            //
                            // //サーバー側に画像を保存
                            // axios.post('/hoge/makeGcpOnly/', dataform).then(e => {
                            //
                            //     vc.imgData = e.data;
                            //     vc.dialogVisible = false;
                            //     vc.message = '';
                            //     vc.loading = false;
                            //
                            // }).catch((error) => {
                            //     console.log(error);
                            //     console.log("エラー");
                            // });

                        });


                    });

                }




        }
    };
</script>


これで画像が生成される。

名称未設定-3.png

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

2020年 2月のJestセットアップ

ありとあらゆるリファレンスを読みまくってまとめました。
一番スッキリJestをセットアップしよう。

Jestとは

Facebookが開発したJavaScriptの単体テストフレームワーク。
Node上で動作するため、手軽にテストを実行できる事が特徴。

Jest · ?快適なJavaScriptのテスト

作業環境

  • Laravel 5.7
  • Docker on Mac
  • Vue.jsとかはLaravel mixのもの

セットアップ

Jestとかをnpm install

npm install --save-dev jest babel-jest @babel/core babel-core@bridge @babel/preset-env vue-jest @vue/test-utils

それぞれのひとこと説明

jest
Jest本体

babel-jest
babelとjestを連携させるもの

@babel/core
Babel本体の最新版
Babelは八百万のJavaScript構文を一定のバージョンと互換のある構文に変換するもの。
Nodeでは最新のJavaScript構文(import / exportなど含め)が動作しません。
そのため、Babelを使ってトランスパイルしてあげます。

babel-core@bridge
vue-jest@babel/coreではなく、babel-coreを参照してしまうらしく、これを解決するためにインストール。
Babel 7.x系だけど名前はbabel-coreなBabel本体。

@babel/preset-env
山ほどあるBabelの構文変換に関わるライブラリをいい感じに自動的に使ってくれるもの。
参考 : babel-preset-envを簡単にさわってみた。 - Qiita

vue-jest
vueをjestでテストできるようにするもの

@vue/test-utils
Vueのテストを書きやすくしてくれるユーティリティ

Babelのバージョンに注意

Babel 7.xからプラグインのプレフィックスとして@babel/がつくようになりました。
ECサプリに入ってるBabelは7系なのでプラグインを追加する時、@babel/から始まっているか注意してください。

6.x以前のプラグインを使うとうまく動かない事があるらしい

設定ファイルをつくる

babel.config.js (.babelrc)

Babelの設定ファイルです。
babel.config.jsでも.babelrcでもいいのでソースルートに配置します。おそらく両方あるのはよろしく無いのでどちらか片方にしましょう。

babel.config.js
module.exports = {
    presets: [
        ['@babel/preset-env', {modules: false}]
    ],
    env: {
        test: {
            presets: [
                ['@babel/preset-env', {targets: {node: 'current'}}]
            ]
        }
    }
};

jest.config.js

どんなファイル名のものをテスト対象とするか、どのファイル名をどのモジュールで変換するか、などを設定できます。

jest.config.js
module.exports = {
    transform: {
        '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
        '^.+\\.js$': '<rootDir>/node_modules/babel-jest'
    },
    moduleFileExtensions: [
        'js',
        'vue'
    ],
};

package.json (既存のものを編集)

Jestの起動コマンドを書き足します。
NODE_ENV=testとすることでNodeへテスト環境であることを明示しています。

package.json
{
    "private": true,
    "scripts": {
        "dev": "npm run development",
        "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "npm run development -- --watch",
        ... ,
        "test": "NODE_ENV=test jest"  <-追記
    },
    "devDependencies": { ... },
    ...
}

テストを書く

テストケースを作ってみましょう。

こんなファイルをテスト対象にしてみました。
カリー化してある2つの数字を足すだけの関数です。

ExampleSum.js
export default{
    sum(x){
        return (y){
            return x + y;
        }
    }
}

これをテストするスクリプトはこんな感じ。

example.test.js
import ExampleSum from "path/to/ExampleSum";

test('test sum()', ()=>{
    expect(ExampleSum.sum(1)(2)).toBe(3);
}

実行してみよう

npm testとターミナルへタイプしてJestを動かしてみよう。
--でファイル名を連結することでそのファイル名と部分一致するファイルのみをテスト対象としてくれます。

npm test -- example.test.js

そのほかのCLIオプションは Jest CLI Options · Jest へ。

async/awaitもテストできる

PromiseExample.js
export default {
    sleep() {
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, 500);
        });
    }
}
test('test async', async () => {
    await PromiseExemple.sleep();
    expect(true).toBe(true);
});

:smiley: :tada:

Vueのフロントエンドテストもかけます

ガイド | Vue Test Utils

ExampleComponent.vue
<template>

</template>

<script>
    export default {
        name: "ExampleComponent",
        methods: {
            sum(x, y) {
                return x + y;
            }
        }
    }
</script>

<style scoped>

</style>
component.test.js
import ExampleComponent from "./models/ExampleComponent";

const {mount} = require("@vue/test-utils");

test('Vue component test', () => {
    const component = mount(ExampleComponent);
    expect(component.isVueInstance()).toBeTruthy();
    expect(component.vm.sum(1, 2)).toBe(3);
});

component.vmが僕らのよく知るVueコンポーネントインスタンスです。
DOMを操作してフォーム入力をしたり、クリックしたりもできます。


@babel/preset-envがびっくりするほど便利だったことがわかった。

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

vueで動的に生成したフォームのバインディング

構成

  • vue2.61
  • vuetify2.26

実現したこと

v-forを使って動的に生成したそれぞれのインプット項目をリアクティブに扱う

無駄にはまったところ

余計なv-modelをつけるとインプットイベントでv-forで展開したインプット項目が初期化されてしまう。

実装したもの

sample.vue
<template>
  <v-container v-for="item in list" :key="item.id"><!-- リアクティブだからといってここに v-model="list"としてはいけない-->
      <v-row>
          <v-col>
              <v-textfield
                  label="ユーザーアドレス"
                  v-model="item.email"
              ></v-textfield>
          </v-col>
          <v-col>
              <v-textfield
                  label="ユーザー名"
                  v-model="item.name"
              ></v-textfield>
          </v-col>
      <v-row>
  </v-container>
</template>

<script>
  export default {
    name: 'sample',
    data: () => ({
      list: [
        {id: 1, email: 'sample1@eamil.com', name: 'sample1'},
        {id: 2, email: 'sample2@eamil.com', name: 'sample2'}
      ],
    }),
  }
</script>

なんでこんなことでハマったのかと思うところではまった。
バインド対象のオブジェクトを削除したり、バインド対象のオブジェクトをユーザーアクションに紐付けて追加したりし、かつ、インプットイベントに対しては対象オブジェクトの中身をリアクティブに変更できるようにする的な箇所だったので、あれこれ元のリストの更新をリアクティブにやるにはモデルで紐づけてあげなければいけないのでは?と考えたところがハマりポイントだった。
二度同じミスをしないように備忘録として残す。

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

【Vue.js】vue-image-lightbox で画像ギャラリーを作成!

はじめに

Googleドライブの画像表示みたいなUIを作りたいと思っていたところ、vue-image-lightbox を使うと
簡単に実現できたので、最低限の実装方法を残したいと思います。

vue-image-lightboxとは?

サムネイルから、画像を拡大表示したり、スライドショーにしたりするための機能を提供しているライブラリです。

参考情報

今回の記事で実現した内容

以下の画像のようになるところをゴールとします。
記事中は主にライブラリに関係した部分を書いてます。ソースは参考情報から見てみてください。
画面収録 2020-02-18 12.19.11.mov.gif

環境

  • Vue.js 2.5.10
  • vue-cli 3.7.0

実装(準備編)

  • 環境構築
    vue createコマンドでプロジェクトを作成していきます。
    プロジェクト名は任意で入れてください。
    セットアップにあたり聞かれる質問も任意で答えていただいて構いません。
vue create image-app(プロジェクト名を入れてください)
cd image-app
  • 必要なライブラリのインストール
yarn add vue-image-lightbox vue-lazyload

※画像の遅延読み込み(Webページを先に表示して画像は読み込んだら表示させること)が必要になるため
vue-lazyloadも併せてインストールしています。

  • CSSのインポート
    まず、app.scssにvue-image-lightboxのCSSプロパティを読み込みます。
app.scss
@import '../../../node_modules/vue-image-lightbox/dist/vue-image-lightbox.min.css';
  • vue-lazyloadのインポート
    画像の遅延読み込みのため、main.jsにvue-lazyloadをインポートしていきます。
main.js
import VueLazyLoad from 'vue-lazyload';
Vue.use(VueLazyLoad);

実装(コンポーネント作成)

vue-image-lightboxを使用したコンポーネントを作成します。
CSSはBuefy(BulmaベースのUIコンポーネントライブラリ)を使い、サムネイルだけCSSを当てています。
要点は以下の通りです。

  • import文でvue-image-lightboxを読み込みます。
  • 画像表示は配列(images)を用意してv-forでimg要素を生成し、v-lazyで遅延読み込みしています。
  • img要素をクリックするとopenGalleryメソッドを発火させることでクリックした画像がギャラリー表示されます。  openGalleryメソッドはvue-image-lightboxのshowImageメソッドに配列(images)のインデックスを渡しています。
  • ImageLightBox要素で指定しているプロパティshow-light-boxがtrueだとブラウザにページを読み込んだタイミングでギャラリー表示されます。
  • show-captionがtrueにすると、ギャラリー表示時に画像オブジェクトに指定されたcaptionが表示されます。
LightBox.vue
<template>
    <div>
        <div class="media">
            <div class="column">
                <div class="columns is-multiline">
                    <div v-for="(image, index) in images" :key="image.id" class="column is-one-quarter has-text-centered">
                        <img v-lazy="image.src" @click="openGallery(index)" class="thumbnail" />
                        <p>{{ image.title }}</p>
                    </div>
                </div>
                <ImageLightBox ref="lightbox" :images="images" :show-light-box="false" :show-caption="true"></ImageLightBox>
            </div>
        </div>
    </div>
</template>

<script>
import ImageLightBox from 'vue-image-lightbox';

export default {
    name: 'LightBox',
    components: {
        ImageLightBox
    },
    data() {
        return {
            images: [
                {
                    thumb: 'https://www.pakutaso.com/shared/img/thumb/YAMAhokkaido015_TP_V.jpg',
                    src: 'https://www.pakutaso.com/shared/img/thumb/YAMAhokkaido015_TP_V.jpg',
                    title: '朝日が昇る摩周湖(北海道川上郡弟子屈町)',
                    caption: '朝日が昇る摩周湖(北海道川上郡弟子屈町)',
                    id: 1
                },
                {
                    thumb: 'https://www.pakutaso.com/shared/img/thumb/YAMAhokkaido032_TP_V.jpg',
                    src: 'https://www.pakutaso.com/shared/img/thumb/YAMAhokkaido032_TP_V.jpg',
                    title: '水鏡の青い池(北海道川上郡美瑛町白金)',
                    caption: '水鏡の青い池(北海道川上郡美瑛町白金)',
                    id: 2
                },
                {
                    thumb: 'https://www.pakutaso.com/shared/img/thumb/KMKC428D354_TP_V.jpg',
                    src: 'https://www.pakutaso.com/shared/img/thumb/KMKC428D354_TP_V.jpg',
                    title: '悪天候の上高地と大正池',
                    caption: '悪天候の上高地と大正池',
                    id: 3
                }
            ]
        };
    },
    methods: {
        openGallery(index) {
            this.$refs.lightbox.showImage(index);
        }
    }
};
</script>
<style lang="scss" scoped>
img.thumbnail {
    height: 100px;
    cursor: pointer;
}
</style>

最後に

画像のギャラリー表示をやりたくなった時になかなかvue-image-lightboxに行きつけなかったのですが、
vue-image-lightboxを見つけてから実装まではスムーズにいったので記事にしてみました。
ぜひお試しください!

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

Vuetifyのv-comboboxで自由に入力された文字列を取得したい

v-comboboxを使うということは自由入力させたいということなのに、自由入力した文字をうまく取得できない!
なぜか微妙にハマったのでメモっておきます。

例のとおりリスト要素から選択された際はselecteditemにオブジェクトがバインドされるまではいいが、では自由に入力された値は?
オフィシャルのドキュメントをよく読むと、、valueプロパティは The input's value、つまり入力された値はvalueプロパティに入る、と記載されております。そこでselecteditemのハンドリングとも併せて検証してみました。

結果、:value="inputedvalue"のバインドだと入力された値は取得できず、 ref="comb"で$refs経由の取得がうまくいきました。

<v-combobox
    v-model="selecteditem"
    :items="items"
    item-text="name" item-value="id" return-object
    ref="comb" :value="inputedvalue">
</v-combobox>

selecteditem: null,
items : [
    { id:"001", name:"牛丼" },
    { id:"002", name:"ピザ" },
    { id:"003", name:"ラーメン" },
]
<script>
var val = "";

// バインド経由では取れない
val = this.inputedvalue; 

// $refs経由でプロパティ直見すると取れる
val = this.$refs.comb.value;

// itemsの中の選択肢から選ぶ判定はv-modelがobjectになってることを確認すればよい
if(this.selecteditem && this.selecteditem.id) {
    val = this.selecteditem.id + " / " + this.selecteditem.name;
}

alert(val);
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue勉強2

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

vue勉強

  • const vm = new Vue ({Vは大文字じゃなきゃ動かない

  • <input type="text" v-model="message">v-modelみたいなv-〇〇はディレクティブ(属性ディレクティブ)といって、「ここを動かしまっせ」みたいな印の役割をしている
    直訳で 指示 という意味
    ディレクター(監督)から出る指示のニュアンス

  • バインディング
    相互に関連付けること。
    双方向データバインディングとかいう

  • v-model
    フォーム入力バインディング。
    input要素とかtextarea要素とかのフォームの状態を、p要素などにリアルタイムで関連付けることができる。
    textareaに入力した文言をp要素内に出力するなど。
    公式

  • idとエレメント名(elの値)が一致していることを確認する

  • v-forのときにリストの配列名を複数形にしたときにsの付け忘れに注意

  • ES6
    スプレッド構文
    アロー関数

  • マウント:mounted は Vue.js におけるライフサイクル(参考)において、仮想 DOM が DOM に置き換わるタイミングを指します

  • const
    定数。多分、「以下を定義します」みたいな意味

  • vm
    view model の略

  • v-bind:class
    状況によってクラスを可変にする
    チェックボックスにチェックが入っていたらクラス(スタイル)を変えるみたいなことができる
    公式

  • filter()メソッド
    例えば、「todoリスト内のアイテムをふるいに掛けてチェックが入っていないアイテムだけを並べる」みたいなことができる。
    配列にフィルターを掛けてあたらしい配列を生成する。
    カッコ内の引数には関数を渡す。その関数に従ってフィルターをかける

  • v-for
    :key(v-bind:keyの略)を必ず付ける。
    固有(ユニーク)なidなどをリストアイテムに付す役割。
    理由は固有のidを振らないとスタイルやチェックの有無などの状態を紐付けられないから。
    keyを持ったアイテムは、状態を維持したまま並べ替えることができるが、keyを持たせないと並べ替えや削除などを行ったときにインデックス番号が状態を持ってしまい、状態を紐付けられない
    わかりやすい説明
    公式

  • el,props,data,methodsなどの名前
    コンポーネントオプション
    推奨される順序

  • 簡単なhello world

script src="https://cdn.jsdelivr.net/npm/vue"
#app
  | {{testVal}}
javascript:
  const app = new Vue({
    el: '#app',

    data: {
        testVal: 'Hello World!' //testValを定義
    }
  });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vuex] getters/mutatinos/actions は複数形だぞ!

複数形

new Vuex.Store({
  state: {},
  mutations: {}, // ← "mutation" ではなく "mutations"
  actions: {},   // ← "action" ではなく "actions"
  getters: {},   // ← "getter" ではなく "getters"
});

単数形だと…

[vuex] unknown action type: xxx

あるいは

[vuex] unknown local action type: xxx, global type: xxx

といったエラーが出ます。

小一時間悩んでしまった…

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

OpenAPIによるスキーマファースト開発の実施サンプルとCloud Runについて

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

iOSのsafariでinput[type="file"]を見た目をいい感じにしたかった

Nuxt.jsで組んでいたアプリをiOS端末でデバッグしてみたらいい感じに動かなかったので、いろいろ探ってみて分かったことをメモ。

問題の起きたコード

とりあえずinputを非表示(display: none)にして、inputの代わりのボタンをクリックしたときに、非表示のinput要素をクリックするイベントを発火させるやつ。


See the Pen
image select(included bug)
by keyakko (@keyakko)
on CodePen.

PCでは動く。
safariではいい感じに動かない。

よくわからない動き

問題のコードでも、画像選択の画面は出てくるんですよね。
ただ、選択後にonchangeのイベントが発火されない。(alertがでない)

試しにinput要素を表示して、inputの要素をクリックしたら当たり前のようにイベントは発火されてalertは出る。
JS経由でクリックさせて、ファイル選択させてもイベント発火されないのはなんで...?

対処

とりあえず今回の対処。

1. buttonとinputの要素の順番を変える

codepenにコード書いててたまたま気づいた。完全に知識不足。


See the Pen
image select(fix pattern1)
by keyakko (@keyakko)
on CodePen.

<label>
  <button>アップロード</button>
  <input type="file" />
</label>

2. ボタンの上に透明にしたinput要素を乗せる

Buefyのソースが参考に。


See the Pen
image select(fix pattern2)
by keyakko (@keyakko)
on CodePen.

さいごに

いろいろ探すと、display:noneはちゃんと動かないからwidth:0; height:0; overflow:hidden;で対応しようねって出たりするけど、それではいい感じにならなかった。
最終的には対処方法の2番に行き着いた感じ。

話はそれるけど、iPhoneのsafariのコンソールが見たいのにsafariの開発タブにデバイスがすんなり表示されないの自分だけ...?
そっちでも苦しめられた...(ここを参考にWi-FiとBluetoothをオフにしたらなぜかちょっと表示されたけど)

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