20201116のvue.jsに関する記事は13件です。

Vue CLIで新規プロジェクトを作成する

環境は以下で作成

  • node
    • 14.15.0
  • vue cli
    • 4.5.8

事前準備

docker-compose.yamlDockerfile を準備

docker-compose.yaml
version: '3'
services:

  sample-front:
    build: <path-to-dockerfile>
    container_name: sample-front
    tty: true
    volumes:
    - ./../sample-front/:/app
    ports:
      - "8080:8080"

Dockerfile

FROM node:14.15.0-alpine3.12

ENV VUE_CLI_VERSION 4.5.8

RUN yarn global add @vue/cli@${VUE_CLI_VERSION}

WORKDIR /app

コンテナ立ち上げ

⛄ docker-compose up

確認

⛄ docker ps

コンテナに入る

⛄ docker exec -it sample-front ash

インストールしたもののversion確認(スキップ可)

/app # node -v
v14.15.0
/app # vue --version
@vue/cli 4.5.8

プロジェクト作成

今回はカレントディレクトリに作成

vue create .

カレントに作らない場合は、以下のコマンドを使用

vue create <app-name>

設定は好みがあると思うのでお好きなように

Vue CLI v4.5.8
? Generate project in current directory? (Y/n) -> Y

? Please pick a preset: (Use arrow keys)
  Default ([Vue 2] babel, eslint)
❯ Default (Vue 3 Preview) ([Vue 3] babel, eslint)
  Manually select features

? Pick the package manager to use when installing dependencies: (Use arrow keys)
❯ Use Yarn
  Use NPM

今回自分はVue 3とYarnを選択

?  Successfully created project app.
?  Get started with the following commands:
 $ yarn serve

docker-composeに以下の部分を足して、サーバーが立ち上がるか確認してみる

command: >
  ash -c "yarn install &&
  yarn serve"

立ち上げ

⛄ docker-compose up
sample-front    |   App running at:
sample-front    |   - Local:   http://localhost:8080/

ブラウザで以下のURLを叩いて確認してみると...

http://localhost:8080/

以下の画像のような画面になっていたらOK!
スクリーンショット 2020-11-16 23.11.46.png

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

Vue.jsでObnizを操作する基本-単純Lチカ(メモ)

Vue.jsからのObniz操作でエラーになったのでメモ。Webアプリでボタン押下でLEDを点灯。

Consoleエラー

image.png

原因

