GWの休日を使って、Fitbitの文字盤(Clock Face)を自作してみました。
文字盤の開発は、HTMLやJSのような文法で、シミュレーターやリファレンスも整備されているので比較的簡単に感じました。
参照した情報をまとめつつ、開発者アカウントの登録や開発者用ブリッジ接続の設定でつまづいたポイントについて書き残します。
[[toc]]
リモートワークで一日に一歩も外に出ない日が平気である生活をしていたのですが、
このままでは不健康まっしぐらと感じたので、最近は意識的に一定の歩数(9000歩)を歩くことを日課にしています。
その日課の継続ができるように、目標達成までの所要時間が予想できるような文字盤作ってみました。
https://gallery.fitbit.com/details/ab76e03b-4b2e-4052-bb1b-2563f2fe9b62
以下の記事を参考にしながら開発手順の大枠を掴むことにしました。
しかし、記事内で触れられている "Fitbit Studio" は April 20, 2023
にクローズされており、一部手順が異なっていました。以下、2023/5月時点での自分が実施した準備作業の備忘録を残します。
今回の開発を始める前の自分の状態は以下です。
FitbitのDeveloperサイトのガイド"Getting Started"の手順を参考に進めます。
https://dev.fitbit.com/getting-started/
このガイドには説明がなかったですが、開発をすすめるためには自分のFitbitアカウントを開発者アカウントとして登録しておく必要がありました。
上部のメニューバーから開発者向けページを開くと、アカウントが未登録の場合「enroll」のボタンが表示されました。
このとき、"Register An App"と"Gallery App Manager"の2つのリンクがあり、どちらも開発者向けページですが、自分の場合は"Gallery App Manager"の方で「enroll」ボタンを押すまでFitbitシミュレーター等がうまく動作しませんでした。(You must be enrolled as a developer to use this application.
というエラーメッセージが出た。)
"Register An App"の方でログイン等しても開発者アカウントのenrollとはならないようです。
(Clock Face の作成にあたってはこちらのページでの操作は特に必要なさそうでした。)
ローカル環境構築は、ガイドの通り進めることができました。
https://dev.fitbit.com/getting-started/
概要
npx create-fitbit-app my-first-clock
)npx fitbit
でfitbitのCLIに入ってから bi
コマンド)実機での動作確認方法も"Getting Started"のガイド内に記載があります。
If you're using a Fitbit device, you need to enable the Developer Bridge. On the watch, go to Settings and tap Developer Bridge, then wait until it says Connected to Server.
Developer Bridge(開発者用ブリッジ接続)の有効化には2つの操作が必要でした。
また、これらのボタンが表示されるようにするためには、前述の開発者アカウントの登録が先に必要でした。 開発者アカウントの登録ができたあと、アプリからログアウトして再ログインすることでボタンが表示されるようになりました。
※ 参考にした記事: Fitbit Ionic の設定画面に "開発者向けメニュー" を出す方法 - Qiita
ちなみに"開発者用ブリッジ接続"を有効にしても、開発していると十数分ごとくらいで接続が切れてしまい、都度再有効化する必要がありました…。
アプリとFitbitデバイスの両方で開発者用ブリッジ接続を有効にした状態で、fitbitのCLIに bi
もしくは install
を入力すると、Fitbitデバイスに開発中の文字盤がインストールされて動作確認ができました。
ロジック関連
表示関連
Gallery App Manager からビルドしたファイルアップロードや説明項目の登録ができました。
https://gam.fitbit.com/apps
手順は最初にも紹介した下記の記事が参考になりました。
項目を入力すると審査に提出することができ、審査を通れば一般に公開することができるようです。
環境構築(というよりも開発者用ブリッジ接続の有効化周り)で少し苦戦しましたが、文字盤の作成自体はとても簡単で、また違う用途の文字盤やアプリを作ってみたいと思いました。
APIドキュメントをみると、FetchやPOSTができるメソッドも用意されているようなので、いろいろなことができそうです。
https://dev.fitbit.com/build/reference/companion-api/fetch/
最近、M5StickCやラズパイも調達したので、組み合わせてなにか面白いことできないかなーと考え中です。この記事はここまで、ではまた〜!
]]>こんにちは。
今回はP5.jsをつかってサウンドビジュアライザーを作成してみました。
P5.jsは、プログラミングで絵を描くためによく使われているProcessingという言語のJavaScript版ライブラリになります。
以前からビジュアライザーを作りに興味はあったのですが、今回はコチラの記事
Adobe製品を使わない"デザイナー"?「ビジュアルコーダー」が考える、自己満足で終わらないWebデザインとは
に触発されまして、Web上にビジュアライザーを作れたら面白いと思いやってみました!!
P5.jsはオーディオを扱うにも便利ですが、ビートの検知で苦労したところがありました。
かなり荒い調整なので参考になるかどうかはわかりませんが、簡単に記事に残しておこうと思います。
[[toc]]
イメージ画像
https://izm51.github.io/visual-coding/visualizer1-pastel/
※ブラウザの設定でマイクを許可し、画面をクリックすると開始します。
デモ動画
背景はパステルカラーがくるくると回っていて、スネア(の音域)に合わせて色が変わります。
そしてキックに合わせて中央の丸が大きくなり、そこから円や四角が近づいてくるようになっています。
下辺にはスペクトラムも設置しました。
始めは何となくで決めた色でしたが、パステルカラーとホワイトのおかげでKawaii感じになりました。
P5.jsには描画だけでなく、Audioに関する便利な関数もたくさんありました。
おかげでマイクの取得やfftなどは非常に簡単でした!
しかしビートの検知には若干苦労しました。
P5.js自体にもPeakDetectという関数があったのですが、ちょっと反応がとがりすぎという印象でした。
(触り始めのときの感想なので、チューニング次第では普通に使えるかも?)
そこで今回は、下記記事を参考に簡単なBeatDetectorも実装してみました。
https://www.airtightinteractive.com/2013/10/making-audio-reactive-visuals/
※テンポを計算するものではなく、ある周波数領域の音量が大きくなったのを検知するものです。
class BeatDetect {
constructor(mode = 'kick', freq2) {
if (!isNaN(freq2) && !isNaN(mode)) {
this.freq1 = mode;
this.freq2 = freq2;
} else {
if (mode == 'snare') {
this.freq1 = 2000;
this.freq2 = 6000;
} else if (mode == 'male') {
this.freq1 = 200;
this.freq2 = 2000;
} else {
// mode == "kick"
this.freq1 = 20;
this.freq2 = 80;
}
}
this.time = 0;
this.threshold = 0;
this.minThreshold = 0;
this.decayRate = 0.01;
this.minThresholdRate = 0.8;
this.holdTime = 45;
this.marginThresholdTime = 10;
this.marginThreshold = 0.06;
}
update(fft) {
const e = fft.getEnergy(this.freq1, this.freq2);
const level = e / 255.0 || 0.0;
let isBeat = false;
if (level > this.threshold && level > this.minThreshold) {
this.threshold = level * 1.05;
this.minThreshold = max(this.minThreshold, level * this.minThresholdRate);
if (this.time > this.marginThresholdTime) {
isBeat = true;
}
this.time = 0;
} else {
if (this.time == this.marginThresholdTime) {
this.threshold -= this.marginThreshold;
}
this.time += 1;
if (this.time > this.holdTime) {
this.threshold -= this.decayRate;
}
}
return { threshold: this.threshold, level: level, isBeat: isBeat };
}
}
全体のコードはGitHubにあります。
課題としては、
という感じでした。
まずは曲のリズムに良い感じに反応する周波数帯を探しました。
自分がよく聞くのはMathRock(バンド系)とFutureBounce(クラブ系)、あとはアイドル系です。
なので、今回はその3ジャンルで探りました。
ビートを取るためであれば低音域が最適かと思っていましたが、キックにメロディがかぶりまくることもあれば、低音域で全く音が鳴っていないこともあり、低音だけでは絵の変化に面白みがないパターンがありました。
いろいろ試した結果、バスドラの音域(20Hz~80Hz)とスネアやクラップらへんの音域(2000Hz~6000Hz)が曲への反応がよさそうだったので、この2領域を使うことにしてみました。
次に、一回のバスドラで何回もビートが検知されたり、曲が静かになって音量に変化がない時に連続で反応してしまうことがあり、そこに対処しました。
始めはthreshold、decayRate、holdTimeの変数だけでちょうどいい調整を探していたのですが、曲によってどうにも反応が変わってしまい、いろいろと手探りで変数を増やしました。
具体的には下記の部分になります。
if (level > this.threshold && level > this.minThreshold) {
this.threshold = level * 1.05;
this.minThreshold = max(this.minThreshold, level * this.minThresholdRate);
if (this.time > this.marginThresholdTime) {
isBeat = true;
}
if (this.time > this.marginThresholdTime)
で連続の検知を防止し、
this.minThreshold = max(this.minThreshold, level * this.minThresholdRate);
で曲中の静かな部分ではビートを検知しすぎないようにしました。
今回のビート検知の仕組みでは少し前に鳴った音量がビート検知の閾値になるため、例えばバンド系の曲でバスドラがダブルで踏まれているときに、二つ目が少し弱かったりすると反応が悪くなってしまいました。
そこで、無理やり感はありますがビート検知から少し時間が経過したら、holdTimeより少し早いタイミングでthresholdを少しだけガクッと下げるようにしました。
if (this.time == this.marginThresholdTime) {
this.threshold -= this.marginThreshold;
} // this.marginThreshold = 0.06
なにかもっとちゃんとしたチューニングはありそうですが、今回はいい感じの反応になったので一旦これで満足しています。
という訳で、今回は初めてのビジュアライザーを作成してみました。
思い返せば中学生くらいの頃から音に合わせて動くプロジェクションマッピングとか、高校時代もインスタレーションに興味がありましたがなんだか手が出せずにいました。
少しずつWebやオーディオの知識をためてきたおかげで、気づいたら昔憧れていた領域に手を出せるようになっていました。
Processingなら気軽にビジュアルコーディングができます!
今後もまた面白いものを作っていきたいと思います。
ではまた。
]]>最近InoreaderというRSSリーダーで情報収集をするようになり、自分のブログもフィードに対応させたいと思うようになりました。
そこでこの記事では、"@nuxtjs/feed"を使ってブログの更新情報をフィードで提供する手順を紹介します。
[[toc]]
まず、今回使用するプラグイン @nuxtjs/feedをインストールします。
$ npm install @nuxtjs/feed
nuxt.config.js
のmodulesに追加します。
modules: [
'@nuxtjs/feed' // 追加
],
そして、nuxt.config.js
にfeedの設定を記入するセクションを追加します。
modules: [
'@nuxtjs/feed'
],
// ここから追加
feed: [
// この中に設定を記述していく
]
Contentfulから記事一覧を取得するために、~/plugins/contentful
を読み込みます。
nuxt.config.js
の最初のほうに次の行を追加します。
const client = require("./plugins/contentful");
feedの設定項目を記述していきます。
まず、フィードを書きだすURLを相対パスで設定します。
一般的には、"/feed", "/feed/", "/feed.xml"などが多いようです。
feed: [
{
path: '/feed' // 追加
}
]
次に、フィードに記事の一覧が登録されるように設定していきます。
当ブログの設定を例として紹介しますので、適宜書き換えて参考にしてください!
feed: [
{
path: '/feed',
// ここから追加
async create(feed) {
feed.options = {
title: "イズミログ",
link: "https://izm51.com/feed", // 上のpathで設定したものと対応するように
description: "イズミログ - フィード"
};
// 記事を取得
await client
.getEntries({
content_type: "post",
order: "-sys.createdAt"
})
.then(entries => {
entries.items.forEach(post => {
feed.addItem({
title: post.fields.title,
id: `https://izm51.com/posts/${post.fields.slug}/`, // 記事のURL
link: `https://izm51.com/posts/${post.fields.slug}/`, // 記事のURL
description: post.fields.description,
content: post.fields.content,
date: post.fields.update ? new Date(post.fields.update) : new Date(post.sys.createdAt), // 記事の最終更新日
published: new Date(post.sys.createdAt), // 記事の公開日
});
});
feed.addCategory("blog");
feed.addContributor({
name: "Izm51",
link: "https://izm51.com/"
});
});
},
}
]
dateについて補足しますと、このブログではContentfulのContent modelにupdateという日付型の入力欄を設置し、記事を修正したときに手動で書き換える形式にしています。
ちょっとした修正だけで毎回最終更新日を更新したくないため、このようにしています。
最後に、cacheTimeとフィードのタイプを設定します。
feed: [
{
path: '/feed',
async create(feed) { /* 略 */ },
// ここから追加
cacheTime: 1000 * 60 * 15,
type: "atom1"
}
]
フィードのタイプは"rss2", "atom1", "json1"の3種類が設定できます。
これらのタイプとはフィードの規格のことですが、"rss2"か"atom1"に設定しておけばたいていのRSSリーダに対応しているかと思います。
ここまでで一応フィードの設定は完了ですが、このままでは記事の内容がMarkdown形式のまま配信されてしまいます。
そこで、markdown-itで記事内容ををHTML形式に展開してから登録するように設定していきます。
nuxt.config.js
の最初の方に次の一行を追加して、markdown-itを読み込みます。
import MarkdownIt from 'markdown-it';
feedのcreateの中で、markdown-itを使用するための準備をしておきます。
async create(feed) {
feed.options = {
// 略
};
// 追加
const md = new MarkdownIt({
html: true,
typography: true,
})
feed.addItemの部分で、記事の内容をmarkdown-itでHTMLにレンダリングしたものをcontentに登録するように設定します。
feed.addItem({
// 略
content: md.render(post.fields.content), // 変更
記事内にある画像とは別にサムネイル画像を設定している場合は、このように記事の最初に画像を挿入してしまうのもおすすめです。
content: `<img src="https:${post.fields.image.fields.file.url}?w=1080">` + md.render(post.fields.content),
最終的な完成版は次のようになりました。
feed: [
{
path: '/feed',
async create(feed) {
feed.options = {
title: "イズミログ",
link: "https://izm51.com/feed",
description: "イズミログ - フィード"
};
const md = new MarkdownIt({
html: true,
typography: true,
})
await client
.getEntries({
content_type: "post",
order: "-sys.createdAt"
})
.then(entries => {
entries.items.forEach(post => {
feed.addItem({
title: post.fields.title,
id: `https://izm51.com/posts/${post.fields.slug}/`,
link: `https://izm51.com/posts/${post.fields.slug}/`,
description: post.fields.description,
content: `<img src="https:${post.fields.image.fields.file.url}?w=1080">` + md.render(post.fields.content),
date: post.fields.update ? new Date(post.fields.update) : new Date(post.sys.createdAt),
published: new Date(post.sys.createdAt),
});
});
feed.addCategory("blog");
feed.addContributor({
name: "Izm51",
link: "https://izm51.com/"
});
});
},
cacheTime: 1000 * 60 * 15,
type: "atom1"
}
]
$ npm run generate
することでフィードが生成されるようにできました!
あとはトップページなどにフィードへのリンクを追加すれば設定完了です。
これで、普段使用しているRSSリーダーでも自分のブログが見られるようになりました!
https://izm51.com/feed
ではまた。
【参考】
generate時にエラーが出たときの対処
javascript - TypeError: Cannot add module namespace property '_nuxtConfigFile' to nuxt.config.js with NUXT 2.4.5 - Stack Overflow
]]>
nuxt.config.js
のmodule.exports = {}
をexport default { }
に変更する
こんにちは、イズミです。
今回は、Googleフォトのアルバム共有リンクから、アルバムに含まれる画像の埋め込み用タグを一気に生成するツールを作成しました。
リンク:Google Photos Embed Link Generator
この記事には、このツールの使い方と、作成時に参考にしたことなどを残しておきます。
[[toc]]
このブログでは、ブログ記事の管理に Contentful を使用しています。
Contentfulは、 無料プランの場合に保存できるレコード数が5000まで という制限があります。
※レコード数は、記事などの"エントリー数"と、アップロードした画像などの"アセット数"の合計
そこで本ブログではレコード数を節約するため、 画像サーバーとしてGoogleフォト を使用しています。
(過去記事:ブログの画像サーバとしてGoogleフォトを使う
しかし、この過去記事で紹介した方法では画像一枚一枚に手間がかかってしまい面倒になったため、アルバムから一気に 埋め込み用コードを生成するWebツール を作成してみました。
使い方はいたって簡単で、Googleフォト上でアルバムを作成し、その共有URLを張り付けるだけです。
アルバムの作成方法から、実際に手順を追って説明していきます。
まず、ブログに使用したい画像たちをGoogleフォトにアップロードします
アップロードが完了すると、左下にモーダルが出現します。
ここで、「アルバムに追加」のボタンを押して、先ほどアップロードした画像たちのアルバムを作成します。
(「共有アルバム」のボタンを押すと手順を若干飛ばせます。お好みで)
「新しいアルバム」を押してアルバムを作成します。
既存のアルバムに追加しても問題ないです。
タイトルを適当に入力し、左上の☑ボタンを押します。
右上の共有ボタンを押します。
「リンクを作成」ボタンを押します。
少し待つと共有用リンクが作成されるので、それをコピーします。
Google Photos Embed Link Generator を開きます。
入力欄に先ほどコピーしたアルバムの共有リンクを張り付けて、「GENERATE」ボタンを押します。
(リンクのURLではなく、https://photos.app.goo.gl/
以後のIDのみ張り付けても動作します。)
するとこのようにアルバム内の画像一覧が表示されます。
オプション欄で次のような設定をできるようにしています。
画像自体をクリックするか、各画像の右下の「Copy!」ボタンを押すと、埋め込みコードがクリップボードにコピーされます。
これをブログ編集画面等の埋め込みたい箇所に張り付ければ完了です!
埋め込みコード作成後の画面はそのままブックマークしておいて、作業を中断してまた別のタイミングで開くこともできます。
https://google-photos-embed-generator.glitch.me/***
の***の部分にアルバムのIDを入力して直接開くことも可能です。
※利用上の注意
今回のツール作成では、以下の記事を大いに参考にしました。
How I used Google Photos to host my website picture gallery
Googleフォトのアルバムから貼り付け用のURLを取得するところまでは上の記事の内容をそのまま使用しています。
この記事のサンプルでは画像の一覧をjsonのようにただ並べて取得するだけだったので、ブログ作成に利用しやすいようにアレンジしてみました。
今回はexpressで作成しましたが、慣れていなかったのでたぶん簡単そうなところでもつまずいてしまいました。
最後に、今回作成したツールのソースコードを一応貼っておきます。
リンク:Glitch
そもそも根幹部分は僕の製作ではないですが、ご自由に参考にしてください!
もし使ってみて感想や要望があれば、ぜひコメント欄まで!!
あと最近、東京の隠れフォトスポットを探すブログを始めたので、興味のある方は見てみてください!
Tokyizm - 「ココで撮りたい」を見つけるWebメディア -
ではまたー!
]]>10/15~18に大阪で開催された国際会議 “2019 IEEE 8th GCCE” でポスター発表してきました。
今回はそのあっさり目の感想と観光の写真を記事にしたいと思います。
今回の学会参加を通して僕は、学会とは “情報交換の場” だと感じました。
(経験者の方々にとってはすごく当然のことだと思いますが。)
学会初参加の僕にとって、学会は雰囲気の想像できない未知の場所で不安もありました。上手く発表できるのか、英語の質疑に対応できるのか、など心配事も多かったです。
実際に参加してみると、今回僕は「ポスター発表」だったこともあり、“発表”というよりは “コミュニケーション” という感じで、あまり緊張はなかったです。会話を通して自分の研究内容を知ってもらい、様々な視点から意見をもらうことができました。
今回の学会では、「口頭発表」、「ポスター発表」、「デモ発表」の3種類の発表形式がありました。
「口頭発表」は会議室のような部屋の前にスライドを表示して発表する “The学会”感のある形式、「ポスター発表」は研究内容をまとめたポスターの前に立ち、興味を持ってくれた人に説明する形式、「デモ発表」はポスターに加えてPCやハードウェアを持参し、実際にデモが行える形式です。
「口頭発表」は発表15分+質疑5分でしたが、「ポスター」「デモ」は1時間40分の長い発表時間がありました。この 長い時間で色々な人と意見交換ができる ことは、「ポスター発表」形式の良い点だと感じました。今回は専門の近い人だけでなく、分野の異なる人にも発表を聞いてもらうことができたので、専門的な意見だけでなく素朴な疑問や感想もいただくことができました。
学会期間中には観光のバスツアーやヒルトンホテルでの夕食会のようなイベントもあり、全体を通して とても楽しかった です。刺激ばかりで、今後のモチベーションにもつながる良い経験となりました。
ここからは大阪観光の写真。
2日目の夜に開催されたバスツアーでは、夜の大阪城と梅田スカイビルを観光しました。
梅田スカイビル展望台からの景色。
梅田スカイビル・屋上の空中庭園展望台からの下りエスカレーター。
梅田スカイビル(下から)
夜の大阪駅。蔦屋書店が大きくて便利そう。
大阪駅内で撮影。良いカーブ。
大阪駅から宿までの帰り道で撮影。
最終日は発表と閉会式の後、通天閣など大阪の名所を巡り、グルメも堪能。
学会も観光も非常に有意義な大阪旅でした。
海外で開催の学会にも挑戦できるように頑張りたい。
]]>
今回は、スマホ版ブラウザのホームページに使えそうな、シンプルなブックマークリストサイトを作ってみました。
ログイン不要で、自由に設定したブックマークリストを表示できます。
見た目はダークでミニマル。落ち着いたデザインを目指しました。
リンク: https://izm51.github.io/home/
[[toc]]
僕はAndroidでChromeを使っています。
Android版Chromeでは、新しいタブを開くとChromeが自動で判断した「よく使うサイト」と「おすすめの記事」が表示されます。
しかしこの「新しいタブ」ページはカスタマイズができず、「よく使うサイト」も自由に設定することができませんでした。
僕はシンプルにブックマークさえ表示できればよかったので、それができるサイトを探しましたが見つかりませんでした。(どこかには絶対あると思うが)
というわけで探すより作ってしまった方が早いと思ったので、今回はシンプルなブックマークリストサイトを作ってみました。
しかし結果として、新しいタブに表示することはできませんでした。
このサイトをChromeのホームページに設定することで新しいタブを開いた際に表示されるページを変更できると思っていました。
しかし、ホームページを変更しても新しいタブページは変更されませんでした(泣)
ホームページのデフォルトはchrome://newtab/でしたが、ホームページ=新しいタブで表示されるページ というわけではありませんでした。
せっかく作成したので、Chromeのホームボタンから飛べるブックマークリストとして使うことにしました。
Chromeのブックマークは表示するまでに2回タップ操作(メニューボタン→ブックマーク)が必要だったので、それが1回に短縮できて少し便利になりました。
リポジトリ: https://github.com/izm51/home
シンプルにHTMLと素のJavascriptのみ。
(スタイルはSCSSで書いてます。)
以下作成時のメモを記録します。
faviconを取得するGoogleのAPIを使用しました。
<img src="http://www.google.com/s2/favicons?domain=[URL]">
http://www.google.com/s2/favicons?domain=[URL]
の [URL]
にアイコンを取得したいサイトのURLを指定するだけで画像を取得できます。
しかし、サイズが16x16で非常に小さいのが難点です。
touch iconなどを取得できる方法はあるんでしょうか。探し中です
今回のサイトでは、ブックマークのリストをクエリにしてURL内に保管しています。
クエリは ?[サイト名]=[URL]&[サイト名]=[URL]...
という形にしています。
素のJavascriptでのクエリの取得は初だったので、以下を参考にしました。
参考: JavaScriptでURLクエリを取得する - Qiita
参考記事内では
var queryArr = queryStr.split('=');
queries[queryArr[0]] = queryArr[1];
という形でkeyとvalueを分けていましたが、今回のvalueとなるURLには =
が含まれる場合もあるので以下のように変更しました。(14,15行目)
//クエリの取得
function getUrlQueries() {
var queryStr = window.location.search.slice(1); // 文頭?を除外
queries = {};
// クエリがない場合は空のオブジェクトを返す
if (!queryStr) {
return queries;
}
// クエリ文字列を & で分割して処理
queryStr.split('&').forEach(function(queryStr) {
// = で分割してkey,valueをオブジェクトに格納
var eqIndex = queryStr.indexOf('=')
queries[queryStr.slice(0, eqIndex)] = queryStr.slice(eqIndex+1)
});
return queries;
}
クエリから取得したブックマークのリストをもとにリンクを作成します。
今回はひたすらに要素を追加していくだけです。
一気に複数の要素を追加する際は、一度 DocumentFragment
に集めた後で追加することでパフォーマンスが向上するようです。
参考: もうjQueryには頼らない!素のJavaScriptでDOMを操作するための基礎知識 - WPJ
<body>
<div id="wrap"></div>
</body>
const pages = getUrlQueries()
const wrap = document.getElementById('wrap')
const fragment = document.createDocumentFragment()
for (const k in pages) {
const page = pages[k]
const box = document.createElement('div')
box.setAttribute('class', 'link-box')
const a = document.createElement('a')
a.setAttribute('class', 'link-button')
a.setAttribute('href', page)
const img = document.createElement('img')
img.setAttribute('src', 'http://www.google.com/s2/favicons?domain='+page)
const p = document.createElement('p')
p.textContent = decodeURI(k)
const btn = document.createElement('button')
btn.setAttribute('class', 'remove-button')
btn.setAttribute('onclick', 'removeQuery("'+decodeURI(k)+'")')
btn.textContent = 'X'
a.appendChild(img)
a.appendChild(p)
box.appendChild(a)
box.appendChild(btn)
fragment.appendChild(box)
}
wrap.appendChild(fragment)
ブックマーク追加の場合は、入力欄の内容をクエリに追加し、URLへ移動します。
逆に削除の場合は、バツを押されたキーをクエリから消去し、URLへ移動します。
ブックマークの追加
<input type="text" name="title" id="title">
<input type="text" name="url" id="url">
function addQuery() {
const queries = getUrlQueries()
const url = document.getElementById('url').value
let title = document.getElementById('title').value
if (title == "") {
title = url
title = title.replace(/=/g, "")
}
if (title!=""&url!="") {
queries[title] = url
updateParams(queries)
}
}
ブックマークの削除
<div class="link-box">
<a class="link-button" href="https://izm51.com/">
<img src="http://www.google.com/s2/favicons?domain=https://izm51.com/">
<p>イズミログ</p>
</a>
<button class="remove-button" onclick="removeQuery("イズミログ")">X</button>
</div>
function removeQuery(q) {
if (window.confirm(q+' のリンクをリストから消去しますか?')) {
const queries = getUrlQueries()
delete queries[encodeURI(q)]
updateParams(queries)
}
}
ページの更新(URLの更新)
参考: Javascriptでパラメータを取得したり設定したりする | webの覚え書き
function updateParams(queries) {
let setParam = '?'
for (k in queries) {
setParam = setParam + k + '=' + queries[k] + '&'
}
setParam = setParam.slice(0, -1)
location.search = setParam
}
今回は、シンプルなブックマークリストサイトを作ってみました!
リンク: https://izm51.github.io/home/
リポジトリ: https://github.com/izm51/home
機能としては単純ですが、自分好みの最小構成で作れたので良かったです。
新しいタブに表示できなかったのは残念ですが、ブックマークが少し便利になりました。
もしよければ誰でもご自由にご利用ください!
ではまた。
]]>勉強やプログラミングで困ったときに英語の情報しかないことは多々あります。
そして僕は海外のEDMやドラマ・ゲームも好きなので、そのクリエイターへのインタビューやメイキングストーリーなどを楽しむにも英語力が必要です。
これらを理解できるようになるだけでなく、せっかくなら英語でコミュニケーションできるようになりたい!
というわけで前々から考えていた英会話学習を本格的に始めることにしました!
[[toc]]
勉強方法は、僕の大好きなバンド"PENs+"のボーカル新井リオさんが昔から発信している英語勉強法を実践することにします。
独学3年間の努力と道のり。日本で英語が話せるようになった僕の勉強法
【勉強法】
①英語日記を書く
時間を決めて書く(15分間)
分からない単語は調べてとりあえず書いてみる②オンライン英会話で添削してもらう
自然な表現に直してもらう③添削してもらった日記を暗唱できるまで繰り返し練習する 読み上げ機能を使ってシャドーイングする
英語日記の書き方はYouTubeチャンネル"Hapa 英会話"さんの方法も参考にすることにします。
着実に英会話力を伸ばす学習法「Journaling」【#106】
英語日記は1週間前から書き始めていて、privateなinstagramアカウントに投稿しています。
日本語で3行程度の日記を書き、これを基に英文を作成してます。
単純に日記として毎日を振り返ること自体が結構楽しいです。
これもリオさんの継続のコツを参考にします。
僕が英語勉強を【5年間継続】させた、3つの方法とコツ【勉強が続かない人へ】
―「なぜ英語を勉強する?」
- 秋にある国際学会で英語で発表する必要がある。事前に用意した発表内容のほかに、質疑応答にもしっかりと対応できるようになりたい。(直近の目標)
- 目標としては、スピーキングでは、事前に想定したシチュエーションでは気軽にコミュニケーションができ、友達を作れるくらいになりたい。そのためには、自分が言いそうなフレーズを一通り覚えている必要がある。
- リスニングでは、海外の俳優やエンジニアが英語でインタビューを受けている動画を聞いて理解できるようになりたい。そのためには、リアルタイムに聞いて理解できる語彙の量を増やす必要がある。
【英語勉強時間】
- 英語日記を書く・教材の予習をする(30分間)
- 英会話レッスンを受ける(30分間)
- 授業の復習をする・読み上げ機能を使って日記のシャドーイング(30分間)
理想は6時30分ごろに起床して朝のうちに勉強したいが、今回は所要時間のみ決めておくことにする。
日記をインスタの非公開アカウントに投稿していき、たまに誰かに見せることにします。
ある程度期間が経ったら、ブログに経過を記録します。
ブログは、英語学習の軌跡として残ればいいと思います。
DMM英会話の無料体験で、オンライン英会話に初挑戦しました。
以下、本当にただの日記です。
先生はフィリピンの女性講師の方で予約しました。
先生は色々な特徴で検索することができて、今回は「初心者向け」「講師歴3年以上」に加えて「キッズ向け」も条件に加えて探してみました。(優しそうなので)
予約時の要望には
This is my first lesson.
I have almost never talked in English.
Please speak slowly.
と書きました。たぶん伝わりました。
さすがに何も準備しないまま臨んでも大変なことになると思ったので、自己紹介を事前に英語で用意しておきました。
また、ここ一週間で英語日記を書いてるときにどう表現すれば良いか分からなかった部分を書き出しておきました。
そして、予約をしてから始まるまでの40分間くらいで、困ったときに使えそうな英語フレーズをかき集めました。
で、時間になりレッスン開始です。ビデオ通話で25分間のレッスンです。
はじめに、
How was your day?
と聞かれたので、とりあえずテンプレ回答の
It was good!
と答えました。しかしそれだけでは簡単すぎると思ったので、
「ここ数日間オンライン英会話を始めようと思っていて、ついに今日始める決心をしたんだよ!」
みたいなことを言おうと思いました。
しかしどう言えばいいのかわからず...🤯
書きのテストなら1分くらい考えて英語にできそうでも、会話だと間に合いませんでした。
よく覚えてないですが、"plan", "several days", "decide"あたりの単語を使って必死に文章っぽい何かを話しました。
困りながらもなんとか伝わったようで、「い、、いいね!」みたいな反応をしてくれてたと思います。(余裕が全くなかったので覚えていない)
開始早々英語力不足に直面しながらも、気持ちを切り替えて自己紹介をしました。
先生は僕の自己紹介を聞きながらいろいろと質問してくれました。
そして僕が拙い英文で返答するたびに、雰囲気を汲み取って正しい英文を教えてくれました。
文字で見たらすぐにわかるような間違いをいくつもしてしまいました。
言いたいことを表現できそうな単語を見つけるのに必死で、文法を考える余裕が全くなかったです。
自己紹介が一段落したら、英語日記を書いていた時の疑問を質問しました。
しかし、聞き方もイマイチだったのでしっくりくる答えはもらえず、これは早めに諦めました。
最後はまた雑談に戻り、ドラマの話に。
プリズンブレイクの話になり、先生は途中までしかストーリーを知らなかったようで、マイケルは実は生きてたんだよ~とか話してたら結構盛り上がりました。
で、時間が来て終了です。
25分間、長いようで一瞬です。
感想としては、
という感じでした。特に先生からの質問ではない発言への返答に困りました。
「ってことは~」や「それなら~」のような表現が分からなかったので、発言の糸口が難しかったです。
【次回使ってみたいフレーズ】
- Which means, ~ 「ということは~」
- Well then ~ 「それなら~」
でも、先生が優しかったので苦痛の時間にはならなかったです。
フリートークはまだ難しそうなので、次回は教材を使ったレッスンにしてみようと思います。
以上、今後の勉強計画とオンライン英会話初挑戦の感想でした。
3か月後の目標は https://youtu.be/v1em-y-a6Ag
]]>Nuxt.jsならJavascriptだけでブログが作れて簡単なのですが、Wordpressならプラグインで導入できるようなことでも自分で実装しなければいけないことがあり少々面倒ですね...!
今回は、記事の内容から自動で目次を生成する設定を紹介します。
markdown-itの追加プラグインで実装しています。
設定すると、↓のような目次を自動で生成することができます。
[[toc]]
今回追加するプラグインをインストールします。
markdown-it-anchor - npm : ヘッダー(h2など)までスクロールするアンカーリンクを作成する
markdown-it-table-of-contents - npm : 記事のヘッダー要素から目次を作成する
$ npm install markdown-it-anchor markdown-it-table-of-contents
インストールしたプラグインをplugins/markdownit.js
の中で読み込みます。
以前の記事で作成したplugins/markdownit.js
内に以下の2行を追加します。
もしplugins/markdownit.js
がない場合は作成し、nuxt.comfig.jsのpluginsに~/plugins/markdownit.js
を追加します。
// plugins/markdownit.js
import MarkdownIt from 'markdown-it'
export default ({ app }, inject) => {
const md = new MarkdownIt({
html: true,
linkify: true,
typography: true,
})
md.use(require("markdown-it-anchor")) // 追加
md.use(require("markdown-it-table-of-contents")) // 追加
// 後略
}
ここまでで目次の自動生成ができるようになっています。
Contentfulの記事内で目次を表示したい箇所に[[toc]]
と記述することで、次のような目次が生成されます。
しかし、これだけでは目次なのか何かのリストなのか分かりにくいので、デザインを変更していきます。
markdown-it-table-of-contentsのプラグインで生成された目次は<div class="table-of-contents"></div>
で囲まれます。
なので、このクラスのスタイルを変更していきます。
今回は以下のように設定してみます。
_slug.vue
/* scss */
.post-content {
padding: 0 0 15px;
position: relative;
z-index: 1;
> * {
position: relative;
z-index: 10; // h2のpadding部分より前面に来るように
}
.table-of-contents {
background: #f3f3f3;
border: 1px solid #ccc;
padding: 5px 10px;
width: fit-content;
ul {
padding-left: 24px;
}
&:before {
content: '目次';
font-weight: bold;
font-size: 16px;
}
}
}
すると、↓のように表示できます。
CSSのbeforeセレクタで"目次"の文字を追加しています。
目次のリンクをクリックすることで、該当するヘッダー要素までスクロールします。
しかしこのままでは、当ブログのようにブログ上部にメニューなどの固定された要素がある場合、ヘッダー要素が下に潜り込んで隠れてしまいます。 (※2020/1/21追記:現在ヘッダーの固定はスマホ表示のみにしています。)
そこで、marginとpaddingをセットすることで重ならない位置までスクロールさせることが出来ます。
当ブログではh2要素に次のようなスタイルを設定しています。
/* scss */
h2 {
font-size: 24px;
margin-top: -64px;
padding: 80px 5px 0;
position: relative;
z-index: 1;
&:before {
content: '';
height: 34px;
width: 100%;
background: #d6d6d6;
position: absolute;
top: 80px;
left: 0;
z-index: -1;
}
}
参考にしていただければと思います!
これで、記事内のヘッダーへジャンプできる目次を自動で生成することが出来るようになりました。
あとは、目次を表示したい記事の箇所に[[toc]]
を記述するのみです。
nuxtでブログを作成した際に公開したリポジトリに、これまでのmarkdown系の設定を反映したmarkdownブランチを作成しました。
Github - izm51/nuxt-blog-test at markdown
これでブログ作成時のメモが尽きたので、これからは新しいことを記事にしていきます!!
ではまた!
個人ブログなのでコメントがつくことはあまりないかもしれませんが、Disqusのコメント欄を設置しました。
この記事では、"vue-disqus"を使ってDisqusを設置する手順を紹介します。
まず、Disqus でアカウントを作り、Create a new siteのページ を開きます。
入力事項を入力し、「Create Site」をクリック。
料金プランを選ぶ画面になるので、どれかを選択。(僕の場合はBasic)
すると様々なプラットフォームの一覧が表示されますが、一番下の「マニュアルインストール」ボタンをクリック。
ここに表示される埋め込みタグは今回は使用しないので読み飛ばして、一番右下の「Configure」をクリック
設定項目の入力欄が表示されるので、入力して「Complete Setup」をクリック
これでDisqusの設定は完了です。
しかし、コピーしておきたいものがあるので、「Configure your site's・・・」または上部メニューの「Setting」をクリックして設定画面を開きます
下の画像で赤く囲った場所に「Shortname」というキーがあるので、コピーしておきます。
ここからは"vue-disqus"の設定になります。
ktquez/vue-disqusの設置方法通りとなります。
まず"vue-disqus"をインストール
$ npm install vue-disqus
plugins/disqus.js
を作成。
import Vue from 'vue'
import VueDisqus from 'vue-disqus'
Vue.use(VueDisqus)
nuxt.config.js
のプラグインに追加
plugins: [
'~/plugins/contentful',
'~/plugins/markdownit',
'~/plugins/prism',
'~/plugins/disqus' // 追加
],
コメント欄を設置したい箇所に↓を追加
pages/posts/_slug.vue
<!-- post.fields.titleにContentfulから取得した記事のタイトルが入っている -->
<div class="comments">
<vue-disqus shortname="your_shortname_disqus" :identifier="'post/'+post.fields.title" :url="'http://example.com/path/'+post.fields.title"></vue-disqus>
</div> <!-- shortname等は書き換えてください -->
ログインが必要なコメント欄なので若干ハードルは高いですが、ないよりはマシだと考えてます!
はたしてコメント欄が使われるときは来るのでしょうか
ではまた。
]]>4月頃、本屋で「アウトプット」系の本が多く並ぶのを見て意気込んでブログを作りましたが、
大学院生生活が思っていたより忙しく、久々の更新になってしまいました。
ブログ作成時に書き溜めていたメモがもったいないので、思い出しながら記事にしていきます。
今回は、"markdown-it"でmarkdown→HTMLの変換をする際に、
で開くように<a>
タグにtarget="_blank"
を書き加える設定を紹介します。
さっそくですがソースコードです。
plugins/markdownit.js
import MarkdownIt from 'markdown-it'
export default ({ app }, inject) => {
const md = new MarkdownIt({ //markdownitのオプション
html: true,
linkify: true,
typography: true,
})
const defaultRender = md.renderer.rules.link_open || function (tokens, idx, options, env, self) {
return self.renderToken(tokens, idx, options)
}
md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
const aIndex = tokens[idx].attrIndex('target')
if (tokens[idx]['attrs'][0][1].match('http')) {
if (aIndex < 0 ) {
tokens[idx].attrPush(['target', '_blank'])
} else {
tokens[idx].attrs[aIndex][1] = '_blank'
}
}
return defaultRender(tokens, idx, options, env, self);
}
inject('md', md)
}
nuxt.config.js
のpluginsに、markdownit.jsを追加します。
nuxt.config.js
でmarkdownitのオプションを設定していた場合は、重複してしまうので削除します。
plugins: [
'~/plugins/contentful',
'~/plugins/markdownit', // 追加
'~/plugins/prism',
],
modules: [
'@nuxtjs/dotenv',
'@nuxtjs/markdownit'
],
// オプションはplugins/markdownit.js内に移動
// markdownit: {
// injected: true,
// html: true,
// linkify: true,
// typography: true,
// },
下記参考リンクをもとに、リンク先の判別を追加してNuxtのプラグインとして組み込みました。
参考:Markdown-Itで独自レンダリングする方法 – 踊る犬.netブログ (旧)
参考:nuxt-community/modules
僕はブログなどの記事で別サイトへのリンクが同じタブのまま移動してしまうのがあまり好みではないので、
細かな設定ではありますがカスタマイズしてみました。
ではまた。
]]>ブログをはじめようと思い立ち、ドメインを取得してから約2週間がたちました。
ところが今日、突然このブログにアクセスできなくなってしまいました。
何も設定をいじっていないはずなのにと思いながら、
お名前.comとNetlifyのDNS設定を全部消しては埋めなおす作業を2回繰り返しても、何も変化なし。
しかし、原因は他の箇所でした!
メールアドレスの有効性を確認していなかったのが原因で、ドメインが一時的に停止されてました。
メール内に記載されたURLリンクからメールアドレスを有効化すると、5分ほどでウェブサイトにアクセスできるようになりました。
お名前.comに新規登録してから2週間くらいメールを有効化していないと、ドメインが制限されるようです。
登録してからドメインの割引案内メールばかりたくさん来ていたのでチェックするのをやめてしまってました。
もし登録してから2週間ほどで同様の症状に困っている方は、メールを確認してみましょう。
]]>Nuxt + Contentful + Netlifyでブログ作成の関連記事です。
当ブログでは、記事はContentful上にてMarkdown記法で作成、保存しています。
そして、Markdown記法からHTMLへの変換には"markdown-it"を使用しています。
しかし、"markdown-it"だけではソースコード部分が見づらかったため、"Prism.js"でシンタックスハイライトする設定をしました。
Nuxt.js + markdown-itでPrism.jsを使用している記事があまりなかったので、簡単にまとめてみました。
@nuxtjs/markdownitはインストール済みとして、Prism.jsをインストール
$ npm install prismjs
pluginsフォルダにprism.js
を作成します。
import Prism from 'prismjs'
import 'prismjs/themes/prism-okaidia.css'
export default Prism
2行目でimportしているcssファイルを変更すればスタイルを変更できます。
テーマは/node_modules/prismjs/themes/
に入っています。
スタイルの比較はPrism.jsを参照してください。
nuxt.config.js
のプラグインに~/plugins/prism.js
を追加
plugins: [
'~/plugins/contentful',
'~/plugins/prism',
],
Prism.jsを有効にするページのmounttdにPrism.highlightAll()
を追加。
/pages/posts/_slug.vue
mounted() {
Prism.highlightAll()
}
ここまでの設定で、ソースコードのシンタックスハイライトを有効にできました。
Contentful上で、以下のようにバッククォート(`
)3つで囲むことでコードブロックにできます。
バッククォートの後に言語を指定することができます。
```js
export default {
mounted() {
console.log("mounted!")
}
}
```
上のコードが↓のように表示されます。
より見やすさを向上するため、行番号を表示するプラグインを有効にします。
プラグインは/node_modules/prismjs/plugins/
に入っています。
先ほど作成した/plugins/prism.js
にline-numbersのプラグインを追加
import Prism from 'prismjs'
import 'prismjs/themes/prism-okaidia.css'
import 'prismjs/plugins/line-numbers/prism-line-numbers.min.js'
import 'prismjs/plugins/line-numbers/prism-line-numbers.css'
export default Prism
そして、<pre>
またはその親の要素にclass="line-numbers"
を設定すると、行番号が表示されるようになります。
Contentfulから取得した記事本文を表示する要素のクラスにline-numbersを追加します。
/pages/posts/_slug.vue
<div class="post-content line-numbers" v-html="$md.render(post.fields.content)"></div>
すると行番号が表示されます。
以上で設定完了になります。
markdown-itだけの状態と比べるとかなり見やすくなりました。
前回までの記事で、Nuxt + Contentful + Netlifyでブログ作成ができました。
そこで、Nuxtでのブログ作成において設定しておいた方がよさそうな項目をリストアップしてみました。
手順についても簡単に紹介しています。
初心者なりの備忘録なので、必要な項目を見つけ次第、随時追加していきます。
[[toc]]
head、metaタグによってブログのタイトルや説明文を設定します。
Nuxtではnuxt.config.js
で全ページ共通の設定に加え、各ページごとにタグの設定ができます。
注意点として、modeがuniversalでない(spaになっている)と、ページごとの設定が反映されません。
設定方法:API: head プロパティ - Nuxt.js
設定の例:SEO HTML Head - Nuxt.js
設定例
nuxt.config.js
(全体で共通の設定)
head: {
// デフォルトとなるタイトル
title: 'イズミログ',
// タイトルの書式 (%sの中にtitleの文字列が入る)
titleTemplate: '%s - Izm Log',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: 'ブログの説明文' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/static/izm-icon.jpg' },
{ rel: 'shortcut icon', size: '196x196', href: '/static/izm-icon.jpg' },
{ rel: 'apple-touch-icon', href: '/static/izm-icon.png' }
]
}
pages/posts/_slug.vue
(各記事の設定)
//this.postにContentfulから取得した記事内容が入っている
head() {
return {
title: this.post.fields.title,
meta: [
{ hid: 'description', name: 'description', content: this.post.fields.description }
}
}
記事ごとにheadの内容を上書きして設定することができます。
僕はContentfulのFieldsにDescriptionという説明文の入力欄を追加して、記事毎にこれを設定するようにしています。
FacebookやTwitterでシェアされた時に表示される文章や画像を設定します。
上の項目で記述したheadの設定に次のように追記していきます。
pages/posts/_slug.vue
(各記事の設定)
//this.postにContentfulから取得した記事内容が入っている
head() {
return {
title: this.post.fields.title,
meta: [
{ hid: 'description', name: 'description', content: this.post.fields.description },
{ hid: 'og:site_name', property: 'og:site_name', content: this.post.fields.title + ' - Izm Log' },
{ hid: 'og:type', property: 'og:type', content: 'website' },
{ hid: 'og:url', property: 'og:url', content: 'https://izm51.com/posts/' + this.post.fields.slug },
{ hid: 'og:title', property: 'og:title', content: this.post.fields.title },
{ hid: 'og:description', property: 'og:description', content: this.post.fields.description },
{ hid: 'og:image', property: 'og:image', content: "https:"+this.post.fields.image.fields.file.url },
{ hid: 'twitter:card', name: 'twitter:card', content: "summary" },
{ hid: 'twitter:site', name: 'twitter:site', content: "@immzm51"}
]
}
},
OGPやTwitter Cardの詳細についてはこちらのサイトが参考になります。
リンク:【WordPress】OGPとTwitterカードをプラグイン無しで設定する方法
サイトマップを設置すると検索エンジンにサイトの構成を伝えることができ、検索にのりやすくなります。
@nuxtjs/sitemapを使用すれば、ビルド時に自動でサイトマップを生成できます。
$ npm install @nuxtjs/sitemap
nuxt.config.js
のモジュールに@nuxtjs/sitemapを追加します。
また、sitemapのセクションも追加し、sitemap生成の設定を記述します。
静的ページは自動でサイトマップに追加されます。
しかし、動的生成されるページのパスはroutes
に自分で設定する必要があります。
このブログの場合は個別記事ページが動的ページです。
generate
のroutes
の内容を少しアレンジして記述しました。
その他の設定項目は"@nuxtjs/sitemap"のReadmeを参照してください。
modules: [
'@nuxtjs/sitemap',
],
sitemap: {
hostname: 'https://example.com',
routes() {
return client
.getEntries({ content_type: 'post' })
.then(entries => {
return entries.items.map(entry => {
return "/posts/" + entry.fields.slug
})
})
}
},
ブログの閲覧数などを解析するために、Googleアナリティクスを設置します。
@nuxtjs/google-analyticsを使用すれば設定は簡単です。
$ npm install @nuxtjs/google-analytics
'nuxt.config.js'のモジュールに'@nuxtjs/google-analytics'とアナリティクスのトラッキングIDを追加
modules: [
['@nuxtjs/google-analytics', {
id: 'UA-XXXXXXXXX-X'
}]
]
詳細:Google アナリティクスの統合 - Nuxt.js
Googleの検索結果を分析するため、Google Search Consoleの設定をしておきます。
TXTレコードの設定方法など、少しややこしい箇所もあります。
まず、Google Search Consoleの入力欄にドメインを入力します。
「続行」ボタンを押すと、ドメインの所有者確認画面が表示されます。
ここで「コピー」ボタンを押し、TXTレコードをコピーします。
当ブログのようにNetlifyのネームサーバーを使用している場合は、NetlifyのDNSパネルを開きます。
Netlifyの管理ページで「Domain settings」をクリック
⇓「・・・」を押し、「Go to DNS panel」をクリック
⇓「Add new record」をクリック
⇓「Record type」を「TXT」、「Name」を「@」にし、「Value」にGoogle Search Consoleでコピーした「TXTレコード」を張り付ける。
そして「Save」をクリック
DNSの設定が反映されるまで数分待ち、Google Search Consoleの確認ボタンを押すと、設定完了の画面が表示されます。
プログラミング系のブログの場合、記事内にコードが多く含まれると思います。
そのプログラミングコードを見やすくするために、シンタックスハイライトを設定します。
下記記事に手順をまとめました。
markdown-it + Prism.jsでシンタックスハイライト - Izm Log
(2020/4/3 追記)
下記の手順を楽にするツールを作成しました!
Googleフォトのアルバムから埋め込みコードを生成するツールを作った【ブログに便利】 - Izm Log
ContentfulではFreeタイプの場合、保存できる記事や画像などの合計個数が5000個までです。
(記事投稿時点)
5000記事にはしばらく到達しないと思いますが、
画像も合わせて5000個までとなるとすぐにオーバーしてしまいそうです。
そこで、記事はContentful、画像はGooglePhotoに保管することにしてみました。
(注:GooglePhotoの仕様変更などにより画像が表示できなくなる可能性アリ)
まず、記事に使用したい画像をGooglePhotoで開き、右上にある「共有ボタン」をクリック
「リンクを作成」をクリックし、作成されたURLをコピー
コピーしたURLを下記リンクの「Paste Google Photos link here..」と書いてある入力欄に張り付け、「GENERATE CODE」をクリック
https://ctrlq.org/google/photos/
少し待つと画像のURLリンクが生成されます。
「Direct Link (URL)」をコピー
Contentfulの記事作成画面を開き、画像のURLをMarkdown記法で貼り付けます。
画像URL末尾の=w2400
は、画像表示時の最大幅になるので、必要に応じて調整や消去します。
![Copy-url](https://lh3.googleusercontent.com/ayA-OhW6nWL10VxmmFb9QFsOuVUOMzHNez6s-ZNPuuJx-Q020m7v2id1rj4xUATpx7zgjsa5IDX-eEuvkikGQPnWRVbA6gB-1vPa8cAnk7hZ7L4IiRPphvzz9c3xcYCyWI8ntL0seA=w2400)
<!-- ![画像のalt属性](画像のURL) -->
以上、少し手間がかかってしまいますがGooglePhotoの画像を記事に使用する手順です。
リンクの生成に外部サービスを使用してしまっているので、より簡単な方法が見つかればアップデートします。
当ブログの画像が表示されなくなっていたら、この方法は使えなくなっている可能性が高いです。
"Disqus"というコメント欄を簡単に設置できるサービスを見つけたので、設置しました。
設置手順は下記記事にまとめました。
Nuxtで作ったブログにコメント欄を設置する方法【Disqus】
このブログの作成時に設定したり、カスタマイズした項目集の備忘録でした。
必要そうな項目を見つけ次第、随時記事にしていきます!
ではまた。
]]>Nuxt.jsを使用してブログ作成の後編です!
<前編>ではNuxtプロジェクトの作成からContentfulのセットアップまでの手順がまとめてあります。
<後編>では記事の作成、Nuxtでの表示、Netlifyへのデプロイまでの手順をまとめていきます!
[[toc]]
上部のメニューから「Content」をクリックし、
「Add Post」をクリック
記事の作成フォームが表示されるので、記事の内容を記入して「Publish」
すると、記事が作成できます。
同じ要領で2つ目の記事も作成しました。
まず、トップページに記事の一覧を表示してみます。
pages/index.vue
を下記のように書き換えます。
<template>
<div class="posts">
<div v-for="(post, index) in posts" :key="index" class="post">
{{ post.fields.title }}
</div>
</div>
</template>
<script>
import client from '~/plugins/contentful'
export default {
asyncData({ params }) {
return client
.getEntries({
content_type: 'post',
order: '-sys.createdAt',
})
.then(entries => {
return { posts: entries.items }
})
.catch(e => console.log(e))
},
}
</script>
そして、下記のコマンドを実行します。
$ npm run dev
すると表示される「http://localhost:3000」のようなURLをブラウザで開くと
先ほど作成した記事のタイトル一覧が表示できました。
pages/index.vue
を下記のように書き換え、少し見た目を整理してみます。
<template>
<section class="latest-posts">
<div class="posts">
<nuxt-link :to="'posts/'+post.fields.slug" class="post" v-for="(post, index) in posts" :key="index">
<div class="thumb">
<img :src="post.fields.image.fields.file.url">
</div>
<div class="post-text">
<p>{{ formatDate(post.sys.createdAt) }}</p>
<h2>{{ post.fields.title }}</h2>
</div>
</nuxt-link>
</div>
</section>
</template>
<script>
import client from '~/plugins/contentful'
export default {
asyncData({ params }) {
return client
.getEntries({
content_type: 'post',
order: '-sys.createdAt',
})
.then(entries => {
return { posts: entries.items }
})
.catch(e => console.log(e))
},
head: {
title: '記事一覧',
},
methods: {
formatDate(iso) {
const date = new Date(iso)
const yyyy = new String(date.getFullYear())
const mm = new String(date.getMonth() + 1).padStart(2, "0")
const dd = new String(date.getDate()).padStart(2, "0")
return `${yyyy}.${mm}.${dd}`
}
}
}
</script>
<style lang="scss">
section.latest-posts {
padding: 10px;
.posts {
max-width: 900px;
margin: 0 auto;
padding: 10px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
background: #ddd;
a.post {
width: calc(100% / 2 - 20px);
@media (min-width: (768px)) {
width: calc(100% / 3 - 20px);
}
margin: 10px;
background: #fff;
text-decoration: none;
color: #111;
.thumb {
width: 100%;
padding-bottom: 75%;
position: relative;
overflow: hidden;
img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-width: 100%;
}
}
.post-text {
padding: 5px 10px 10px;
h2 {
width: fit-content;
font-size: 20px;
}
}
}
}
}
</style>
すると下図のように表示できます。
これで記事の一覧ページは一旦完成とし、記事の個別ページを作成します。
pagesフォルダ内にpostsフォルダを作成します。
そして、postsフォルダ内に_slug.vue
ファイルを作成します。
<template>
<article class="article">
<div class="single">
<h1 class="post-title">{{ post.fields.title }}</h1>
<p class="post-created-at">{{ formatDate(post.sys.createdAt) }}</p>
<div class="post-content" v-html="$md.render(post.fields.content)"></div>
</div>
</article>
</template>
<script>
import client from '~/plugins/contentful'
export default {
asyncData({ params, error, payload }) {
if (payload) return { post: payload }
return client
.getEntries({
content_type: 'post',
'fields.slug': params.slug,
})
.then(entries => {
return { post: entries.items[0] }
})
.catch(e => console.log(e))
},
head() {
return {
title: this.post.fields.title,
}
},
mounted() {
console.log(this.post)
},
methods: {
formatDate(iso) {
const date = new Date(iso)
const yyyy = new String(date.getFullYear())
const mm = new String(date.getMonth() + 1).padStart(2, "0")
const dd = new String(date.getDate()).padStart(2, "0")
return `${yyyy}.${mm}.${dd}`
}
}
}
</script>
<style lang="scss">
article.article {
padding: 10px;
.single {
max-width: 900px;
margin: 0 auto;
padding: 10px;
color: #222;
border: 2px solid #444;
border-radius: 10px;
h1, h2, h3 {
margin: 16px 0;
}
h1.post-title {
font-size: 32px;
text-decoration: underline;
}
.post-content {
h1 {
font-size: 32px;
}
h2 {
font-size: 24px;
background: #ccc
}
p {
margin: 16px 0;
font-size: 16px;
}
img {
max-width: 100%;
border: 1px solid #000;
}
}
}
}
</style>
そして、記事の一覧ページから個別記事のページへ移動すると
このように、記事の内容を取得して表示ができているはずです。
markdownitを使用して、記事のマークダウン記法をHTMLタグに変換して表示しています。
細かいスタイルについてはnuxt.config.js
内のmarkdownitの設定項目や、CSSによるスタイリングが必要となります。
Netlifyへデプロイ、の前に、今まで作成したnuxtプロジェクトをGitにpushします。
新しくリポジトリを作成し、
$ git init
$ git add .
$ git commit -m "first commit"
$ git remote add origin <gitのURL>
$ git push -u origin master
次に、Netlifyのサイトで「Get started for free」ボタンから会員登録、ログインします。
(画面は2019/4/4のものになります)
右の方にある「New site from Git」ボタンをクリック
Gitのproviderを選択
インストールするリポジトリを選択
使用するリポジトリを選択し、
「Build command」にnpm run generate
、
「Publish directory」にdist
と入力し、「Show advanced」をクリック
「New variable」をクリックしてキーの入力欄を表示し、
.envファイルに記述した「CTF_SPACE_ID」と「CTF_ACCESS_TOKEN」を記入→「Deploy site」
「Deploying your site」と表示され、少し待つとデプロイが完了します。
「Site settings」をクリック
左のメニューから「Build & deploy」をクリック 下へスクロールし、「Build hooks」の「Add build hook」をクリック
「Build hook name」に「Deploy」と記入し、「Save」をクリック
表示された「https://api.netlify.com/build_hooks/~」 で始まるURLをコピー
Contentfulのサイトに戻り、上部の「Settings」メニューから「Webhooks」を選択
「Add Webhook」ボタンから、Webhookの設定をします。
「Details」の「Name」に「Deploy」と記入し、「URL」の入力欄に先ほどNetlifyでコピーしたWebhookのURLを貼り付けます。
「Triggers」で「Select specific triggering events」を選択し、「Entry」にチェックを入れます。
設定が完了したら、「Save」をクリック
これで、Contentful上で新しい記事の投稿や記事の編集を行うと、Webhookにより自動でNetlify上のデプロイが行われるようになります。
これにて設定が完了となり、記事の投稿とウェブサイトへの反映ができるようになりました。
ここから、ドメインの設定やデザインの変更をすることで、実際にブログとして使用できるようになると思います。
ここまでのコードをGithubで公開 しておきます。
ご自由に使用・参考にしてください!
今後は、基本的なSEOの設定や、記事のタグづけ方法なども記事にしていきたいと思っています。
]]>趣味をアウトプットするためにブログをはじめようと思い立ちまして、
せっかくなので最近勉強中のNuxt.jsを使用してブログを作成してみました!
記事表示までの手順をまとめてみたので、参考にしていただければと思います!
<前編>ではNuxtプロジェクトの作成からContentfulのセットアップまで、
<後編>では記事の作成、Nuxtでの表示、Netlifyへのデプロイまでの手順をまとめてあります。
なお、この記事はこちらの動画を大いに参考にしています。
併せて参考にしていただければと思います。
[[toc]]
前提条件として、nuxtはインストール済みとします。
$ npx create-nuxt-app <project-name>
nuxtで新しくプロジェクトを作成します。
設定はすべて初期設定のままです。
次に必要な物をインストールします。
$ cd <project-name>
$ npm install
$ npm install contentful @nuxtjs/dotenv @nuxtjs/markdownit node-sass sass-loader
nuxt.config.js
に以下のように追加します。
require("dotenv").config() //一番上に
const client = require("./plugins/contentful")
/* ~~~中略~~~ */
plugins: [
'~/plugins/contentful'
],
modules: [
'@nuxtjs/dotenv',
'@nuxtjs/markdownit'
],
markdownit: {
injected: true,
html: true,
linkify: true,
typography: true,
},
generate: {
routes() {
return client
.getEntries({ content_type: 'post' })
.then(entries => {
return entries.items.map(entry => {
return {
route: "/posts/"+entry.fields.slug,
payload: entry
}
})
})
}
},
env: {
CTF_SPACE_ID: process.env.CTF_SPACE_ID,
CTF_ACCESS_TOKEN: process.env.CTF_ACCESS_TOKEN,
},
pluginsフォルダにcontentful.js
を作成します。
const contentful = require("contentful")
module.exports = contentful.createClient({
space: process.env.CTF_SPACE_ID,
accessToken: process.env.CTF_ACCESS_TOKEN
})
nuxt.config.jsと同じ階層に.env
ファイルを作成します。
CTF_SPACE_ID=
CTF_ACCESS_TOKEN=
では、CTF_SPACE_IDとCTF_ACCESS_TOKENを埋めるために、contentfulのセットアップを行っていきます。
Contentfulのサイトで右上にある「Try for free」ボタンから会員登録、ログインします。
(画面は2019/4/4のものになります)
登録完了後、表示されるボタンを押すと「The Example Project」が作成されます。
「The Example Project」が表示されたら、左上のボタンからメニューを開き、
「Create space」をクリック
"Space type"を選択し(僕の場合はFree)、"Space details"を記入し、進んでいけばspaceの作成は完了です。
※"Space type"が"Free"の場合、保存できる記事とアセット(画像など)の合計個数が5000個まで(記事投稿時点)のため、画像を多く使用すると比較的すぐに上限に達してしまうかもしれません。
「Create a content type」をクリック
記事を格納するために「Post」という名前の"Content model"を作成します。
右側の「Add field」ボタンから新しい入力欄を作成できます。
今回は下記のような"field"を作成します。
まず「Title」を作成します。
「Add field」→「Text」→NameにTitleと入力→「Create and configure」
タイトルは必須項目にした方がいいと思うので、「validations」タブから「Required field」にチェックして「Save」
これで「Title」のfieldが作成できました。
同様に「Slug」、「Image」、「Content」を作成します。
「Slug」は記事のURLとなる、IDのようなものです。
「Add field」→「Text」→NameにSlugと入力→「Create and configure」
→「validation」→「Required field」と「Unique field」にチェック→「Save」
「Image」は記事の一覧に表示するサムネイル画像です。
「Add field」→「Media」→NameにImageと入力→「Create and configure」
→「validation」→「Required field」にチェック→「Save」
「Content」は記事の本文です。
「Add field」→「Text」→NameにContentと入力→「Long text, full-text search」にチェック
→「Create and configure」→「validation」→「Required field」にチェック→「Save」
これらの作成が完了したら、右上にある「Save」ボタンをクリック
そして上部の「Settings」メニューから「API keys」を選択
右側の「Add API key」ボタンから、APIキーを確認することが出来ます。
ここで確認できたキーを.env
ファイルに記入します。
CTF_SPACE_ID=<Space ID>
CTF_ACCESS_TOKEN=<Content Delivery API - access token>
長くなってしまったため、<後編>へ続く
]]>