- 投稿日:2020-08-18T23:33:23+09:00
VuePressにTailwind CSSを導入する方法
最低限トップページをVuePressで表示させる
VuePressでトップページを表示させる手順は公式ドキュメントに沿って行ってください。
この時点でのディレクトリ構成は以下の通りです。
. ├── docs │ ├── README.md ├── package.jsonTailwind CSSの導入
手順は基本的にTailswind CSSの公式ドキュメントに記載された通りです。
- npm or yarnでTailwind CSSをインストールする
ルートディレクトリで以下コマンドを実行
# Using npm npm install tailwindcss # Using Yarn yarn add tailwindcss
- ./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
- ./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;
- ./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
- サーバーを再起動して確認
もしサーバーを起動している状態なら再起動しましょう。
これでVuePressでTailwind CSSが使えるようになりました!
- 投稿日:2020-08-18T20:09:05+09:00
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>
- 投稿日:2020-08-18T18:14:37+09:00
【Vue.Draggable】リストやdivタグ以外の要素で、ドラッグアンドドロップで並び替えする方法【Vuetify】
環境
$ vue --version 2.9.6ライブラリの導入と、元のソースコード
今回必要なのは、Nuxt.jsでよくお世話になるUIライブラリVuetifyと、直感的に要素の並び替えができるVue.Draggableを利用させていただきます。
$ yarn add @nuxtjs/vuetify $ yarn add vuedraggablenuxt.config.jsbuildModules: [ '@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>タグ
の位置をずらしたり、新たな属性を追加したり、、、、試行錯誤した結果。この記事より、こんな書き方が。
<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
- 投稿日:2020-08-18T17:00:53+09:00
【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を固定で表示させてみます?
directorylayouts/ --| default.vue pages/ --| index.vue --| sample.vueコード
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-f5index.vueにこちらを貼り付けてみると
Headerなどは固定されたまま
sample.vueに遷移することができます?
リンクカスタムレイアウト
index.vueでは
layouts/default.vue(Header, Footerを使用)にして
blog内のposts.vueでは
layouts/blog.vue(ブログ用ナビゲーションバー)を表示させてみます?? 続きはWPでご覧ください?
https://wp.me/pc9NHC-A4
- 投稿日:2020-08-18T14:24:26+09:00
【Vue.js】src/assetsフォルダに画像ファイルを設置し、v-forで画像を表示する
src/assets/imagesに画像ファイルを置く
src/ └ assets/ └ images/ ├ 1.jpg ├ 2.jpg └ 3.jpgVueコンポーネント
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>
- 投稿日:2020-08-18T10:09:58+09:00
Vue.jsでReact公式チュートリアルの三目並べを作ってみた
作ったもの
React の公式チュートリアルにある三目並べを Vue.js で作りました。
完成品はこちら。— ikeo (@ikeo256) August 18, 2020環境
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.jsimport 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>