20200818のvue.jsに関する記事は6件です。

VuePressにTailwind CSSを導入する方法

最低限トップページをVuePressで表示させる

VuePressでトップページを表示させる手順は公式ドキュメントに沿って行ってください。

VuePress 公式ドキュメント

この時点でのディレクトリ構成は以下の通りです。

.
├── docs
│   ├── README.md
├── package.json

Tailwind CSSの導入

手順は基本的にTailswind CSSの公式ドキュメントに記載された通りです。

Tailwind CSS 公式ドキュメント

  1. npm or yarnでTailwind CSSをインストールする

ルートディレクトリで以下コマンドを実行

# Using npm
npm install tailwindcss

# Using Yarn
yarn add tailwindcss
  1. ./docs/.vuepress/styles/index.styl を作成
mkdir ./docs/.vuepress
mkdir ./docs/.vuepress/styles
touch ./docs/.vuepress/styles/index.styl

ちなみに、上記ディレクトリ構成でindex.stylを配置すると、vuepressが自動的にindex.stylを認識、デフォルトのCSSファイルとして扱ってくれます。

この時点でのディレクトリ構成は以下の通りです。

.
├── docs
    ├── README.md
    ├── .vuepress
        ├── styles
            ├── index.styl
├── package.json
  1. ./docs/.vuepress/styles/index.styl に Tailwind CSSの設定を追加

./docs/.vuepress/styles/index.styl に以下を追加

@tailwind base;
@tailwind components;
@tailwind utilities;

自分で全体に効かせるCSSを書きたいときは、
@tailwind components; と @tailwind utilities; の間に記述しましょう。

@tailwind base;
@tailwind components;

(ここにCSSを記述)

@tailwind utilities;
  1. ./docs/.vuepress/config.js を作成して設定を追加
touch ./docs/.vuepress/config.js

./docs/.vuepress/config.js の内容を以下の通りにします。

module.exports = {
  plugins: [
    // ...
    require('tailwindcss'),
    require('autoprefixer'),
    // ...
  ]
}

この時点でのディレクトリ構成は以下の通りです。

.
├── docs
    ├── README.md
    ├── .vuepress
        ├── styles
            ├── index.styl
        ├── config.js
├── package.json
  1. サーバーを再起動して確認

もしサーバーを起動している状態なら再起動しましょう。

これでVuePressでTailwind CSSが使えるようになりました!

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

Vue.jsでクリックマップ風を作ってみた

ウェブのアクセス解析にヒットマップ(クリックマップ)のようなものがビジュアル的に見やすくて、直感的なクリエイティブヒントを生み出すヒントとなったりします。
そのため、GAやAAのウェブページ上クリック率などを見られるもの、ウェブサイトを担当していたときに重宝していました。
しかし、それがアプリとなると、いろいろ難しくなります。
当然、解析ツール上にはデータが残っております。
そのデータを活用して、アプリの画面を画像で書き出して、その上に各箇所のクリック率を描画すれば、なんとなくそれらしきものが出来上がるではないかと思い、Vue.jsで実装してみました。

事前準備

data.json