var obniz = new Obniz('1234-5678');
obniz.onconnect = async function() {
  // **** 実行する処理 ****
}
  if (obniz.connectionState === "connected") {
    // **** 実行する処理 ****
  } else {
    obniz.on('connect', () => {
      // **** 実行する処理 ****
  })

修正後のコード

.html
<!DOCTYPE html>
<html lang="jp" >
<head>
  <meta charset="UTF-8">
  <title>Obniz Test</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  <!-- 全体をVue.js有効にする -->
  <div id="app">
    <!-- タイトル -->
    <h1>Obniz Test</h1>

    <!-- 設定 -->
    <h5>Obniz ID</h5>
    <input v-model:value="ObnizID[0]" type="text" maxlength="4">
    <label>-</label>
    <input v-model:value="ObnizID[1]" type="text" maxlength="4">
    <!-- LED-ON-OFF -->
    <button v-on:click="PowerON">LED-ON</button>
    <button v-on:click="PowerOFF">LED-OFF</button>
  </div>

  <!-- CDN -->
  <script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js'></script>
  <script src='https://unpkg.com/obniz@3.9.0/obniz.js'></script>
  <!-- 実行script -->
  <script  src="./script.js"></script>

  </body>
</html>
script.js
let obniz;  // Obniz関数

// Obniz呼び出し関数
const connect = function(func, ob){
  console.log(ob.connectionState);
  // Obnizへの接続を確認
  if (ob.connectionState === "connected") {
    func();
  } else {
    ob.on('connect', () => {
      func();
    })
  }
}

const app = new Vue({
  el: '#app', // Vueが管理する一番外側のDOM要素
  data: {
    // Vue内部で利用する変数定義
    ObnizID: ['0000', '0000'],
  },
  methods: {
    // 関数はココに記述
    PowerON: function() {
      // LED ON
      // Obniz ID 指定
      let obnizid = `${this.ObnizID[0]}-${this.ObnizID[1]}`;
      this.obniz = new Obniz(obnizid);
      console.log(obnizid);

      let me = this; // thisを関数内で使えないので変数に代入
      // connect関数を呼んで、connect関数内で以下のFunctionを実行
      connect(async function() {
        const led = me.obniz.wired('LED', { anode: 0, cathode: 1 });
        me.obniz.display.clear();
        me.obniz.display.print('ON');
        led.on(); // LED点灯
      }, this.obniz);
    },

    PowerOFF: function() {
      // LED OFF
      // Obniz ID 指定
      let obnizid = `${this.ObnizID[0]}-${this.ObnizID[1]}`;
      this.obniz = new Obniz(obnizid);
      console.log(obnizid);

      let me = this; // thisを関数内で使えないので変数に代入
      // connect関数を呼んで、connect関数内で以下のFunctionを実行
      connect(async function() {
        const led = me.obniz.wired('LED', { anode: 0, cathode: 1 });
        me.obniz.display.clear();
        me.obniz.display.print('OFF');
        led.off();
      }, this.obniz);
    },
  },
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue.js]v-bind

v-bindでGoogleへのlinkつくってみる

js
data:{
//htmlに挿入するurlをdataの中に入れておく
  url:'https://www.google.com/?hl=ja'
//これでGoogleへのリンクが貼られる
<a v-bind:href = "url">google</a>

Googleへのリンク以外にもクラスやidなどまとめて表示させたい場合

js
data:
//まとめてgoogleオブジェクトをつくる
    google:{
        href:'https://www.google.com/?hl=ja',
        id:12,
        class:'goo'
    }
html
<a v-bind = 'google'>google</a>

href="https://www.google.com/?hl=ja" id="12" class="goo">google

検証画面で見ると上のようになっている(aタグが本当はある)

ちゃんとhref属性とidとclassがまとめてaタグの中に入ってます

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

[Vue.js]methodsを使う

簡単なmethodを使う

js
new Vue({
    data:{
    },
    methods:{
//SayHi!!!と言うメソッドを作る
    SayHi:function(){
        return 'SayHi!!!'
    }
html
//SayHi!!!と表示される
<p>{{sayHi()}}</p>

dataから呼び出したものをメソッドに使う

   data:{
         message:'hello'     
}
    SayHi:function(){
//クォテーションをつけない、thisをつけないとアクセスできない
        return this.message;
    }

これでhelloを呼び出すことができる

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

Hello Vue.js 初心者が学んでみた

javascript初心者がVueを学んでいく

まず自分のレベルはjavascriptの基礎を覚えたか覚えてないかくらいの初心者なので的外れなことも多々あるかもしれないのでご注意ください。

//html
<body>
//Hello Vue.jsと表示させてみる</h1>
<div id="app">
//普通にかくとこう
<p>Hello Vue.js</p>
  <script>
//vueインスタンスを作る
        new Vue({
//elでhtmlのどこを対象にするかを指定する。
            el:'#app',
//dataには色々なものを定義できる。 ここではHello Vue.jsと表示させたいのでmessage は Hello Vue.js だよーというのを指定
            data:{
                message:'hello Vue.js'
            }

このmessageに入れたのを使うにはhtmlに指定してやる必要がある

//{{}}これにさっき作ったmessageを入れてやる
<p>{{message}}</p>

こうするとHello Vue.jsと表示することができる

ちなみにこれは v-textという物を使っても表現することができる

html
//これでも同じくHello vue.jsというのが呼び出せる
<p v-text = 'message'></p>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android 11以降でWebViewでローカルHTMLが表示できないとき

※本記事は、NativeScript-Vueのコードを提示していますが、考え方は、どの開発言語・フレームワークでも同一と思われます。予めご了承ください。

陥った事象

最近、NativeScript-Vueというものを使って、Androidネイティブアプリケーションを試しに作ってみたりしています。Vue.jsに慣れている人であれば、楽にネイティブアプリを作ることができるということで、使わせてもらっています。

さて、開発をしている最中に、UIコンポーネントの1つである「WebView」を使って、アプリ内のアセットとして配置しているHTMLファイルを表示させたい場面がありました。

通常であれば、NativeScript-Vueの場合、次のように書きます。

./src/components/App.vue
<template>
  <Frame>
    <Page>
      <GridLayout columns="*" rows="*">
        <!-- ↓ここでWebViewコンポーネントを配置して、srcとしてassetsディレクトリ内のファイルを指定 -->
        <WebView row="0" col="0" src="~/assets/index.html" />
      </GridLayout>
    </Page>
  </Frame>
</template>

しかしこの状態で、アプリをAndroid 11上で動作させると、次のような「ERR_ACCESS_DENIED」という表示になってしまいました。エミュレータでも実機でも同様です。

ERROR

そして、試しにAndroid 10のエミュレータで動作確認すると、正常に表示されるのです。

解決方法

Androidのリファレンスに次のようなことが書かれていました。

setAllowFileAccess

The default value is true for apps targeting Build.VERSION_CODES.Q and below, and false when targeting Build.VERSION_CODES.R and above.

バージョンコードR(つまりAndroid 11)から、WebSettingsのAllowFileAccessの設定が、デフォルトでfalseになっていますよ、とのことです。ですので、明示的にtrueにしてあげることで、解決できそうです。

NativeScript-Vueの場合、次のように、Loadedイベントと組み合わせることで、解決できました。

./src/components/App.vue
<template>
  <Frame>
    <Page>
      <GridLayout columns="*" rows="*">
        <!-- ↓ここでWebViewコンポーネントを配置して、loadedイベントを捕捉 -->
        <WebView row="0" col="0" @loaded="webViewLoaded" />
      </GridLayout>
    </Page>
  </Frame>
</template>

<script lang="ts">
export default {
  methods: {
    // loadedイベントが発生した時の処理
    webViewLoaded(args) {
      if (args.object.android) {
        args.object.android.getSettings().setAllowFileAccess(true); // ここで、setAllowFileAccessメソッドを使って、trueにする
        args.object.src = "~/assets/map/index.html"; // 上記設定をしてから、ファイルを初めて読み込む
      }
    },
  },
};
</script>

ただ、同リファレンスにも少し書かれているのですが、このような方法でHTMLを表示させるよりも、より安全な方法があるのかもしれません。


※結局自己解決したのですが、もともとStackOverflowで質問していました

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

親から孫コンポーネントのデータ連携方法

概要

vue.jsとtypescriptでアプリを制作中、親-子コンポーネントのデータ連携はできたが、親-子-孫コンポーネントのデータ連携に少し時間がかかってしまった。

そこで、階層の深いコンポーネント間のデータ連携についてまとめていきます。

親クラス
Parent.vue
<template>
<!-- 1, 2 -->
  <Child
    :name="name"
    @nameFromChild="name=$event">
    </Child>
</template>

<script lang="ts">
import {Vue, Component} from 'vue'
import Child from 'components/Child.vue'

@Component({
    components: {
        Child
    },
    })
class Parent extends Vue {
    /** 名前(変更対象) */
    private name = "";
}  
</script>

  1. Childコンポーネントに空のname=""を渡す
  2. 子コンポーネントが"nameFromChild"で発火したイベントを受け取り、name変数に変更された値が入る
子クラス
Child.vue
<template>
<!-- 2, 3 -->
  <GrandChild
    :grandChildName="childName"
    @grandChildName="childName = $event">
    </GrandChild>
</template>

<script lang="ts">
import {Vue, Component} from 'vue'
import Child from 'components/Child.vue'

@Component({
    components: {
        Child
    },
    })
class Child extend Vue {
    /** 1 */
    @Prop()
    private name = "";

    /** 3 */
    get childName(){
        return this.name;
    }

    /** 4 */
    set childName(name: string){
        this.$emit('nameFromChild', name);
    }
}  
</script>

  1. 親コンポーネントからProp関数で空のname=""を受け取る
  2. GrandChildコンポーネントに空のchildName=this.nameを渡す
  3. 孫コンポーネントが"nameFromGrandChild"で発火したイベントを受け取り、childNameに変更された値が渡される
  4. 孫コンポーネントからgrandChildNameでイベントが発火される。childNameが孫から渡された値($event)によって変更された場合、下記ではemit関数で親コンポーネントにイベントを発火し、書き換えられたnameを渡す
孫クラス
GrandChild.vue
<template>
  <v-text-field
    :value="grandChildName"
    @input="$emit('nameFromGrandChild', $event)">
    </v-text-field>
</template>

<script lang="ts">
import {Vue, Prop} from 'vue'

class GrandChild extends Vue {
    /** 1 */
    @Prop()
    private grandChildName?: string;
}  
</script>

  1. 子から渡ってきた空のgrandChildName=""
  2. emit関数を使用し、テキストフィールドが変更(input)されたら、変更された値($event)を引数を持ってイベントを発火
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

3年間数学に触れていない大学生がセンター数学で9割を目指したことがきっかけで習慣継続アプリを作った話。【個人開発】

こんにちは、かしわ(@kasiwa9494)と申します。

1年前にプログラミングを勉強し始めて、今回、個人開発を継続していくためのアプリを作成しました。

何を作ったのか?

タテミチ
demo
demo

タテミチ
目的を達成するまでのメモを残せるWebサービスです。

自分だけのプロジェクトを簡単に作成できます。

なぜ、作ったのか?

3ヶ月前、友達と「3年間数学に触れていない文系大学生(数学受験)がどれくらいセンター数学で点数が取れるのか」という企画を行っていました。

1日目、数学1A25点という結果で、時間が経てば、人間の記憶は
薄れていくものだと、実感させられました、、、
写真 2020-08-28 13 48 05.jpg
2週間後、毎日、過去問を解くことを継続することで、
センター数学の解き方の勘と基礎知識を思い出し、数学1A77点を取ることができました!!!
写真 2020-09-09 18 31 28.jpg
結果的にみると、2~3週間で70~80点台は叩き出すことができました。
しかし、9割は難しかったです:sob:

この経験により、「友人に自分が習慣にしたいことを共有し、
反応をもらえると、めちゃくちゃ嬉しい:relaxed:
ということに気付きました。
service-image (1).jpg
よって、習慣を続けさせるシステムをベースとしたWebサービスを作れたら良いなと思い、
今回、作成しました!!!

どうやって、作ったのか?

使った技術

Nuxt.js

Netlify上でホスティングしています。
SPAで作っています。

Firebase

データベースとしてFirebase FireStore,
認証としてFirebase Authentication,
OGP用の画像を保存するために,FirebaseStorageを利用しています。

Netlify

SPAで動的ページのOGPを使えるようにするために
Netlifyのプレレンダリング機能を活用しています。

めちゃくちゃ便利です。

TypeScript

コンポーネント間のデータの受け渡しで
どういう型か判断でき、コードが見やすくなりました。

今まで、手を出すのが面倒でしたが、
使ってみると、快適です。

最新の投稿を検知

demo
firestoreに投稿を保存すると、通知が出るように設定しています。

UX的に適切かどうか分かりませんが(勉強不足)、
個人的に欲しいと思ったので、実装しました。

動的OGPを実装

SPAでも、コンテンツに応じてOGP画像を出力できるNetlifyのPrerenderingの機能を利用しています。

これにより、JavaScriptで設定したmetaタグ、OGP画像がTwitter,Facebookで表示できるようになりました。(動的OGPの実装)

どのくらいの期間で作ったのか?

約2週間。
作業時間としては、60~80時間ぐらいだと思います。

今後の展望は?

アップデートとしては、
・拍手ボタン(応援)を作る。
・デザインを改善する
など考えています。

今回の振り返り

Nuxt + TypeScriptを初めて、個人開発で利用しました。

TSのおかげでコードを快適な環境でかけたので、
今後もTypeScriptを利用していきたいです。

これからの個人開発の目標としては、
Webサービスを作る工程を最適化し、3日でサービスを公開できるようにすることです。

どうしても、Webサービスを作るという作業に億劫になってしまうので、
今回作った「タテミチ」を活用して、モチベーションをキープできるようにしたいです。

今、行っているプロジェクト


読書する習慣をつける | 継続するならタテミチ
CSSアニメーションを極める | 継続するならタテミチ
料理スキルを身に付けるために、個人開発でクッキングアプリを作る。| 継続するならタテミチ

ぜひ、プロジェクトを始めてみてください

demo
タテミチ

ここまで読んでくれた方へ

以上、習慣を継続するためにwebサービスを作った話でした。

ここまで読んでくださりありがとうございました。

いいねやコメント、SNSでの共有等をしてくださると、めちゃくちゃ嬉しいです。

良かったら、ツイッター(@kasiwa9494)フォローしてください。

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

Vue.js で googleMap に overlay view を表示するコンポーネントを作る

google.maps.OverlayViewを継承する用クラス

GoogleOverlay.ts
export class GoogleOverlay {
  lat: string | number
  lng: string | number
  drawable: boolean = true
  div: HTMLDivElement | null
  className: string = ''
  google: any
  innerHtml: string
  updateClass: (className: string) => void
  updateHtml: (html: string) => void
  active: () => void
  inactive: () => void
  // extend google.maps.OverlayView
  getProjection: any
  getPanes: any
  setMap: any
  draw: () => void
  remove: () => void

  constructor(
    google: any,
    map: any,
    lat: string | number,
    lng: string | number,
    innerHtml: string,
    className: string = '',
    events: { click?: () => void } = {}
  ) {
    this.lat = lat
    this.lng = lng
    this.div = null
    this.google = google
    this.innerHtml = innerHtml
    this.className = className

    this.remove = () => {
      if (!this.div) return
      ;(this.div.parentNode as Node).removeChild(this.div)
      this.div = null
    }

    this.draw = () => {
      this.remove()
      if (!this.drawable) return
      const projection = this.getProjection()
      if (!projection) return
      const point = projection.fromLatLngToDivPixel(
        new this.google.maps.LatLng(this.lat, this.lng)
      )

      this.div = document.createElement('div')
      this.div.className = this.className
      this.div.innerHTML = this.innerHtml
      this.div.style.left = point.x + 'px'
      this.div.style.top = point.y + 'px'

      const panes = this.getPanes()
      panes.overlayMouseTarget.appendChild(this.div)
      google.maps.event.addDomListener(this.div, 'click', () => {
        events.click && events.click()
      })
    }

    this.updateHtml = (html: string) => {
      this.innerHtml = html
      this.draw()
    }

    this.updateClass = (className: string) => {
      this.className = className
      this.draw()
    }

    this.active = () => {
      this.drawable = true
      this.draw()
    }

    this.inactive = () => {
      this.drawable = false
      this.remove()
    }

    this.setMap(map)
  }
}

GoogleMapにOverlayViewを表示する用のコンポーネント

GoogleMapOverlayView.vue
<template>
  <div v-show="false">
    <slot />
  </div>
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { GoogleOverlay } from '../../lib/googleMap'

@Component
export default class GoogleMapOverlayView extends Vue {
  @Prop() private google!: any
  @Prop() private map!: any
  @Prop() private center!: { lat: number; lng: number }
  @Prop() private active!: boolean
  overlay: GoogleOverlay | null = null
  html: string = ''

  @Watch('active')
  setOrRemove() {
    if (this.active) {
      this.overlay ? this.overlay.active() : this.setOverlay()
      return
    }
    this.overlay && this.overlay.inactive()
  }

  mounted() {
    this.setOrRemove()
  }

  updated() {
    if (!this.overlay || !this.$el) return
    if (this.html === this.$el.innerHTML) return
    this.overlay.updateHtml(this.$el.innerHTML)
    this.html = this.$el.innerHTML
  }

  setOverlay() {
    GoogleOverlay.prototype = new this.google.maps.OverlayView()
    this.overlay = new GoogleOverlay(
      this.google,
      this.map,
      this.center.lat,
      this.center.lng,
      this.html,
      'google-map-overlay-view',
      { click: this.handleClick }
    )
  }

  handleClick() {
    this.$emit('click')
  }
}
</script>

使用例

<GoogleMap
  :zoom="17"
  :center="center"
  :height="240"
>
  <template slot-scope="scope">
    <GoogleMapOverlayView
      v-for="overlay in overlays"
      :key="overlay.key"
      :center="overlay.latLng"
      :google="scope.google"
      :map="scope.map"
      @click="handleSelectOverview(hotel)"
    >
      <div>overlayView</div>
    </GoogleMapOverlayView>
  </template>
</GoogleMap>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コンポーネントに、リンクは一つまで【2020/11/16】

SNSで、今月のおすすめ記事、のようなものを作るときは、投稿いくつかまとめて、その投稿ひとつひとつがリンクを持っている。

この場合は、
1. organism = 今月のおすすめ記事
2. molecule = 各々の投稿 <= routerLinkをひとつ持つ

とすると、見通しが良くなる

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

Vueでお絵かき機能を実装

ポートフォリオで写真をクロッピングし、絵を書いたり、フィルターを掛けたり、テキストを描画したりできる機能を実装したので、復習をかねて記事にする。
こちらの方のサイトを例に作成させて頂きました。

https://tech.pjin.jp/blog/2020/04/16/%E3%80%90vue-js%E3%80%91%E3%81%8A%E7%B5%B5%E6%8F%8F%E3%81%8D%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92vue%E3%81%A7%E5%AE%9F%E8%A3%85%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B/

drawing.vue
<template>
  <div>
    <div>
      <canvas ref="canvas" v-bind="attr" @mousedown="dragStart" @mouseup="dragEnd" @mouseout="dragEnd" @mousemove="draw"></canvas>
//スマホはこれ @touchstart="dragStart" @touchend="dragEnd" @touchmove="draw"
    </div>
    <div id="tool-area">
      <v-btn @click="context.globalCompositeOperation = 'source-over';mode = true" :color="mode ? 'red' : 'black'">ペン</v-btn>
      <v-btn @click="context.globalCompositeOperation = 'destination-out';mode = false" :color="!mode ? 'red' : 'black'">消しゴム</v-btn>
      <!-- globalCompositeOperationをdestination-outにしないとただ背景を白で塗りつぶすことになる。
      <v-btn @click="clear">クリア</v-btn>
    </div>
    <div>
      <!-- v-modelで常に色を双方向データバインディング -->
      <v-color-picker v-model="penColor"></v-color-picker>
      <!-- 色と同様ペンの太さを双方向データバインディング -->
      <v-slider v-model="lineWidth" ticks="always" max="20" thumb-label="always"></v-slider>
    </div>
  </div>
</template>

<script>

export default {
  data() {
    return {
      attr: {
        width: '500px',
        height: '500px',
      },
      context: null,
      isDrag: false,
      penColor: '#000000',
      lineWidth: 10,
      mode: true,
    }
  },
  watch: {
    penColor(){//ペンの色が変わればcanvasのコンテキストに反映される
      this.context.strokeStyle = this.penColor;
    },
    lineWidth(){//ペンの太さがが変わればcanvasのコンテキストに反映される
      this.context.lineWidth = this.lineWidth;
    }
  },
  mounted(){
    //canvasの初期設定
    const canvas = this.$refs.canvas;
    this.context = canvas.getContext('2d')
    this.context.lineCap = 'round';
    this.context.lineJoin = 'round';
    this.context.lineWidth = this.lineWidth;
    this.context.strokeStyle = this.penColor;
  methods: {
    // 描画開始(mousedown)
    dragStart(e) {
      const x = e.layerX//canvas内のx座標を取得
      const y = e.layerY//canvas内のy座標を取得

      this.context.beginPath();
   //パスをリセット。これがないとクリックした場所から最初クリックした場所に毎回ラインが走る。
      this.context.lineTo(x, y);
      this.context.stroke();

      this.isDrag = true;
    },
    // 描画
    draw(e) {
      const x = e.layerX
      const y = e.layerY

      if(!this.isDrag) {
        return;
      }

      this.context.lineTo(x, y);
      this.context.stroke();
    },
    // 描画終了(mouseup, mouseout)
    dragEnd() {
      this.context.closePath();
      this.isDrag = false;
    },
    clear() {
      this.context.clearRect(0, 0, this.attr.width, this.attr.height);
      //canvasのwidthとheightを入れると全体がリセットされる。
    },
  }
}
</script>

canvas.getContext('2d')でcanvasの環境を操作できるようになるという認識。
なおcontextは初期状態で以下のような設定になっている。

{
    direction: "ltr"
    fillStyle: "#000000"
    filter: "none"
    font: "10px sans-serif"
    globalAlpha: 1
    globalCompositeOperation: "source-over"
    imageSmoothingEnabled: true
    imageSmoothingQuality: "low"
    lineCap: "butt"
    lineDashOffset: 0
    lineJoin: "miter"
    lineWidth: 1
    miterLimit: 10
    shadowBlur: 0
    shadowColor: "rgba(0, 0, 0, 0)"
    shadowOffsetX: 0
    shadowOffsetY: 0
    strokeStyle: "#000000"
    textAlign: "start"
    textBaseline: "alphabetic"
}

これらの値を変えるとどうなるか調べて使ってみよう。
なお、今回使ったのはfillStyle、globalCompositeOperation、lineCap、lineJoin、lineWidthである。

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

Vue + Electron の NodeIntegration でハマった話

環境について

@vue/cli: 4.5.8
vue: 2.6.11
electron: 9.0.0

vue add electron-builder でプロジェクト作成。
Typescrtipt使用。あと、関係あるかは分かりませんがvuetify(2.2.11)も使用。

起こった症状

ipcRenderer を ブラウザ側でimport使用とすると、fs.existsSync is not a function とエラーが発生し、ページが描画されない。

image.png

もちろん、webPreference の nodeIntegration は true にしてあります。

対処法

vue-cli-plugin-electron-builderのissueで解決したという人がいたため、そのリンク先であるElectronのissue を見に行きます。
すると、require 周りでエラーが出ているのでラップするとか、preloadにipcRendererを入れるといいとか書いてあります。
でも前者はどこを直すかよくわからないし、後者はTypescrtiptを使っていると、windowにプロパティを足すのにいろいろと手間が必要……
なのでもう少し探してみることにしました。

解決

上記のissueの下のほうにありました。
vue.config.js をいじる方法です。

vue.config.js
module.exports = {
  "transpileDependencies": [
    "vuetify"
  ],
  pluginOptions: {
    electronBuilder: {
      nodeIntegration: true
    }
  }
}

このpuluginOptionsを追加したところ、無事にエラーが消えてビルドできるようになりました!

image.png

めでたしめでたし。

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

Vue.jsを使って簡単なカウンターアプリを作ってみた

最近Vue.jsの勉強を本格的に始めました。ひとまず何か簡単なものを作ろう!ということで、今回はカウンターアプリを作ってみました。良ければお試し下さい→カウンターアプリ

実現したいこと

1.ボタンをカチカチして数値を変更し、計算させる。
2.計算方法は足し算、引き算、掛け算、割り算を用意する。
3.計算方法の表記は、切り替えたタイミングで上に表示させる。

app2.gif

コード

index.html
<!DOCTYPE html>
<html lang="jp">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>カウンターアプリ</title>
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Oswald:wght@400;700&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="app">
        <div id="container">
            <h1 class="title">{{title}}</h1>
            <div class="inputArea">
                <div class="top">
                    <button class="btn" @click="firstIncrement">+</button>
                    <button class="btn" @click="secondIncrement">+</button>
                </div>
                <h1 class="cal">{{first}} {{key}} {{second}} = {{resultAdd(key)}}</h1>
                <div class="bottom">
                    <button class="btn" @click="firstDecrement">-</button>
                    <button class="btn" @click="secondDecrement">-</button>      
                </div>
            </div>
            <div class="format">
                <button  class="changeBtn" @click="key = '+'">+</button>
                <button  class="changeBtn" @click="key = '-'">-</button>
                <button  class="changeBtn" @click="key = '×'">×</button>
                <button  class="changeBtn" @click="key = '÷'">÷</button>
                <button  class="changeBtn" @click="first = 0, second = 0">C</button>
            </div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
    <script src="main.js"></script>
</body>
</html>
style.css
*{
    margin: 0;
    padding: 0;
    font-family: 'Oswald', sans-serif;
    touch-action: manipulation;
}
body{
    color: white;
    background-color: black;
}
#container{
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
button{
    color:black;
    background-color: white;
    outline: none !important;
    cursor: pointer;
}
.title{
    font-size: 45px;
    margin-bottom: 60px;
}
.cal{
    font-size: 50px;
    text-shadow: 3px 3px rgb(121, 121, 121);
}
.inputArea{
    width: 100%;
    height: 250px;
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
    margin-bottom: 60px;
    & .btn{
        font-size: 30px;
        font-weight: 700;
        padding: 5px 10px;
        margin: 20px;
    }
    & .top,
    .bottom{
        margin-right: 70px;
    }
}
.changeBtn{
    font-size: 30px;
    font-weight: 700;
    padding: 5px 10px;
    margin: 0px 10px;
}
main.js
new Vue({
    data:{
        first:0,
        second:0,
        sum:0,
        title:'addition',
        format:['Addition','Subtraction','Multiplication','Division'],
        key:'+',  
    },
    methods:{
        firstIncrement(){
            this.first += 1;
        },
        firstDecrement(){
            if(this.first > 0){
                this.first -= 1;
            }
        },
        secondIncrement(){
            this.second += 1;
        },
        secondDecrement(){
            if(this.second > 0){
                this.second -= 1;
            }
        },
    },
    computed:{
        resultAdd(){
            return function(f){
                if(f === '+'){
                    this.title = this.format[0];
                    return this.first + this.second;
                }else if(f === '-'){
                    this.title = this.format[1];
                    return this.first - this.second;
                }else if(f === '×'){
                    this.title = this.format[2];
                    return this.first * this.second;
                }else{
                    this.title = this.format[3];
                    this.sum = this.first / this.second;
                    return Math.round(this.sum * 1000) / 1000;
                }
            }
        },
    },
}).$mount("#app");

methodsプロパティ

+ボタン、-ボタンをクリックすると、methodsプロパティのIncrementメソッド、Decrementメソッドが発火します。メソッド内ではdataプロパティに設定したfirstプロパティとsecondプロパティを参照し、値を加算、減算します。
メソッドからdataプロパティを参照する時はthisを忘れずに(よく忘れてエラーになります・・・)

computedプロパティ

computedプロパティのresultAddではfirstやsecondの変更を検出して計算結果を返してくれます。
さらに今回は計算方法が4パターン欲しいので、resultAddに引数を持たせて、条件分岐をさせてみました。例えば渡された引数の文字列が「+」だったら、足し算の結果を返す。「×」だったら、掛け算の結果を返す。という感じです。

また、計算方法の変更は、下の「+」「-」「×」「÷」のボタンで変更しますが、これらのボタンが押された際も、resultAddが発火し、表記の変更をしつつ、変更した方法で計算結果を返してくれます。(computed便利!)

割り算で四捨五入

割り算で割り切れない場合、結果が「3.33333333・・・」という感じで表示され、レイアウトが大変なことになるので、今回はMath.round関数を使って、小数点第四位を四捨五入しています。

作ってみて

という感じで、簡単なカウンターアプリを作ってみました。
JavaScriptと比べるとVue.jsはコードが短く、簡潔に記述できるので、(もっと簡潔に書けるかもしれませんが、、)改めてフレームワークすご!!と思いましたね。

引き続きVue.js頑張りたいと思います!

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