僕がVueで使用している便利パッケージを教えます!
2019/06/28 2019/06/28Vueで使える!便利なパッケージ一覧 最初に これ、ちゃんと一つずつ説明しながら記事にしたい。 なので、この記事はとりあえず作成したみたいな感じです。 Vue-CropperJs vue-cropperjs A Vue wrapper component for cropperjs. Latest version: 5.0.0, last published: 2 years ago. Start usi... 前に書いたCropper.jsのVue版ですね。 Vueはラップするのも得意なので、こういうバニラで書かれたライブラリをVue化するというプロジェクトは結構見かけますし、多いですね。 似たようなラッププロジェクトが存在しますので、用途によって使うものを選択すればいいですよ。 Vue-Google-AdSence vue-google-adsense Vue.js Google Adsense Component with InFeed and InArticle Ads support. Latest version: 1.10.1, last ... これは今年の抱負メーカーの記事で少し触れましたね。 Vueを導入すると、Vueの中にプレーンなjavascriptを記載することはできますが、外部スクリプトを記載(WEB上で読み込んだり)は怒られてしまいます。そこで、このプロジェクトができたのでしょう。 copied.<!-- Google Ads --> <div class="text-center"> <Adsense data-ad-client="ca-pub-5315745554235923" data-ad-slot="6372572820" data-ad-format="horizontal" ></Adsense> </div> こんな感じで広告表示をすることができます。 vue-tags-input GitHub - JohMun/vue-tags-input: A tags input component for VueJS A tags input component for VueJS. Contribute to JohMun/vue-tags-input development by creating an acc... これはタグ機能の操作に便利。 簡単に言えばQiita風にタグ付けができるようになります。 これのおかげで記事を書くのも簡単です。 toast-ui/vue-editor TOAST UI :: Make Your Web Delicious! TOAST UI is an open-source JavaScript UI library maintained by NHN Cloud. ご存じですか?? とんでもない量のUIを作成し、配布しているプロジェクトなんですが、この中にめっちゃくちゃ優秀なMarkdownエディタが存在します。 GitHub - nhn/toast-ui.vue-editor: This repository is DEPRECATED! GO TO 👉 https://github.com/nhn/tui.editor/tree/master/apps/vue-editor This repository is DEPRECATED! GO TO 👉 https://github.com/nhn/tui.editor/tree/master/apps/vue-editor... toast-uiのページ言ってDEMO触ってきたらわかる。 やばい。しかもそれをVueに落とし込む始末。 そのうち有料になりそうで怖いですね。 bootstrap-vue https://bootstrap-vue.js.org/ https://bootstrap-vue.js.org/を見る これはかなり有名ですね。Bootstrapで書かれたコンポーネントを簡単に呼び出す事ができます。記述量が多くなってしまいがちでしたが、この子のおかげで完結にまとまりますね。 少し前まで、日本語入力のバグがありましたが、それも解決されているようです。 Vuetify https://vuetifyjs.com/ja/ https://vuetifyjs.com/ja/を見る これもかなり有名。マテリアルデザインがすごい使いやすい。PWA向きですね。 でも、正直そこまで詳しくはない。 最後に Vue-CropperJs toast-ui/vue-editor vue-tags-input についてはそのうち時間ができたら纏め直そうと思う。 本当に便利なので、今後もお世話になりそうですし、宣伝しておきたい。 こういうの嫌う人もいるけど、効率化を本気で求めるなら、できている物は使わないとね。
Squidの注意点
2019/06/21 2019/06/21Squidを使用するときにはここに気をつけろ! 皆さんこんにちは。Nonです。 実は私、Qittaにもしばしば投稿しておりまして、そちらがここになります。 ある時、Squid使用中に、ハマったことがありまして、今でもちょいちょいハマる場所なので、備忘録ついてでに書いておくこととしましょう。 ちなみにQiita記事はこちら 結論 copied.if (nt == NULL) /* empty line */ continue; *nt = '\0'; /* null-terminate the address */ debugs(1, 5, "etc_hosts: address is '" << addr << "'"); lt = nt + 1; SBufList hosts; 一応念のためと思って squid のソースを読んでみたら、 なんと squid は libc を使わず独自に /etc/hosts を parse していて、 そこでは行頭にスペースやタブがあると、アドレスが書いてないものとみなされてしまうコードになっていました。 この辺です: https://github.com/squid-cache/squid/blob/d429cbd0e4d86d4af54ed50e499da14039ff7429/src/tools.cc#L954 これはビックリ。 というわけで、行頭のスペースないしTABが squid 限定で犯人でした。 この回答くれた方には恩しか感じません。僕は完全にSquidの設定ばかりを疑っていました。 注意点 文字コードや、改行ではない。 文字コードに依存する文字がhostsの中に記述されていれば関係あるかもしれません。 しかし、hostsは基本的に英数字しか書きませんので、問題ないかと。 なんかいい方法ないですか?? 自作アプリでローカルWEBサーバーにアクセスする際、Squidでリバースプロキシ立てちゃう方法をよく使用します。 この手法してるとあらぬトラブルに巻き込まれるので、なんかいい方法ありませんか? ちなみに、エミュレーターで無理くりするあの感じが嫌なので、実機を使用しています。 最後に ちょっと短いけど今日はここまで。 たまには過去の資産をまとめることも悪くないですね。 こういうリメイク記事も作成していくかもしれません。 にしてもコード読んで解決とは・・・世の中にはすごい人がたくさんいますね。
Z250での冒険日記
2019/06/14 2019/06/14定期的にこんな感じの記事も追加して行くと思う。 4月27日〜29日 西日本一周ツーリング これはお友達と弾丸ツーリングに行きましたZXR1200のパワーを見せつけられましたね。 鳥取砂丘 写真は撮影してないけど、ジャリジャリ道ちょっと怖かった。滑るか心配だったね。 ここは大阪から近いので、何回も行ったことがあったけど、何回来ても楽しいですね。 今回の冒険ではメインディッシュじゃないので、早々に退散。 日本海を眺めながら こっち側は天気が荒れますね〜。日本海の波もいい荒れ具合。いい写真が取れました。 濡れたZが一番かっこいいんだよ・・・ 人生に一度は言ってみたい場所ランクイン 秋吉台カルストロード 完全に日本じゃない。別世界でしたね。 【絶景ロード】 秋吉台カルストロード ー山口県ー - 【公式】BikeJIN(培倶人|バイクジン) 角島大橋 あいにくのお天気でしたが・・・ 5月4日〜6日 能登半島一周ツーリング こちらもお友達と金沢へ。道中危険なこともありましたが、とても楽しかった。 東尋坊 火サスできそう・・・ 能登半島先端「狼煙」 ちょっとかっこいい 【道の駅 狼煙】公式サイト 〜能登半島さいはて、大浜大豆の地豆腐〜|石川県珠洲市 能登半島西側某所 サンセットが美しい。ふと見たときに、バックミラーに写り込んだので撮影。 5月26日 曽爾高原 いつもの いつメン 空気が美味しい 中学生の林間学校中でした。 最後に バイクはいいぞ! インプットの多くて、オフィスワークに偏りがちなプログラマーという職業にはピッタリじゃないでしょうか!! .
自分のサイトをサクッとPWA化!PWA導入編①
2019/06/07 2019/06/07PWAを簡単に導入するには? 最近のWEB言語界隈は流行り廃りが非常に激しいですよね。 でもこのPWAはまだまだこれからな技術!導入したい誰かのために、導入方法を簡単ではありますが、記載しておこうと思います。 基礎知識として ServiceWorkerご存知でしょうか? こちらGoogleのブログです。 Service worker overview - Chrome Developers An overview of service workers. このページにはこう記載されています。 Service Worker の紹介 リッチなオフライン体験、定期的なバックグラウンド同期、プッシュ通知など、これまでネイティブアプリを必要としていた機能が Web にもやってきます。 Service Worker はそれらの機能を提供する基盤技術です。 Service Worker とは Service Worker はブラウザが Web ページとは別にバックグラウンドで実行するスクリプトで、Web ページやユーザーのインタラクションを必要としない機能を Web にもたらします。 既に現在、プッシュ通知やバックグラウンド同期が提供されています。 さらに将来は定期的な同期、ジオフェンシングなども導入されるでしょう。 このチュートリアルで説明する機能は、ネットワーク リクエストへの介入や処理機能と、レスポンスのキャッシュをプログラムから操作できる機能です。 「Service Worker はブラウザが Web ページとは別にバックグラウンドで実行するスクリプトで」この一文がキモですよね。 要はWEBページ開いて無くても、バックグラウンドで特定の処理をしてくれるということです。ブラウザ立ち上げてないのにプッシュ通知受け取ってくれたりするのはこの子のおかげです。 manifest.jsonご存じでしょうか? お次はこちらの公式サイト manifest.json - Mozilla | MDN manifest.json ファイルは、WebExtension API を使う拡張機能に必ず含めなければならない唯一のファイルです。 要はこのWEBアプリの基本属性の情報をためておくファイルですね。 テーマカラーは青で・・・アプリ名は〇〇で・・・アイコンは〇〇.pngで・・・みたいな情報を格納するファイルがmanifest.jsonです。 ちなみにこのサイトのmanifest.jsonは簡単な構成です。 copied.{ "short_name": "non's Labo", "name": "non's Labo", "display": "standalone", "icons": [{ "src": "android-chrome-512x512.png", "type": "image/png", "sizes": "48x48" }, { "src": "android-chrome-512x512.png", "type": "image/png", "sizes": "96x96" }, { "src": "android-chrome-512x512.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "/" } この構成ファイルをもとに、PWAとしてインストールする情報になっています。 上図に乗っているアイコンが、アプリアイコンとして使用され、Name属性がアプリ名として、アイコンの下に表示されます。 導入するにはこの子達の設定をすればいいだけ 読み込むべきファイルを用意します。 manifest.jsoncopied.{ "short_name": "non's Labo", "name": "non's Labo", "display": "standalone", "icons": [{ "src": "android-chrome-512x512.png", "type": "image/png", "sizes": "48x48" }, { "src": "android-chrome-512x512.png", "type": "image/png", "sizes": "96x96" }, { "src": "android-chrome-512x512.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "/" } sw.js copied.var urlsToCache = [ '/js/app.js' ]; self.addEventListener('install', function (event) { return install(event); }); self.addEventListener('message', function (event) { return install(event); }); const install = (event) => { return event.waitUntil( caches.open('laboCache') .then(function (cache) { urlsToCache.map(url => { return fetch(new Request(url)).then(response => { return cache.put(url, response); }); }) }) .catch(function (err) { console.log(err); }) ); } self.addEventListener('activate', function (e) { console.log('ServiceWorker activate') }) self.addEventListener('fetch', function (event) {}) 導入したいTOPページにこれを記載 copied.<script> // PWA window.addEventListener("load", () => { if ("serviceWorker" in navigator) { navigator.serviceWorker .register("/sw.js") .then(registration => { console.log("ServiceWorker registered"); registration.onupdatefound = function() { console.log("Exist update"); registration.update(); }; }) .catch(error => { console.warn("ServiceWorker error", error); }); } }); </script> 同様にheadタグ内でmanifest.jsonを読み込み copied.<!-- PWA --> <link rel="manifest" href="/manifest.json"> 解説 sw.jsについて sw.jsこれはなにかというと、WEBページがアクティブじゃないときに走る処理です。 copied.// インストールされたとき self.addEventListener('install', function (event) {}); // アクティベートしたとき self.addEventListener('activate', function (event) {}); など、様々なイベントを検知して、バックグラウンド実行してくれます。 他にもイベントはたくさん用意してありますし、これ用のサードパーティ製ライブラリも存在します。 私達はこれを駆使してオフライン対応や、通知イベントを発火したりします。 上に書かれているような内容ではconsole.logでログ吐き出しているので、オフライン対応もへったくれも無いですね。 PWA導入「ホームボタンへ追加」について copied.<script> // PWA window.addEventListener("load", () => { if ("serviceWorker" in navigator) { navigator.serviceWorker .register("/sw.js") .then(registration => { console.log("ServiceWorker registered"); registration.onupdatefound = function() { console.log("Exist update"); registration.update(); }; }) .catch(error => { console.warn("ServiceWorker error", error); }); } }); </script> そのページがロード完了したらserviceWorkerを起動させてregisterでインストール処理を走らせます。クリックされればthenが走ります。 また、このページにアクセスするたびに、registration.onupdatefound でキャッシュのアップデートがないか確認します。ある場合(つまりjavascriptが変更されている場合やcssが変更されている場合)はregistration.update();で更新処理が動作します。これでキャッシュの中身を入れ替えてバージョンを管理しています。 ここに表示されているのが、manifest.jsonの内容ですね。 headで読み込みするのを忘れずに scriptとmanifest.json読み込みできなければ上の図のようなボタンは発生しません。 ご注意を! 最後に 改めて読み返し見ると、わかりにくい箇所が何点かありますね・・・ これは今後記事の更新をしていくとしましょう! この記事が参考に慣れば幸いです。
自分のサイトをサクッとPWA化!PWA解説編①
2019/05/31 2019/05/31PWAとは? 皆さんPWAご存知でしょうか?去年くらいから流行りだしたあの技術をですね! PWA(Progressive Web Apps)とは技術やライブラリではなくただのWEBサイトです。 Googleが定める要素を備えたWebサイトをPWAと呼びます。私個人の感覚ではありますが、この辺を勘違いしている人が多いと思います。 プログレッシブ ウェブアプリ: オフライン | Google Developers プログレッシブ ウェブアプリ: オフライン | Google Developersを見る PWAで何ができるの? これ気になるなる人多いと思います。 簡単に言ってしまうと、ネイティブ環境(OSのカードリーダー機能の読み込みなど)デバイスにアクセスする機能以外ならネイティブアプリと遜色ありません。 javascriptには位置情報を取得する機能や、カメラにアクセスする機能、カードリーダーのライブラリまで揃っています。セキュリティ的にはともかく、そういったライブラリ群など使用すればネイティブアプリには近づけるといって遜色無いでしょう。 代表例はこちら プッシュ通知機能 位置情報取得機能 オフライン機能 SPAによるネイティブライクな画面実装 ゲーム用javascriptライブラリを使用したゲーム実装 などなど。 これらを踏まえて、特徴とメリット、デメリットを見ていきましょう。 特徴 スマートフォン上でモバイルアプリのような振る舞いを見せる Service Worker機能(今度別記事で解説するとしましょう)の一つ「サイトのキャッシュ化」でオフラインでも使用することができる。 キャッシュ化されるの、表示が高速。 WEBプッシュ通知機能をService Workerなどが吸収してくれる。 メリット Google PlayやApp Storeの認証を待つ必要がない。 キャッシュの利用により、WEBアプリでは困難とされてきたオフライン対応が簡単。 WEBの技術で可能なため、ネイティブアプリを作成するための勉強コストが0。 デメリット Google PlayやApp Storeの強力なランキング機能や、あなたにおすすめ機能を使用できないため、新規ユーザーを獲得することが比較的困難。 現在、iOSでは機能制限されているため、普及しているとは言い難い。 使ってみた所感 PWAハマりますねこれは。上の画像については別記事で触れるとしましょう。 Monaca - すべての人にアプリ開発を Monacaは最新のWeb技術を活用したアプリ開発ソリューション。クロスプラットフォーム開発の効率化、組織の開発プロセスの標準化を実現します。 Monacaはご存知でしょうか。 3年位前に私も使用してアプリをリリースしたことがあるんですが、これに似た感じですかね。 HTMLとjavascriptでネイティブアプリを作成できるだけでもすごかったのに、今ではそのように振る舞うことができるわけです。 特性上、フロント言語によるSPAやSSRとも相性が非常によく、これからの主流になっていくのではないでしょうか。 また、審査をクリアする必要がないので、早急なアップデートが可能だったり、時間を無駄にしなくて済むなど他にもメリットがたくさんあります。 以外に知らない人が多い印象 知人にこの話をすると割と「なにそれ?」と帰ってきます。一般人以外にもエンジニアに聞いても3分の2くらいはこの反応。フロントエンジニアの知人に聞いてようやく90%くらいの反応でした。 Google PlayやApp Storeが強すぎて「アプリ=OSのStoreからダウンロード」という印象になっていますね。 ゲームを探すときも、まずそこを開くイメージです。 対して、ソシャゲであるグランブルーファンタジーはWEB上で動作するゲームです。 これはPWA移行夢じゃないハズですね。こういったWEB上で動作しているゲームには美味しい話ではないでしょうか。 だんだん実用的になってきている Firebase FireStore(NoSQL化とも言い換えられます) FCM(Firebase Cloud Messaging) フロント言語の普及 Angular React Vue 以上の技術が発展してきて、実用化に移っている起業も多数存在します 例えば?? Qiita エンジニアで見たこと無いって人はいるのでしょうか?組み込み言語の記事は少ないですが、緑色のあの画面を見たことがあるって人は多いではないでしょうか。 こもPWAですね。 日経新聞社 正直かなり驚きました。新聞社などはこういう技術に疎いものだとおもっていたので、ネットの電子版記事を読もうとしたとき、「ホーム画面に追加」ボタンが出てきたときは感動しましたね。 Twitter Lite Twitterはアプリが有名過ぎてPWA側のアプリを知らない人は多いのでは無いでしょうか? けど個人アプリ以外に活躍の場少なくない? そういう風に考えることも昔はありました。 大きい規模のWEBサービスだったりすると、オフライン化の対応だけでデスマになりそうです。 しかし最近その考えは無くなってきました。 オフライン対応はアプリチームがやっているようなものですし、大きい規模のWEBサービスでもドメイン管理をしていれば、PWAはすぐに可能です。PWAに規模は関係ありませんしね。SPAやSSRが必須というわけでもないので、普通にページ遷移してやればいいです。 オフライン対応も必須ではないので、できるところからコツコツとすることができます。 つまり、大きさや規模など考えなくても「導入するだけ」ならすぐにできます。 この敷居の低さが最大のメリットでもいいましょうか。 もちろんSPAやSSRを利用して、最大限のPWAのメリットを引き出すことができれば、ユーザーの皆様のためになるでしょう。 でもその一歩として、「とりあえず入れちゃう」のも悪くはないかもしれませんね。
【個人開発】勉強方法と開発手法、そのスピード感について【勉強方法】
2019/01/03 2019/05/24【個人開発】勉強方法と開発手法、そのスピード感について【勉強方法】 今年の年始に今年の抱負メーカーというアプリを開発しました。(宣伝) 【個人開発】新年早々、今年の抱負をつぶやくWEBアプリをつくってみた(記事) 実は主だった周知はしていませんが、他にもたくさんのアプリを開発しています。 同じようにこの業界にサラリーマンとして働き始め、独学を中心に曲がりながらやってこれました。 この中で、自分でうまくいったと思う開発手法・勉強方法とそのスピード感について(今回は特にこちら)記事にしたいと思います。 特に技術に関係していませんが、開発に関係すると思うので、大目に見てやってください。 では、綴っていきたいと思います。 また、この記事は完全に僕の主観です。 異論は当然認めます。 兵は拙速を尊ぶ 何事も早い(速い)ほうが良い。 ことわざにも早さを重視するものが多いことから、歴史的に見ても開発開始は早ければ早いほど良く、開発期間は短ければ短いほど良いと思います。 これはイメージが着きやすいですよね。リリースが早くなればユーザーの満足度も高くなりますし、不具合対応も早ければ早いほど鎮火がスムーズです。 その中でも特に勉強はスピードを重視せよ スピード>正確性。これは僕の持論です。 昔のインターネット黎明期ならまだしも、現代のプログラミング界隈は 情報が多く 移り変わりが早く 透明度が高い(公開しやすく、指摘されやすい) です。 去年正しかったものが今年間違っていることはよくあることですね。 その中、僕が陥ったのは 情報が多すぎて正解がわからない 頑張って正解を調べた結果、いつの間にか古くなってる 結果、それは違う(古い)と指摘される 結果、モチベ低下する。 この解決策を考えた結果、考えるのをやめました ひたすら目の前のしたいこと、作りたい自作アプリの開発をしまくりました。 すると気持ちのいいくらいに知識が入ってきます。 なんでだろう?これにいくつかの答えを出しました。 失敗するまでが早い これは一番最初に思いつきました。 知らないことを検索するより、やってみてハマったところを少し勉強して実装する。 少し勉強しての少しがキモです。ここでガッツリ勉強するとハマった(完成しない)ままです。なぜそうなるかというと情報が多すぎて、情報の取捨選択をしているうちに刻々と時間が過ぎていくからです。ぶっちゃけなんとなくでいいとすら思っています。結果100%とは言えないまでも、60%くらいの知識がそこで付きますね。 単位あたりの勉強量が多くなる 勉強量/時間が当然増えます。 だって たくさん書いて たくさん失敗して たくさんやり直して いますから。 成果が出るのが早い これは感じている人が多いのではないでしょうか。成果が出ないと面白くないです。 早いほど成果が出て、楽しいですよ。 スピードは歳とってからはつかない スピードがないまま年をとると鈍くなってしまいます。ディスってるわけではありません。PC使えない世代、スマホ使えない世代のように新しい技術を噛み砕く力がなくなってしまいます。 逆に昔からスピード重視の方は、70歳プログラマーなど噛み砕くのが速いです。 この訓練だと思いましょう。 つまりここで何が言いたいかというと、 とにかく これであってるの?間違ってないの?指摘怖い・・・ 最初からコードを美しく、完璧なものにしたい。 この考えを捨て、今すぐ勉強ないしはコーディングを始めてください。 リファクタリングという考えもあります。 完璧でないコードを書いてしまったら「リファクタリングの勉強できるじゃん」とポジティブにとらえてください。 早く始めないと勉強している間に歳をとってしまいます。 「正確性を軽視せよ。」という意味ではない ユーザーがいないアプリならまだしも、いるアプリでバグがあれば当然直しますし、そもそもバグを出すな。これは僕もそう思います。 僕が言いたいのは最初から正確性を追究するなということです。 皆さんアジャイル好きじゃないですか。勉強方法もアジャイルにしましょう。(もしかしてアジャイルももう古いですかね?) それ・・・ユーザーに関係ある? 次は開発の方のお話をしたいです。 実はこれ実話でありました。 リリースが迫っているのに明らかに間に合わない修正をしたいという話でした。 処理としてのバグはありませんが、ソース上では確かに読みにくいものでした。 説得してマイナーバージョンで対応しましたが、完璧を求める過ぎるあまり自分しか見えていないエンジニア結構いると思います。(ちょっと偉そうですかね?) これは僕も陥ったことがありまして、あるレガシーコードを修正していたのですが、あまりにレガシーなのでイライラしていたんだと思います。 このときこう言いました。 >早く修正させてください!リスク(時間的な)を冒してでも対応するべきです! デザインや処理系は確かに古いですが、問題なく使えてるシステムでした。 >それ、ユーザーに関係ある?修正するのは良いけど、リスクは取らないよ。 今考えるとまさに自分のことしか考えていませんでしたね。 完璧を求めすぎて、周りが見えなくなるというのはこういうこと。 逆バージョンも見たことがあります。 >ユーザーに頼まれてこの処理をこう変えたいんだけど・・・ といわれ、 >え、この処理をこう変えるとコードが汚くなるのでやりたくないです。 コードの美しさ 処理系の完璧さ 使用言語 いろいろなこだわりがありますがユーザーがいる以上、できるだけ捨てられるようにしたほうが良いと思います。 ユーザー=神様ではない でも、ユーザー>コードですよね。 まとめ 自分のエンジンの最高回転数を上げるために、多少荒くていいのでスピード重視で勉強する。 ユーザーを相手にするときは、ユーザーを労って運転してあげる。 コーディングなんて割と適当で良いと思っています。 大事なのは最後までコードのお世話をすることです。 ちょっと挑戦的な記事でしたでしょうか? 去年いろいろあって、思うところがあり記事にしてみました。 また加筆修正すると思いますので、そのときはよしなに。
【個人開発】新年早々、今年の抱負をつぶやくWEBアプリをつくってみた
2019/01/01 2019/05/17【個人開発】新年早々、今年の抱負をつぶやくWEBアプリをつくってみた 明けましておめでとうございます。 本年もよろしくお願いいたします。 早速ですが、新年早々アプリを作成しました。 今年の抱負メーカー アイデアを思いついて形にするまでに3日間という短い期間で作成したので、それまでの過程を記事にしようと思います。 アプリの内容 使い方は非常に簡単で、名前と今年の抱負を入力するだけで半紙に筆で書いた抱負画像を生成することができます。 下の方にある「つぶやく」ボタンをクリックしてツイッターにアップロードするという流れです。 他にはシェアした抱負が見られたランキング、PVランキングを搭載したぐらいですね(よくあるパターンです) こんな感じ アプリ作成の流れ 開発期間は3日間なので特に目立ったことはしていません。 アイデアを形にするまでにダッシュもダッシュ、猛ダッシュで作成しました。 https://ambition.nozomi.bike この子のドメインも既に買ってあるやつから適当に追加しました。 では、開発の流れにいきます。 1日目 アイデアを思いつき、それを形に使用と決意。 それと同時に競合してそうなサービスがあるかなどを調査。これは同じようなサービスを作成されている先人がいる場合、できるだけ作りを参考にしたり、自分の個性を表現する機会を得るためです。 個人的には学ぶは真似ぶ、同じような物があれば積極的に参考にしていきます。 今回は10分位調べて結果なさそうだったので、完全オリジナルです。 ついでにこのあたりから頭の中で設計開始。「2画面くらいやな〜」と思いながら構想を練ってました。 2日目 コーディング開始 ちなみに使用技術は Laravel Vue.js この2つです。目立った技術は使用していません。 敷いてあげるのであれば、画像生成のところを苦労しました。 Laravelで画像生成処理が搭載されているものと踏んでいたのでサクサク進むかなぁと思っていたのですが、見つけられずに自作しました。(もしこういう便利機能あるよって知っている方おりましたらご教示いただきたい。) 画像生成 大体こんな感じ copied./** * 抱負画像を作成します。 * * @param string $ambition 抱負 * @return string $toImage 保存先 */ private function createAmbitionImage(string $ambition) { // 元になる画像を取得 $image = imagecreatefromjpeg('./dist/001.jpg'); // ファイル名 $fileName = str_random(32).'.jpg'; // 保存先 $toImage = Ambitions::AMBITION_FILE_PATH.$fileName; // 設定情報 $size = 150; $x = imagesx($image) / 2 - 75; $y = 80; $color = imagecolorallocate($image, 0, 0, 0); $font = "../public/font/fude.ttf"; $verticalString = $this->convertStringToVirtival($ambition); if (count($verticalString) > 1) { $x = imagesx($image) / count($verticalString) * (count($verticalString) - 1) - 75; } // 画像と文字を合成 foreach ($verticalString as $string) { imagettftext($image, $size, 0, $x, $y, $color, $font, $string); $x -= 75 * 4; } imagejpeg($image, $toImage, 100); imagedestroy($image); $imagePath = asset(Ambitions::AMBITION_FILE_PATH. $fileName); return $imagePath; } /** * 文字列の文字と文字の間に改行コードを挿入し、縦書き対応にします。 * * @param string $string 対象文字列 * @return array $verticalString 変換後文字列 */ private function convertStringToVirtival(string $string) { $l = mb_strlen($string, 'UTF-8'); $chunked = array(); $col = 0; for ($i = 0; $i < $l; $i++) { if ($i % 7 == 0) { $col++; for ($j = 0; $j < $col; $j++) { $chunked[$col][] = ''; } } $chunked[$col][] = mb_substr($string, $i, 1, 'UTF-8'); } //配列を改行でjoin $verticalString = array(); foreach ($chunked as &$colValue) { $verticalString[] = join("\n", $colValue); } return $verticalString; } こう見るとマジックナンバー多いですね・・・課題に挙げられます。 image~系のメソッドを駆使して画像を生成しています。 正直このあたりは初めてのことでしたので、手探りでした。ここの機能開発に少し時間を取られたくらいでしょうか。 DB周り 他の機能に関してはただのDB通信なのでサクッと作成。 フロント この部分は今後のことを考えてVueで作成しました。 本当は全部Vueに任せるつもりだったんですが、時間がなくて慣れてる開発手法でやってしまったのが原因です。 フロントは只今勉強中・・・他のアプリは全てVueで作成しているのですが、この子は例外です。 SNS この部分はよくあるやつですね。 「SNS ボタン 設置」とかで検索すると出てきますよ。 つぶやく / 画像ダウンロード 機能として少し考え込んだのは左の「つぶやく」ボタン。 ぶっちゃけこれだけのアプリのためにOAuth認証を作るのも、使わせるのも面倒くさい じゃあもうシェアボタンで良いや。となりこの子の招待はシェアボタンです。 ボタンの作り方は copied.<a href="https://twitter.com/intent/tweet?url=URL&text=表示したいテキスト&hashtags='ハッシュタグ1',ハッシュタグ2" onClick="window.open(encodeURI(decodeURI(this.href)), 'tweetwindow', 'width=650, height=1000, personalbar=0, toolbar=0, scrollbars=1, sizable=1'); return false;" rel="nofollow" class="btn btn-primary"> <i class="fab fa-twitter-square"></i>つぶやく </a> 画像ダウンロードは普通の画像ダウンロード処理です。 3日目 広告やCSSなどの最終調整とテスト 広告 最終的に使用していませんが、vue-google-adsenseというプラグイン便利でした。使用方法はリンク先に乗っていますので、見てください。 CSS ほとんどBootstrapに頼っているので目立ったところはありません。 こういうのを凝ってしまうと時間かかるので、有り物で作成してしまうくせがあります。 テスト 自動テストするまでもないので、テストコードは書いていません。手動テストです。 作ってみて 多分最速くらいに速いサービスです。 こんなに早く完成できたのは初めてです。 もっと言えばこんなに適当に作ったもの初めてです。 クセでマージンは3倍取るのが僕のポリシーなので、普段なら1人日の作業ですね笑。 競技プログラミングをしてるみたいでかなり面白かったです。 タイムアタックみたいな感じでした。 今年の抱負メーカー 意識したこと 季節感でアプリを作成してみました。 いろんなアプリが世の中にある中で埋もれてしまいやすいものがたくさんあると思います。 しかし日本人はイベントを重視する文化をもつので、季節やイベントに関するアプリは使用頻度は低いけど、廃れにくいのかなと思いました。 今回は年末は近いのと、自分で抱負をつぶやこうと思っていたのでこれを選びました。 スピード感 かなり焦りながら作りました。 なんてたって年末はすぐそこでしたから。 スピードを高めると技術獲得スピードが上がるというのは本当かもしれない。そう思えました。 これについては別記事にしようかなと思います。 記事にしました【個人開発】勉強方法と開発手法、そのスピード感について【勉強方法】 課題 3日間というのもあって機能不足が否めません。今後のアップデートにご期待ください。 SNSボタンの使用方法に意外な使い方があるような気がしてしようがありません。 画像生成部分に違和感を感じるので、再作成するかも。 参考 フォントフリー 和紙のフリー素材が無料 最後に 業務系というかビジネス関連でトドTaskというアプリも作成しました。 ただのタスク管理アプリなので、供給過多どころではありませんが、API利用(予定)やノート機能も搭載しているので、良かったら使ってみてください。API利用については近々ドキュメント公開します。 質問あれば回答できる範囲でするのでコメントお願いします
今更だけどCropper.jsでの画像トリミングについて書いてみました!
2019/05/10 2019/05/10WEBサービスにおける画像トリミングについて考えてみた。 とある自作WEBサービスの開発中、ユーザーアイコンを登録できるようにしよう!と考えつきアップロードしたはいいものの、やっぱりトリミングできた方がいいよね。と考えGoogle先生に色々聞いてみました。 https://fengyuanchen.github.io/cropperjs/ https://fengyuanchen.github.io/cropperjs/を見る なるものを発見したのはいいのですが、そこで苦労したので備忘録ついでに書いてみました。 留意点 これまでにもCropperでのサンプルは色々上がっておりましたが、またそれとは違うパターンにしておいたはずですので参考になればと思います。 サーバーサイドについては触れていません。 サンプル https://labo.nozomi.bike/cropper/sample.html https://labo.nozomi.bike/cropper/sample.htmlを見る copied.<html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="dist/cropper.css" charset="UTF-8"> <title>Cropper Sample</title> <style> /* 下記は円形にするなら必須です。 */ .cropper-view-box, .cropper-face { border-radius: 50%; } /* 下記はできれば必要なスタイルかと思います。(厳密にはスタイルなど必要ありませんが、最低現のスタイルとしてという意味です。) */ .cropper-container{ width: 100%; } /* 下記は必須ではありません。 Sampleを見やすくするために作成しました。 */ main{ width: 50%; margin: 0 auto; } main .triming-image{ width: 100%; height: 100px; border: dashed #000 1px; cursor: pointer; } main #trimed_image{ height: 500px; } </style> </head> <body> <main> <h1>Cropper Sample</h1> <p>ここでのサンプルはTwitterアイコン風に丸型とします。</p> <p>特にCSSに指定はありません。</p> <p>各自で自由に設定してください。</p> <div class="cropper-container"> <p><input type="file" id="triming_image" name="triming_image" class="triming-image" required /></p> <p><img src="" alt="トリミング画像" id="trimed_image" style="display: none;" /></p> <p><input type="button" id="crop_btn" value="画像をトリミングして送信" /></p> </div> <p>トリミング結果が下記に表示されます。</p> <p>例ではAjaxにて送信済みでので、下記機能に特に意味がありません。</p> <p>(結果表示したところですでに送信済みですので。)</p> <p>Cropper.jsそのものに画像操作は<a href="https://fengyuanchen.github.io/cropper/" target="_blank">https://fengyuanchen.github.io/cropper/</a></p> <div id="result"></div> </main> </body> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" charset="UTF-8"></script> <script type="text/javascript" src="dist/cropper.js" charset="UTF-8"></script> <script type="text/javascript"> /** * 丸くトリミングするために必要な関数です。 * キャンバスの画像を円形に座標計算し、切り取って返しています。 */ function getRoundedCanvas(sourceCanvas) { var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); var width = sourceCanvas.width; var height = sourceCanvas.height; canvas.width = width; canvas.height = height; context.imageSmoothingEnabled = true; context.drawImage(sourceCanvas, 0, 0, width, height); context.globalCompositeOperation = 'destination-in'; context.beginPath(); context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true); context.fill(); return canvas; } $(function(){ $('#triming_image').on('change', function(event){ var trimingImage = event.target.files; // imageタグは1つしかファイルを送信できない仕組みと複数送信する仕組みの二通りありますので、サーバー側でチェックを忘れないようにしてください。 if(trimingImage.length > 1){ console.log(trimingImage.length + 'つのファイルが選択されました。'); return false; } // 改め代入します。 trimingImage = trimingImage[0]; // 画像のチェックを行いますが、あくまでjsでのチェックなのでサーバーサイドでもう一度チェックを行ってください。 if(!trimingImage.type.match('image/jp.*') // jpg jpeg でない &&!trimingImage.type.match('image/png') // png でない &&!trimingImage.type.match('image/gif') // gif でない &&!trimingImage.type.match('image/bmp') // bmp でない ){ alert('No Support ' + trimingImage.type + ' type image'); $(this).val(''); return false; } var fileReader = new FileReader(); fileReader.onload = function(e){ var int32View = new Uint8Array(e.target.result); // see https://en.wikipedia.org/wiki/List_of_file_signatures // ファイルのヘッダを参照し、マイムタイプを疑似的に取得します。フレームワークによってはもっと簡単に正確に読めるものもあります。 // 下記は厳しい設定です。正規の手順を踏んでもアップロードできないカメラなどがあります。 // (私の環境ではアクションカメラの写真などは下記に引っ掛かりました。) if((int32View.length>4 && int32View[0]==0xFF && int32View[1]==0xD8 && int32View[2]==0xFF && int32View[3]==0xE0) || (int32View.length>4 && int32View[0]==0xFF && int32View[1]==0xD8 && int32View[2]==0xFF && int32View[3]==0xDB) || (int32View.length>4 && int32View[0]==0xFF && int32View[1]==0xD8 && int32View[2]==0xFF && int32View[3]==0xD1) || (int32View.length>4 && int32View[0]==0x89 && int32View[1]==0x50 && int32View[2]==0x4E && int32View[3]==0x47) || (int32View.length>4 && int32View[0]==0x47 && int32View[1]==0x49 && int32View[2]==0x46 && int32View[3]==0x38) || (int32View.length=2 && int32View[0]==0x42 && int32View[1]==0x4D && int32View[2]==0x46 && int32View[3]==0x38) ){ // success $('#trimed_image').css('display', 'block'); $('#trimed_image').attr('src', URL.createObjectURL(trimingImage)); return true; } else { // failed alert('No Support ' + trimingImage.type + ' type image'); // exeファイルのアップロードを考えると下記よりもいいプラクティスがある可能性があります。 $('#trimed_image').val(''); return false; } }; fileReader.readAsArrayBuffer(trimingImage); fileReader.onloadend = function(e){ var image = document.getElementById('trimed_image'); var button = document.getElementById('crop_btn'); var croppable = false; var cropper = new Cropper(image, { aspectRatio: 1, viewMode: 1, ready: function () { croppable = true; }, }); // fileReaderが完了した後にボタンクリックイベントを作成する必要があります。 button.onclick = function () { var croppedCanvas; if (!croppable) { alert('トリミングする画像が設定されていません。'); return false; } // cropper.jsに用意されている機能です。 croppedCanvas = cropper.getCroppedCanvas(); // 下記toBlob関数はブラウザによって名前が違います。 var blob; if(croppedCanvas.toBlob){ croppedCanvas.toBlob(function(blob){ var trimedImageForm = new FormData(); trimedImageForm.append('blob', blob); // この例ではAjaxにて送信します。 $.ajax({ url: '', // POST送信先 type: 'post', processData: false, contentType: false, data: trimedImageForm, }).done(function( jsonResponse ){ var responese = $.parseJSON(jsonResponse); if(responese.status == 'success'){ console.log(responese); alert('アップロードしました。'); }else if(responese.status == 'error'){ alert('画像作成に失敗しました。再度お試しください。\n' + responese.msg); }else{ alert('システムエラーが発生しました。'); } }).fail(function( responese ) { alert('システムエラーが発生しました。'); // フレームワークによってはサーバーエラーをjsonで返してくれます。 var responese = $.parseJSON(jsonResponse); }); }); }else if(croppedCanvas.msToBlob){ blob = croppedCanvas.msToBlob(); var trimedImageForm = new FormData(); trimedImageForm.append('blob', blob); // この例ではAjaxにて送信します。 $.ajax({ url: '', // POST送信先 type: 'post', processData: false, contentType: false, data: trimedImageForm, }).done(function( jsonResponse ){ var responese = $.parseJSON(jsonResponse); if(responese.status == 'success'){ console.log(responese); alert('アップロードしました。'); }else if(responese.status == 'error'){ alert('画像作成に失敗しました。再度お試しください。\n' + responese.msg); }else{ alert('システムエラーが発生しました。'); } }).fail(function( responese ) { alert('システムエラーが発生しました。'); // フレームワークによってはサーバーエラーをjsonで返してくれます。 var responese = $.parseJSON(jsonResponse); }); }else{ // これは少しわからないです。申し訳ない。 imageURL = canvas.toDataURL(); } // 画面にトリミング結果を出力する場合は下記が必要です。 // 例ではAjaxにて送信済みでので、下記機能に特に意味がありません。(結果表示したところですでに送信済みですので。) var result = document.getElementById('result'); var roundedImage; roundedCanvas = getRoundedCanvas(croppedCanvas); roundedImage = document.createElement('img'); roundedImage.src = roundedCanvas.toDataURL() roundedImage.name = 'trimed'; roundedImage.id = 'trimed'; result.innerHTML = ''; result.appendChild(roundedImage); }; }; }); }); </script> </html> copied.<?php $res = array(); try{ // $_FILESで受け取れます。 $file = $_FILES['blob']; // 画像アップロードについては割愛。 $res = [ 'status' => 'success', 'msg' => 'sample01', 'obj' => $file, ]; }catch(Exception $ex){ $res = [ 'status' => 'error', 'msg' => $ex->getMessge(), 'obj' => null, ]; } echo json_encode($res); exit(); ちょっと冗長な箇所ありますが、result部分など省けばもう少しシンプルにまとまりますね。 画像選択->トリミング->アップロードまでを簡単に行いたいときには便利ではないでしょうか。 課題 Cropperの再読み込み(画像間違えちゃったー)のパターンからの復帰方法 これは公式ページを見れば改善可能ですね。 toBlob関数のブラウザ依存 3通り書きましたがtoBlobとmsToBlob以外にあるのか?しかもすべてに当てはまらない場合どのようにアップすればいいかわかりませんでした。知識のある方ご教示いただきたい。 画像トリミングの重要性 最後になりますが、画像トリミング、かなり重要だと思うんですよね。 WEBサービスだと画像アップしてもトリミングさせてくれないサービスが多いと思います。 セキュリティなども重要ですが、ユーザビリティも重要ですよね。 有名どころであれば(Google,Twitter,GitHub……)色々なアカウントサービスではアイコンをトリミングさせてくれますが、 ぶっちゃけ小さな会社のサービスとか含めてしまいますとどうなのでしょうか。 こういったコンテンツ編集系の処理はあまり得意としませんが、Youtube等の台頭で動画の編集もWEBやるのが当たり前になるかもしれませんね。(Twitterとかそうだし、もうなっているのか?) ネイティブで組まなきゃーってなっていたところがWEBに喰われている感がある今日のこの頃です。 何か指摘事項ありましたら是非ともお願いいたします。 何分ぺーぺーなもので、指摘されるために書いたまであります。 参考 cropperjs/README.md at main · fengyuanchen/cropperjs JavaScript image cropper. Contribute to fengyuanchen/cropperjs development by creating an account on...
non's Laboをリニューアルしました!
2019/05/03 2019/05/03non's Laboをリニューアルしました! 皆さんお久しぶりです。のんです。 知っている人は知っているnons'Labo。 大幅アップデートしましたー!! なぜリニューアルしたのか? non-it-infomation.comって思いっきりTypoした状態。 これで3年間近く使用してきました。 実はすぐに気づいていて、どうやって変えようか迷っていたのですが、どうも変更方法がわからない。 なので、放置していました。 ツーマッチ開発のために取得したnozomi.bikeドメインにすべて移行しようと思ったのは半年くらい前。non-it-infomation.comはAPI用とかなにか別の目的で使用かなと思っています。んで期限が切れたらそのまま解約しようかなと。 実はnon's Laboは処女作 個人開発で作成したという意味では、処女作だったんです。 当初はPHP + Smarty2っていうプレーンな状態で開発を進めていました。当時は新卒の頃だったので、プログラミングの知識は皆無(自己紹介でもあるようにもともと工学の人間)ですので、クラスの勉強やら、Html CSSの勉強やらをするために作成したブログでした。そこからWordPress風にしたり、動画と画像をアップロードできるようにしたり色々いじっていました。 しかし、そろそろ技術もついてきましたし、オンボロのこの子をなんとかピカピカにしてあげようとして、作成したのがこのアップデートです。 新しい技術を取り入れたかった結果、安定しなかった。 もともとの技術がレガシーなコードで構成されていましたので、なにかフレームワークやライブラリを入れようと、いろいろごちゃごちゃ入れまくった結果。もう汚い汚い。しかし、これも僕の資産。棚卸しを先日完了したので、作業に移った次第です。 ちなみに、non's Labo、長いこと続けてきたおかげで、SEO的に優秀(らしい)です。 少なくともnons laboで検索をしたTOPに表示されます。 だからドメインも捨てたくなかったんですが、これはしょうがない。SEOの勉強ということで色々実験してみましょう。 目的 目的って見出しをブログのエディターに書くと初代non's Laboで直に書いたの思い出しますねw 目的はあの頃とは違います。(ちなみに、あの頃の目的) 技術のアウトプットを習慣にする ブログを書くことで怠け癖を治す みんなに知られるブログにする あの頃の目的は自分のためだったけど、今度の目的はみんなのためって感じですね。このブログ見て悩みが解決したとかならとても嬉しいと思います。 使用技術 最近はこれで個人開発を行っています。慣れているので、開発スピードが早いんですよね。勉強が目的ではないので、下記を選択。 Laravel Vue このブログについて一言。 最初はブログサービスなんて山程あるし、外部のブログでやろうかなと思ったんですよ。でもQiitaなどの記事形式以外は長いこと続かなくて、、、 ちなみにWordPressを使用してみましたが、僕には合いませんでした。 多分、書かされている感あるんですよね。。。 それに比べて自作のものは使ってて嬉しいですよね。ちゃんと動いてるって感じを書きながら感じられる。きっと今のバージョンのnon's Laboにも使いにくいところがあるかもしれませんが、それを直すのも醍醐味というか楽しいところですよね。 ということでブログを書いていく中で原則を決めました。 楽しくブログを書く です!。当たり前かと思われそうですが、僕ってそもそもブログ書くのに向いてない。。。 最後に 個人開発のアプリも、連続したアップデートでユーザーさんには迷惑をかけているかもしれません。 全てはもっと良くしていこうという結果なのでご容赦を。 3度めのアップデートですが、今後ともよろしくお願いいたします。