htmlと同じ階層にdata.jsonとして置いておきます。
解析ツール上からデータをエクスポート、下記のように整形します。
* 原始的に、解析ツールからファイルを自動配信などして、マクロやGASで必要なフォーマットへ整うなど(GASでの対応
* セキュリティに許容できるなら、Googleのデータスタジオなどを使うと便利
* そもそも、解析ツールのAPIがもし叩けるなら、なによりも
{"update":"2020/08/18 10:21:46","data":[{"screen_name":"talk","item_name":"element1","ctr":0.2,"x_value":100,"y_value":200},
//中略
{"screen_name":"talk","item_name":"element100","ctr":0.3,"x_value":300,"y_value":400}]}

下地の画像ファイル

htmlと同じ階層にimgフォルダを作成し、{appScreen}.jpgという形式でファイルを置いておきます。

html/jsのコード

<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="Content-Type" content="text/html">
  <title>Click Map</title>
</head>
<body>
  <div id="app">
    <header>
      <h1>Click Map</h1>
      <select v-model="selected">
        <option value="default" disabled selected>Please select a view</option>
        <option v-for="option in options" v-bind:value="option.value">{{ option.name }}</option>
      </select>
    </header>
    <main>
      <img :src="'img/' + selected + '.jpg'" />
      <ul>
        <li v-for="item in items" v-if="item.screen_name === selected" :key="item.item_name" :class="item.screen_name" :style="{left:item.x_value + 'px', top:item.y_value + 'px', backgroundColor: 'rgb(' + item.ctr * maxRate + ',48,48)'}">
          <!--クリック率の最大値を255にして、各Rateをそれに換算してrgbのバックグラウンドカラーに当て込めば、ホットなクリック箇所が良しなに見えてきます。また各クリック率の表示場所はjsonデータ内のx_value,y_valueから位置を指定します。-->
          {{ item.ctr * 100 | decimalFormat }}%</li>
      </ul>
    </main>
  </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js"></script>
<script>
  new Vue({
    el: '#app',
    data: {
      items: '',
      selected: 'default',
      options: [{
          name: 'appScreen1', //テキトーにスクリーン名を
          value: 'appScreen1'
        },
        {
          name: 'appScreen2', //テキトーにスクリーン名を
          value: 'appScreen2'
        },
      ]
    },
    computed: {
      maxRate: function() { // クリックマップ風にクリック率とバックグラウンドカラーrgbの係数を生成
        var rateList = []
        for (let i = 0; i < this.items.length; i++) {
          if (this.items[i].screen_name == this.selected && this.items[i].ctr < 0.8) {
            rateList.push(this.items[i].ctr)
          }
        }
        var maxRate = 255 / Math.max.apply(0, rateList)
        return maxRate
      }
    },
    filters: { //指定したスクリーンの値を絞り込む
      decimalFormat: function(num) {
        return num.toFixed(2)
      },
      numFormat: function(num) {
        return num.toFixed(0)
      }
    },
    mounted: function() { //jsonファイルからデータを取得
      axios.get("data.json").then(response => (this.items = response.data.data))
    }
  });
</script>
<style>
/* cssは良しなに */
body{background:#999;padding:0;margin:0}
#app{width:960px;min-height:720px;margin:0 auto;background:#fff;position:relative;-moz-box-shadow:4px 4px 4px rgba(0,0,0,.4);-webkit-box-shadow:2px 2px 2px rgba(0,0,0,.4);-o-box-shadow:10px 10px 10px rgba(0,0,0,.4);-ms-box-shadow:10px 10px 10px rgba(0,0,0,.4);border-bottom-left-radius:4px;border-bottom-right-radius:4px}
header{padding:16px;border-bottom:solid 1px #efefef;margin-bottom:8px}
h1{width:120px;float:left;font-size:18px;text-align:center;margin-right:100px;color:#777}
select{height:48px;text-align:center;font-size:20px;padding:8px 16px;border:solid 2px #777;border-radius:4px;color:#777}
img{width:100%;margin:0 auto}
li{list-style:none;position:absolute;font-size:14px;padding:4px;border-radius:4px;color:#fff;-moz-box-shadow:4px 4px 4px rgba(0,0,0,.4);-webkit-box-shadow:2px 2px 2px rgba(0,0,0,.4);-o-box-shadow:10px 10px 10px rgba(0,0,0,.4);-ms-box-shadow:10px 10px 10px rgba(0,0,0,.4)}
</style>
</html>

出来上がりはこんな感じです。
sample.jpg

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

【Vue.Draggable】リストやdivタグ以外の要素で、ドラッグアンドドロップで並び替えする方法【Vuetify】

環境

$ vue --version
2.9.6

ライブラリの導入と、元のソースコード

今回必要なのは、Nuxt.jsでよくお世話になるUIライブラリVuetifyと、直感的に要素の並び替えができるVue.Draggableを利用させていただきます。

$ yarn add @nuxtjs/vuetify
$ yarn add vuedraggable
nuxt.config.js
  buildModules: [
    '@nuxtjs/vuetify',
  ],

並び替えしたい要素は、JSONplaceholderから取得した記事情報を落とし込んだカードコンポーネントになります。

posts/index.vue
<template>
  <v-app dark>
    <v-container>
      <v-row id="main-area">
        <v-col id="feed-template" class="col-4"  v-for="post in posts" :key=post.id>
          <v-card style="overflow:scroll; height:30vh;">
            <v-card-title>
              <nuxt-link :to="'/posts/' + post.id" class="feed-title">{{ post.title }}</nuxt-link>
            </v-card-title>
            <v-card-text class="feed-body">
              <ul class="feed-items">
                <li class="feed-list">
                    {{ post.body }}
                </li>
              </ul>
            </v-card-text>
          </v-card>
        </v-col>
      </v-row>
    </v-container>
  </v-app>
</template>

<script>
import axios from 'axios'
import draggable from 'vuedraggable'

export default {
  components: {
    draggable
  },
  data () {
    return {
      posts: []
    }
  },
  mounted () {
    axios.get('https://jsonplaceholder.typicode.com/posts').then(res => this.posts = res.data)
  }
}
</script>

さあ、並び替えする要素を囲んでみよう(失敗例)

<v-app>
  <draggable v-model="posts">
    <v-container>
      <v-row id="main-area">
        <v-col id="feed-template" class="col-4"  v-for="post in posts" :key=post.id>
  // 省略
  </draggable>
</v-app>

v-modelで、配列名を指定した<draggable>タグを囲んで使う。
ただ、これだと要素全体が移動対象になってしまう。。。

<v-app>
  <v-container>
    <v-row id="main-area">
      <draggable v-model="posts" draggable=".item">
        <v-col id="feed-template" class="col-4"  v-for="post in posts" :key=post.id>
  // 省略
      </draggable>
    </v-row>
  </v-container>
</v-app>

これでもダメ。
<draggable>タグの位置をずらしたり、新たな属性を追加したり、、、、試行錯誤した結果。

https://mebee.info/2019/12/03/post-4508/

この記事より、こんな書き方が。

<div id="app">
  <draggable v-model="list" element="ul" :options="{animation:500}">
    <li v-for="item in list" :key="item.id">{{ item.name }}</li>
  </draggable>
</div>

ほお!元あったタグ名をelement属性に設定しておく必要があるんですね!

カード毎に要素の入れ替えを行う方法(成功例)

つまり、元あったタグを、<draggable>タグで置き換えるイメージで書いていく。
最終的なソースコードはこちら。

posts/index.vue
<template>
  <v-app dark>
    <v-container>
      <draggable v-model="posts" element="v-row">
        <v-col class="col-4 feed-template" v-for="post in posts" :key=post.id>
          <v-card style="overflow:scroll; height:30vh;">
            <v-card-title>
              <nuxt-link :to="'/posts/' + post.id" class="feed-title">{{ post.title }}</nuxt-link>
            </v-card-title>
            <v-card-text class="feed-body">
              <ul class="feed-items">
                <li class="feed-list">
                  {{ post.body }}
                </li>
              </ul>
            </v-card-text>
          </v-card>
        </v-col>
      </draggable>
    </v-container>
  </v-app>
</template>

<script>
import axios from 'axios'
import draggable from 'vuedraggable'

export default {
  components: {
    draggable
  },
  data () {
    return {
      posts: []
    }
  },
  mounted () {
    axios.get('https://jsonplaceholder.typicode.com/posts').then(res => this.posts = res.data)
  }
}
</script>

カードコンポーネントが、自由に移動できるようになった!!

追記

サーバーを立ち上げると、こんな警告が。

 WARN  Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props

どうやら「elementは廃止されるので、tag属性にするように」とのこと。

      <draggable v-model="posts" tag="v-row">

これだけでおk

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

【Nuxt.js】Nuxt文法編:layout

? この記事はWP専用です
https://wp.me/pc9NHC-A4

前置き

HeaderやFooter, sidebar(sidemenu)など
どのページでも固定で表示させたいコンポーネントってありますよね❓
そんな時に便利なlayoutを解説していきます??‍♀️

公式サイトを元に順番に説明しています?
https://ja.nuxtjs.org/guides/directory-structure/layouts/

layout

基本的な使い方

HeaderとFooterを固定で表示させてみます?

directory
layouts/
--| default.vue

pages/
--| index.vue
--| sample.vue

Frame 5.png

コード

HeaderやFooterはテキストだけなので省きます?
また、CSSも省略します。

ページコンポーネント(pages内のindex.vueなど)の
レンダリングはこちらの3行です?

layouts/default.vue
<template>
 <Nuxt />
</template>

pagesが表示される位置より上にHeader,
下にFooterを表示させましょう?

layouts/default.vue
<template>
 <div class="layout">
   <Header />
   <nuxt />
   <Footer />
   </div>
 </div>
</template>

<script>
import Header from '~/components/Header.vue'
import Footer from '~/components/Footer.vue'

export default {
 components: {
   Header,
   Footer,
 }
}
</script>

使用するlayoutの指定は必要ありません。
layouts/default.vueがデフォルトで適応されます??

index.vue
<template>
 <div class="page">
   <p>sample</p>
 </div>
</template>

index.vueとsample.vueを見てみてください?
どちらのページでもHeaderとFooterが固定されているはずです?

ページ内コンポーネントのリンクは
router-linkまたはnuxt-linkを使用します??

⬇️解説記事はこちら
https://wp.me/pc9NHC-f5

index.vueにこちらを貼り付けてみると
Headerなどは固定されたまま
sample.vueに遷移することができます?
リンク

カスタムレイアウト

index.vueでは
layouts/default.vue(Header, Footerを使用)にして
blog内のposts.vueでは
layouts/blog.vue(ブログ用ナビゲーションバー)を表示させてみます?

? 続きはWPでご覧ください?
https://wp.me/pc9NHC-A4

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

【Vue.js】src/assetsフォルダに画像ファイルを設置し、v-forで画像を表示する

src/assets/imagesに画像ファイルを置く

src/
 └ assets/
     └ images/
        ├ 1.jpg
        ├ 2.jpg
        └ 3.jpg

Vueコンポーネント

Sample.vue
<template>
   <div v-for="(image,i) in images" :key="i">
       <img :src="image" />
   </div>
</template>

<script>
export default {
  data() {
    return {
      images: [
        require('@/assets/images/1.jpg'),
        require('@/assets/images/2.jpg'),
        require('@/assets/images/3.jpg'),
      ],
    }
  }
}
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでReact公式チュートリアルの三目並べを作ってみた

作ったもの

React の公式チュートリアルにある三目並べを Vue.js で作りました。
完成品はこちら。

環境

Vue 2.6.11
Vue CLI 3.3.0
npm: 6.1.14
Node.js: 12.18.0

構成

Vue CLI を使って単一ファイルコンポーネント化してます。

root
 ├ public
 │ └ index.html
 └ src
   ├ components
   │ ├ Bord.vue
   │ ├ Game.vue
   │ └ Square.vue
   ├ App.vue
   └ main.js

コード

main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
  <div id="app">
    <Game />
  </div>
</template>

<script>
import Game from "@/components/Game";

export default {
  components: {
    Game
  }
}
</script>

<style>
</style>
Game.vue
<template>
  <div class="game">
    <div class="game-board">
      <Board
        :squares="current.squares"
        :handleClick="handleClick"
      />
    </div>
    <div class="game-info">
      <div>{{ status }}</div>
      <ol>
        <li :key="index" v-for="(move, index) in moves">
          <button @click="jumpTo(index)">
            {{ move }}
          </button>
        </li>
      </ol>
    </div>
  </div>
</template>

<script>
  import Board from "@/components/Board";

  export default {
    components: {
      Board
    },
    data() {
      return {
        history: [{
          squares: Array(9).fill(null)
        }],
        stepNumber: 0,
        xIsNext: true,
        status: '',
        current: {},
        moves: [],
      }
    },
    created() {
      this.render()
    },
    methods: {
      calculateWinner(squares) {
        const lines = [
          [0, 1, 2],
          [3, 4, 5],
          [6, 7, 8],
          [0, 3, 6],
          [1, 4, 7],
          [2, 5, 8],
          [0, 4, 8],
          [2, 4, 6],
        ];
        for (let i = 0; i < lines.length; i++) {
          const [a, b, c] = lines[i];
          if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
            return squares[a];
          }
        }
        return null;
      },
      jumpTo(step) {
        this.stepNumber = step
        this.xIsNext = (step % 2) === 0

        this.render()
      },
      handleClick(i) {
        const history = this.history.slice(0, this.stepNumber + 1)
        const current = history[history.length - 1]
        const squares = current.squares.slice()
        if (this.calculateWinner(squares) || squares[i]) {
          return;
        }
        squares[i] = this.xIsNext ? 'X' : 'O'
        this.history = history.concat([
          {
            squares: squares
          }
        ])
        this.stepNumber = history.length
        this.xIsNext = !this.xIsNext

        this.render()
      },
      render() {
        const history = this.history;
        this.current = history[this.stepNumber]
        const winner = this.calculateWinner(this.current.squares)

        this.moves = history.map((step, move) => {
          const desc = move ?
            'Go to move #' + move :
            'Go to game start'
          return desc
        })

        if (winner) {
          this.status = 'Winner: ' + winner
        } else {
          this.status = 'Next player: ' + (this.xIsNext ? 'X' : 'O')
        }
      }
    }
  }
</script>

<style scoped>
  body {
    font: 14px "Century Gothic", Futura, sans-serif;
    margin: 20px;
  }

  ol, ul {
    padding-left: 30px;
  }

  .kbd-navigation .square:focus {
    background: #ddd;
  }

  .game {
    display: flex;
    flex-direction: row;
  }

  .game-info {
    margin-left: 20px;
  }
</style>
Bord.vue
<template>
  <div>
    <div class="board-row">
      <Square :value="squares[0]" :index="0" :handleClick="handleClick"/>
      <Square :value="squares[1]" :index="1" :handleClick="handleClick"/>
      <Square :value="squares[2]" :index="2" :handleClick="handleClick"/>
    </div>
    <div class="board-row">
      <Square :value="squares[3]" :index="3" :handleClick="handleClick"/>
      <Square :value="squares[4]" :index="4" :handleClick="handleClick"/>
      <Square :value="squares[5]" :index="5" :handleClick="handleClick"/>
    </div>
    <div class="board-row">
      <Square :value="squares[6]" :index="6" :handleClick="handleClick"/>
      <Square :value="squares[7]" :index="7" :handleClick="handleClick"/>
      <Square :value="squares[8]" :index="8" :handleClick="handleClick"/>
    </div>
  </div>
</template>

<script>
  import Square from "@/components/Square";

  export default {
    props: {
      msg: String,
      squares: Array,
      handleClick: Function,
    },
    components: {
      Square
    },
  }
</script>

<style scoped>
  body {
    font: 14px "Century Gothic", Futura, sans-serif;
    margin: 20px;
  }

  ol, ul {
    padding-left: 30px;
  }

  .board-row:after {
    clear: both;
    content: "";
    display: table;
  }

  .kbd-navigation .square:focus {
    background: #ddd;
  }
</style>
Square.vue
<template>
  <button class="square" @click="handleClick(index)">
    {{ value }}
  </button>
</template>0

<script>
  export default {
    props: {
      value: String,
      handleClick: Function,
      index: Number
    }
  }
</script>

<style scoped>
  .square {
    background: #fff;
    border: 1px solid #999;
    float: left;
    font-size: 24px;
    font-weight: bold;
    line-height: 34px;
    height: 34px;
    margin-right: -1px;
    margin-top: -1px;
    padding: 0;
    text-align: center;
    width: 34px;
  }

  .square:focus {
    outline: none;
  }
</style>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む