A note of a person who is learning programming, SakaTaQ

ロック好きのプログラミング学習

BASIC認証の導入

アプリケーションをデプロイした場合、基本的に世界中のどのユーザーもログインできる状態になっている。
成果物として、他のサイトの模写サイトなどのようなクローンサイトを作成してアップロードなどをおこなった場合に、不特定多数のユーザーが誤解しないように閲覧を制限できる機能BASIC認証がある。これをRuby on Railsで導入する。

BASIC認証機能

HTTP通信規格に備わっているユーザー認証の仕組みの一つ。
ユーザー名とパスワードを設定しておき、それを知っているユーザーのみが閲覧、利用できる状態にする。
知らない場合でもアクセス自体は可能だが、ポップアップウィンドウで表示される認証項目を入力しないと画面は表示されない。

導入方法

導入するにあたって特に必要なgemなどは存在しない。
Railsには元々BASIC認証機能を導入するためのメソッドauthenticate_or_request_with_http_basicが用意されているのでそちらを利用する。

RailsアプリケーションでのBASIC認証機能を用いたログインは全てのアクセス(コントローラー)で行われるのが望ましい。
その為、app/controllers/application_controller.rbのprivateメソッドにて定義し、before_actionで呼び出すことで、上記の要件を満たすことができる。

class ApplicationController < ActionController::Base
  before_action :basic_auth

  private

  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == 'admin' && password == '2222'
    end
  end
end

application_controllerはこのコントローラーを継承している全てのコントローラーの処理が実行される前に呼び出される。
privateメソッドは呼び出された場合のみ、読み込みや処理がされる。
before_actionとしておくことで、このコントローラーが動作した場合に、basic_authメソッドが実行され、BASIC認証機能が働く。
このコードではユーザー名がadmin、パスワードは2222となっている。
これらを入力することでアプリケーションを利用することが可能。

問題点

HerokuやAWSなどのサーバーにアクセスする際に利用するパスワードや、PAY.JPなどの公開鍵•秘密鍵なども含め、基本的にこういった個人情報に繋がるような秘匿すべき情報はコードに記述することは危険。
特に今回のようにGitHubなどでOSSとして公開されている場合(Public repository)は、こういった情報は誰でも見ることができるようになってしまう。
その為、こういった情報は環境変数を利用した実装に切り替える必要がある。

また、この実装のままでは全ての環境でこのメソッドが実行されてしまう。
公開前である開発、テスト環境の時点でこの処理が実行されると非常に煩わしくなる為、本番環境(デプロイ後)のみで実行されるように記述を変えていく。
(環境変数は開発環境、テスト環境、本番環境ごとに別途設定する必要がある。
開発、テストなどのローカル環境であれば、自身のPC~/.zshrcに、本番環境であればEC2やHerokuなどのリモートマシン上の環境設定ファイルに設定する必要がある)

% vim ~/.zshrc

vimモードで.zshrcを開いて、インサートモードで書き込み(追記)を行っていく。
※ Mojave(旧Macバージョン)の場合はファイル名は.bash_profileなので注意。ファイルの格納場所は同じ。

export BASIC_AUTH_USER='admin'
export BASIC_AUTH_PASSWORD='2222'

追記が完了したら「esc」を押してコマンドモードに戻し、「:wq」で保存しながら終了する。
環境変数などの設定を変更した場合、すぐに適用されない為再読み込みコマンドを実行する。

% source ~/.zshrc

続いて本番環境の設定を行っていく。

# AWSなどを用いてデプロイする場合
% ssh -i [ダウンロードした鍵の名前].pem ec2-user@[作成したEC2インスタンスに紐づいたElastic IP]
例)% ssh -i adminman.pem ec2-user@111.222.333.444 のような形
% sudo vim /etc/environment

# ファイルを開いたらiでインサートモードにして環境設定ファイルに「追記」を行う。
BASIC_AUTH_USER='admin'
BASIC_AUTH_PASSWORD='2222'

# esc→:wqで保存終了した後に設定を適用する為、一度リモートマシンからログアウトし再度ログイン
% exit
% ssh -i [ダウンロードした鍵の名前].pem ec2-user@[作成したEC2インスタンスに紐づいたElastic IP]

# 設定した環境変数を確認するためのコマンド
% env | grep BASIC_AUTH_USER
% env | grep BASIC_AUTH_PASSWORD


# Herokuを用いてデプロイする場合
# % heroku config:set KEY=VALUE の形

% heroku login

% heroku config:set BASIC_AUTH_NAME='admin'
% heroku config:set BASIC_AUTH_PASSWORD='2222'

AWSでの場合の1行目は、sshを利用した接続で、ダウンロードした鍵を使用してEC2にログインしている。
ssh接続では通信が暗号化されているので安全に通信できる。また、公開鍵と秘密鍵を利用した鍵認証を利用することでより安全に接続を行うことも可能。
-iオプションは秘密鍵である(identityファイル)を指定している。この時、AWSの設定時にダウンロードしたpemキーを記述。 ログインできたら2行目のコマンドでリモートマシン上の環境変数を設定するファイルに書き込みを行うためにsudo(スーパーユーザー)vimモードにて開いている。
ファイルを開くことができたら、元々書いてある記述などを消さないように注意。
環境設定を適用するために再度ログインを行い、env | grepコマンドにて設定した環境変数をの確認を行っている。
なお、通常は本番環境のリモートマシンでも~/.zshrcがあるのでそちらに記述を行えば環境変数の使用は可能だが、Capistranoで自動デプロイをする際には本番環境にある~/.zshrcを参照しないようにプログラムが実行(ユーザーのログインシェルが割り当てられない)されるため、/etc/envirnmentに定義する必要がある為この方法を採った。

~/.zshrc環境変数を記述した場合は、config/deploy.rbにて

set :default_env, {
  rbenv_root: "/usr/local/rbenv",
  path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH",
  BASIC_AUTH_USER: ENV["BASIC_AUTH_USER"]
  BASIC_AUTH_PASSWORD: ENV["BASIC_AUTH_PASSWORD"]
}

のように記述してどの環境変数を使用するか明示する必要がある。 他にもcredentials.yml.encとかにも色々と書く必要があるのであれば、最初の方法を採った方が導入しやすそうです。

Herokuでの場合、スペースで区切れば複数の環境変数を同時に設定することが可能。
heroku config:set BASIC_AUTH_NAME='admin' BASIC_AUTH_PASSWORD='2222'
環境変数特殊文字などが入っている場合はシングルクォーテーションで囲むこと。
どう設定されたかを確認するにはheroku configでターミナル上で見ることができる。

ソースコード変更

# controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :basic_auth
  protect_from_forgery with: :exception

  private

  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"]
    end
  end
end

ENV["BASIC_AUTH_USER"]のような変数を環境変数と呼ぶそうです。
環境設定ファイルに定義されてるものはこういった呼び方になるって認識。

本番環境のみBASIC認証を行う

Herokuでのデプロイの場合は
username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"] if Rails.env.production?
に書き直すだけで問題なく導入できるようです。

AWS(with Capistrano)の場合は

# config/deploy/production.rb の10行目付近にあるコメントアウト下に追記
server "(EC2のIPアドレス)", user: "ec2-user", roles: %w{app db web}
例)server "111.222.333.444", user: "ec2-user", roles: %w{app db web}

set :rails_env, "production"
set :unicorn_rack_env, "production"

# role-based syntax
# ==================

この記述でUnicornが現在の環境を本番環境として認識する。application_controllerも少し記述を加える。

class ApplicationController < ActionController::Base
  before_action :basic_auth, if: :production?
  protect_from_forgery with: :exception

  private

  def production?
    Rails.env.production?
  end

  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"]
    end
  end
end

production?というメソッドを定義し、現在の環境を条件分岐にてtrueかfalseを返すようにしたもの(Rails.env.production?)を、before_action :basic_authの後にif: :production?と記述することにより、本番環境のみでbasic_authメソッドが実行されるようにしている。
なお、この記述は前述したHerokuデプロイ時の記述と動作としては差はない。

BASIC認証の動作確認

※ 初めてcapistranoで自動デプロイをしてBASIC認証の動作確認をする場合のみ、unicornの停止と起動も行ってから動作確認をする。

% bundle exec cap production deploy unicorn:stop
(デプロイ完了まで待ち、完了後に以下を実行)

% bundle exec cap production deploy unicorn:start

うまく起動できない場合にはAWSのEC2コンソールを再起動し、njinx MySQL Unicornを手動で順番に再起動させる。 その際Unicorn

$ cd /var/www/アプリケーション名/current
$ bundle exec unicorn -c /var/www/アプリケーション名/current/config/unicorn.rb -E production -D

のように起動する。

手動、もしくはcapistranoを用いた自動デプロイを行ってアクセスできる状態を整えたら動作確認を行う。

参考にさせていただいた記事
今更だけどRailsアプリケーションにBasic認証を設定した
[Heroku] ReactのアプリのBASIC認証の設定方法
Rails.env


今回はここまで。

デプロイ時に使用するサーバーによって設定する量が少し違うので全体的にごちゃっとした感じが否めませんが...

BASIC認証は導入はしやすいけど完全に信頼における認証方式ではなく、「認証した際の通信経路でユーザー名とパスワードが取得可能」だったり、ログアウト機能は自身で実装する必要がある。
また、複数のサーバーを跨いだ認証は難しくなるようです。
絶対に安全なものとは言い難いけど、最低限の認証機能を採用したい場合の選択肢の一つとして導入する、というくらいの認識の方が望ましいようですね。

したらな❗️ 👋

Herokuにデプロイしたアプリの更新

前回デプロイしたアプリケーションについていくつか更新をしたのですが、GitHubにpushしても自動デプロイされてなかったので、アプリが停止している状態では更新されないのではとか、色々と解釈を間違えている部分があったのではないか、検索不足の部分があったのでは?ということで、少し調べてみました。

実際に行ったこと

とりあえずログイン

% heroku login

コマンドを叩いたら起きるのかな?とかまぁ分からんなりに打ってみる。

% heroku ps

プロセスの確認ができるコマンド。とりあえず現状は起動できているようでした。
f:id:saka20taku43:20200608141055p:plain 参考にさせていただいた記事
Herokuのアプリケーションの停止 / 再開
他にも自分でcurlコマンドを打って確認するなど方法があるようです。
何かあったらまた参考にしようかと。


で、自動でできないのであれば手動で行うか〜ということで

% git push heroku master

エラーが吐き出された。
f:id:saka20taku43:20200608141427p:plain なるほど、自動デプロイ自体は行われてたのだけど失敗して更新できてなかったというのがわかった。
GitHubのリンクに問題がないか念のため確認したのですが、Herokuのブラウザのアプリ各個ページのOverviewタブにてBuild failed/Yesterday at 〜となっていたことから、昨日のタイミングで失敗して更新できてないことも分かった。

で、エラーが最初に返された(と思っていた)Precompiling assets failed.について検索。
参考にさせていただいた記事 Herokuにpushしたらprecompile assets faild.が。pushできたと思ったらApplication Errorが。解決するまで。

こちらにあるようなcss関連については全てscssで統一していた為、問題ないというのは分かっていたのですが、読み進めていたらエラ〜ログのずっと前にエラーが出ていた部分があるようなので、自分も確認してみました。
f:id:saka20taku43:20200608143012p:plain
完全に見逃していました。ES6っていうのがJavaScriptのバージョンというのは分かるけど、何が関係あるのと思って再びググる君。

参考にさせていただいた記事 【Rails】ES6が原因でHerokuへのデプロイ失敗 Uglifier::Error: Unexpected token:~
最初の方にサクッと書いてくれてます。 ようは、ES6から使えるletを記述すると自動的にES6のバージョンとみなされ、そのES6はUglifierというgemはES5までしか対応してないのでエラーが返っている状態とのことでした。

サイトにあるように環境設定ファイルを変えるか、今回の僕のアプリのようにletを使っている場所が少なかったり、規模が小さい物ならそのコード自体を書き換えることで対応できました。
書き換えて、GitHubにpushしてみた所、そのままHeroku Appに自動でデプロイがされたのが確認できました。
今回の更新ではDB情報を少し書き換えたものがデプロイされてるので、

% heroku run rake db:migrate:status

で、Heroku上のアプリケーションのマイグレーションファイルの状況の確認。
f:id:saka20taku43:20200608144534p:plain

% heroku run rake db:migrate

// 問題なく移行完了、一応確認
% heroku run rake db:migrate:status

f:id:saka20taku43:20200608150716p:plain

// 一通り終わったらログアウトしておく
% heroku logout

とりあえず今回はこれだけ!

したらな❗️ 👋

canvasで書いたデータをDBに保存(canvas +JavaScript + Rails)

canvas APIで描画したデータを保存する方法を学んだので実際に行ったことをアウトプットしていきます。

結論として、最初から保存される状態や形式、データ型などに拘らなければすぐ終わったのですが、随分と遠回りしてしまいました😅
最終的にはcanvasデータをバイナリ形式のままでDBに保存、読み込み編集できるというのと、画像としてPCに保存できるようにするということで2つに分けて実装することにしました。


バージョンについては前回のアプリをベースにしています。

Rails 5.2.3
rbenv 1.1.2
ruby 2.5.1

はじめに

スクール学習中に経験したのですが、jQueryRails上で反映させるにあたりgem 'turbolinks'の有無でローカル環境とデプロイ環境で動作が変わりました。
残したままでlink_toタグにdata: {'turbolinks' => false}を付与してリンク先で切っても、デプロイ環境ではエラーが出続けたり色々あったので、この辺りは大体切っています。
後は、application.jsのrequire部分のrails-ujsjQuery-ujs。この辺も競合したのでどっちかだけ残すように気をつけています。

canvasデータを画像としてPCへ保存

これは公式や個人記事でのソースがあったので色々参考にしながら実装しました。
割と決まった書き方になっていたのでそのまま写している部分もあります。

とりあえず「保存」というボタンを実装し、JavaScript(jQuery)でイベントを書いていく。

$('#canvas-submit').on('click', function(){
  // 処理用の関数などを書く
});

ボタンを押した時の処理をする関数をsaveCanvas()として関数を作成していく。

function saveCanvas(){
    let imageType = "image/png";
    let fileName = "sample.png";
    var base64 = cvs.toDataURL(imageType);
    var blob = base64toBlob(base64);
    var url = (window.URL || window.webkitURL);
    var dataUrl = url.createObjectURL(blob);
    var a = document.getElementById('canvas-submit');
    a.href = dataUrl;
    a.download = fileName;
  }

function base64toBlob(base64){
    var tmp = base64.split(',');
    var data = atob(tmp[1]);
    var mime = tmp[0].split(':')[1].split(';')[0];
    var buf = new Uint8Array(data.length);
    for (var i = 0; i < data.length; i++) {
      buf[i] = data.charCodeAt(i);
    }
    var blob = new Blob([buf.buffer], { type: mime });
    return blob;
  }

1つ目の関数について。1,2行目は変数宣言。これらは後で利用します。
3行目はcanvasタグのURLをbase64形式で取得している。
4行目は下記関数を呼び出しのコード。base64データををblobデータに変換している。
5行目はブラウザの種類?(Chrome, Safariなど)URLオブジェクトを作成した際に変数名が異なるので名前を統一する為に変数宣言して代入させている。
6行目はcreateObjectURL()という引数で指定されたオブジェクトを表すURLを生成するメソッドを5行目で宣言した変数urlに使用させているコード。引数には4行目で変換したblobデータを渡している。
最終的にaタグのリンクに持たせるダウンロード用のURLを作成している。
7行目ではクリックしたボタン要素を変数に代入。8、9行目はダウンロード用のURLの用意とファイル名をセット。
最終的には、クリックするとcanvasデータをPCでダウンロードするようになっている。

下側の関数では前述したとおりbase64データをblobデータに変換している。
1行目はカンマで分割してデータを配列で分けてtmpに代入、tmp[0]:データ形式(data:image/png;base64,)、tmp[1]:base64データ
2行目ではbase64のデータをatobメソッドでデコード。ランダムな文字列が並んでいるbase64データを引数に渡している。
3行目はtmp[0]の文字列(data:image/png;base64)からコンテンツタイプ(image/png)部分を取得し代入(=> image/png;base64 => image/png)
4行目では1文字ごとにUTF-16コードを表す 0から65535 の整数を取得、newなのでオブジェクトを生成してるのかな。
5行目からのfor文はcharCodeAt()メソッドで、与えられたインデックスに位置する文字の UTF-16 コードを表す 0 から 65535 の整数を返している。
この値はコンソールで表示させると配列で表示されるのは確認している。
その下の変数blobはblobオブジェクトを作成。引数にはこれまでで用意した変数を渡している。
最後は呼び出した関数base64toBlob()にreturnで値を返している。

参考にさせていただいた記事
HTMLCanvasElement.toBlob()
String.prototype.charCodeAt()
Blob()
Canvas上のイメージを画像ファイルとして保存する方法
Canvas に描いた画像を png などの形式の Blob に変換する方法

canvasデータをDBに画像ファイル(.pngとか)として保存させる

色々あって失敗。file_fieldに直接value渡そうとして、セキュリティ上の問題で対策されていて実装に失敗したり。最終的にたどり着いた記事
最終的には「そもそも各投稿毎に保存されて編集もできるようにする」という要件を満たす為に、画像として色々変換させてから保存させるのって読み込む時も面倒になるんじゃってことで断念。

DBにバイナリデータとして保存させる

再度読み込み編集、更新する物を変換しきってしまうのは面倒だと思ったので、Railsのデータ型を変えて保存させてしまった方が早いのでは?となり。
postsテーブルのimageカラムのデータ型をtextbinaryに変更するためのマイグレーションファイルの発行。

% rails g migration change_data_image_to_post

生成された中身。

class ChangeDataImageToPost < ActiveRecord::Migration[5.2]
  def change
  end
end

RailsのMigrationでbinary型を選択すると、MySQLを使用した場合はblob型で解釈されるとのこと。
また、binary型はdefaultでは64KB(65535Byte)までしか保存できないというのを目にしました。
canvasのデータが書き込まれる毎にサイズが増えていく、というのをコンソール上でBlob{size: 79345}のような形で確認。
簡単に64KBは超えてしまっていたのでmediumblob(最大16MB)に変えます。
下記のように書くとマイグレーション後にはdb/schema.rblimit: 16777215となっていることが確認できます。

class ChangeDataImageToPost < ActiveRecord::Migration[5.2]
  def change
    change_column :posts, :image, :binary, limit: 10.megabyte
  end
end

最初に発行した時にpostsと記入しなかったので、1行目複数形でないのが少し気になりましたが書き換えてしまうとマイグレーションできなかったので名前はこのまま。
change_column :posts, :image, :binary, limit: 10.megabyte
メソッド テーブル名 カラム名 データ型 オプションの順番で書かれている。
基本的な書き方についてや、メソッドの種類、オプション種類については一通り公式ドキュメントに書いてある。
Railsドキュメント/マイグレーションとは
細かくどういうことをするのかとかは説明の上手い人の記事見たりしながら自分のアプリ上で何が起こるか目にした方が早いです。

% rails db:migrate

でDBにマイグレーション情報を適用させる。適用できたかどうかは% rails db:migrateをする前後で

% rails db:migrate:status

で確認すれば差異がわかる。移行前は以下のような感じで表示される。

database: freehands_writer_development

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20200521070549  Create posts
  down    20200602044245  Change data image to post

downはまだマイグレーションされてない状態。
% rails db:migrateした後に確認して二つともupになっていればOK。

参考にさせていただいた記事を貼っておきます
ActiveRecordでbinary型をblob以外の型にする
【Rails・MySQL】MySQLのデータ型とRailsのマイグレーションファイルのデータ定義の対応まとめ

バイナリデータを投稿できるようにしていく

送信ボタンを押した時にcanvasのデータを送信できるようにJavaScriptを書いていく。

// form.html.erb
// labelタグは必要なくなる + file_fieldから変更。
<%= form.hidden_field :image, class: 'hidden' , value: '' %>

//index.erb.html, show.erb.htmlなどのビューではimage_tagは外しておく。
$("#post_submit").on("click", function(e) {
  e.preventDefault();
  var base64 = cvs.toDataURL();
  $('#post_image').val(base64);
  $('#new_post').submit();
});

file_fieldではvalueを渡せなかったのでフォームヘルパーを変更し、hidden_fieldを使用。
念のためconsole.logなどでvalに挿入できたか確認→OK。
atobメソッドでデコードをしなかったらこんな感じ(つーか、blobにするとエンコードしないとRailsからエラー返されて表示すらできない)。
1行目のpreventDefault();がないとイベントが発生したらformの動作が勝手に進行してしまうのでここで止めてる...というくらいの感覚で使用してます。
一応確認として、引数eをconsole.logしてみるとjquery.fn.initが取得できた。押した時のDOM要素とイベント~.submitみたいなのが書いてあったので、これに対してpreventDefault()を使用することで止めているのが分かる。
最後に処理を止めているform要素のidに対して、submitメソッドを使用することでDBにデータ送信。
この流れでbase64データをDBに保存することができた。

投稿したデータを表示できるようにする

Base64形式で保存された物はimage_tagで、src属性に@post.imageを指定しておくだけで、そのまま画像として表示することができる。
表示できるデータがない場合は、alt: 'No Image'で何もデータがない時に表示させる文字を指定しておく。
DBからデータを読み込んだ際にJavaScriptを使ってcanvasに反映させるには、Railsの変数をJSに渡して使用する必要があるのではないかと思い、それを可能にするgem 'gon'を導入。

// どの環境においても使用するため、Gemfile最下部に記述
gem 'gon'

// 対象ディレクトリ上にcdして
% bundle install

// application.html.erb のheadタグ内で記述してgonを使って読み込みできるようにしておく
<%= include_gon %>

これでgem 'gon'を使用する準備はできたので、次は実際に繋ぐためのコードを書いていく。

def edit
  @post = Post.find(params[:id])
  gon.post_image = @post.image
end

各投稿のcanvasデータを編集するedit.html.erbに変遷した時、gonを使用してRailsで変数宣言。
変数名の前にgon.を書くことでこの変数をJavaScriptで使用することができる。

後はこの変数を使ってcanvas上にBase64の画像データを読み込ませるように記述していく。

var img = new Image();
img.src = gon.post_image;
img.onload = function(){
  conText.drawImage(img, 0, 0, 800, 600);
}

1行目では画像オブジェクトを生成し、2行目で先ほど表示させたようにsrc属性にBase64データを代入。
ここでRailsのgonを使用して定義した変数を使用している。
3行目~4行目で画像データををcanvasに設定している。引数は表示する画像、x, y, 大きさx, 大きさy。
0にしないと表示する場所が微妙に変わってしまうのと、大きさの数値を元の数値と合わせないと拡縮されてしまうので注意が必要。

参考にさせていただいた記事
【gem gon】Railsで定義した変数をさくっとJavascriptで使う
[JavaScript] Base64形式の画像データをcanvasに表示する


名前を間違えたり、gem 'gon'にたどり着くまで時間がかかった。
色々と可能性を感じるgem。また使える機会があれば!

したらな❗️ 👋

canvas API について使ってみたのでまとめてみる

フリーハンドでメモを取るアプリ作成のためにちと齧りました。
紙にメモ → iPhone, iPadでサクッとメモが取れるようにする...のが目的ですが実装段階ではPCで作業を行っているので今回はまずPCで実装することを目指しています。

個人的にこれまで殆どの人が必要なかったであろうタッチペンの出番はここだと思ってます

いつも通り、自分のための備忘録っすね。

Canvas APIって何?

HTMLにある<canvas>タグとJavaScriptで描画(矩形描画など含む)をしたり、参考サイトにある通り、他にも色々と用途があるようです。因みにcanvasは閉じタグは必須のようです。
サイト下部のチュートリアルにもっと細かい説明があります。

実装するにあたり詰まったところ

canvasタグはデフォルトでサイズがw300 * h150と決まっているようですが、これまでと違いサイズ指定をタグに直接しないと後々描画できるようになった時に上手く描画できなくなります。
どうもデフォルトで大きさが設定されているのが原因のようですが、cssで適用するプロパティは相対的なサイズとして適用されるため収縮されたりするということが起こるようです。
これについては最初原因がさっぱり不明だったためこちらのサイトを参考にさせていただきました。
今回のケースでいくと、自分はcsscanvasタグのclassにwidth:800px;を適用しましたが、canvasタグ上でwidthを指定せずにデフォルトのままにすると300pxなので、8/3(≒ 2.333...)倍にcanvas上で描画した図が引き延ばされてしまっていたということですね。
canvasタグ自体は大きさはきちんと800pxになったため、描画できるようになってから初めて気がつきました😅


画面サイズに合わせて#canvasを拡縮させたい場合は

<div id="candiv">
  <canvas id="canvas"></canvas>
</div>

JavaScriptでの以下の処理はページを最初に開いた時だけがいいかもしれません。

var wd = $('#candiv').width();
var ht = $('#candiv').height();
$('#canvas').attr('width', wd);
$('#canvas').attr('height', ht);

この時、幅と高さの取得先であるcanvasの親要素の#candivは開いているブラウザウィンドウのサイズによってwidth, heightが変動するように、1000pxなどの絶対値で指定しないようにする必要があります。
100% x 100%にしておくのがわかりやすいかもしれません。

また、実装時はPCにてcanvas要素の描画領域を絶対値で指定していたため分かりませんでしたが、これに変更が加わるとそれまで描画していた内容は失われるようです。
スマートデバイスなどで回転させた時に領域変更のコードなどを都度読み込みさせると、変化する度に描画内容が消えてしまうことになるので、サイズ決定のタイミングは注意が必要ですね。

使い始める前に

canvasタグを作っただけでは真っ白い画面があるだけで、クリックなどを押しても何も反応することはないです。
ここに描画をするためにはJavaScriptcanvas要素から描画コンテキスト(描画ができる状況、場面)というのを取得する必要があるようです。

var cvs = $("#canvas")[0]
var conText = cvs.getContext("2d");

このgetContext()が取得するために使うメソッド。正式にはCanvasRenderingContext2Dという名前のインターフェースということで。
2dとあるのは2次元のことで、これを()に書くことで2次元グラフィックスを書くことができる描画コンテキストを取得するようです。
getContext('2d').createLinearGradient()のようにグラデーションが掛かった線なども描画できます。沢山あるので詳細はリンク先にて。
[0]を記述している理由についてはconsole.log($('#canvas'));をすれば分かりますが、jQuery.fn.initっていうのが返されて、これに対してコンテキストを取得しようとしても失敗するからです。
このオブジェクト(って呼び方で合ってるかな?)をコンソール上で展開していくと、配列になっていて配列の一番最初にDOM要素である<canvas>が入っているので、[0]と添字をつけて呼び出しています。
なお、JavaScriptで記述しているdocument.getElementById('canvas');であればDOM要素を直接取得できるようです。

描画をするにあたって

まずパスというものを描く必要があります。
最終的に実際の画面に描画されて表示されることになる文字や線などは点の集合で、それがどういう風に繋がるか、どんな色かは指定可能です。
パスはその点の集合体をどこに描くのかを決めるために描く必要があります。
原文に近い表現をするとよくわからないので少しだけ噛み砕いたつもりですが、ザックリというと線を描く場所を決める為の下書きをするみたいなことだと思います。
パスの設定でどこに線を描くのかを決めるだけで、実際にはその後にstrokeというものを使って(パスが描かれた場所に)線を描く、という動作を行うことで決められた図形を描画したり、自由に線を書いたりすることができる、というイメージです。
パスはどこから始まってどこで終わるか指定できます。 また、パスを開始するメソッドを再度利用することで、次に描く時は前のパスはリセットされる仕組みになっているようです。
そうでないと同じところに延々と書き続けてしまうことになるので、仕様としては寧ろ当然ですね😅

グダグダと書き殴りましたが実際にパスの描画で使うメソッドは

// パスの開始
.beginPath();

// パスの終了
.closePath();

だけです!名前からも想像しやすいし、分かりやすいですね!

また、パスが描かれた場所に実際に線などを描画を行う場合には

// 輪郭をなぞる方式で図形を描く、と参考サイトにはありますが要はこれで実線が描ける、の方がパッと分かりやすいと思います。
.stroke();

// パスで指定した範囲「内」を全て塗りつぶすメソッド
.fill()

これらのメソッドは描画コンテキストに対して行われる必要があるので

conText.beginPath();
conText.closePath();
conText.stroke();

のように記述することになります。

勿論これだけではどうしようもない

上記の状態はパスを開始終了して描画させているだけになるので、当然何も描けません。
「どこから、どれだけ or どこまで」描くのかはまだ決めてないですね。

注記に少し書いてありますが、canvasを作成した直後や、beginPath();を使用した後に現在のパスの指定が空の状態の時は、その実際の状態がなんであれ最初にパスを構築するコマンドは後述するmoveTo();として扱われるようです。
パスをリセットした後は、開始位置を明示する必要が出てくるとのこと。

.moveTo({x}, {y});は描画をすること自体はしないですが、描画し始める場所にパスを引く為のペンを置くようなイメージのメソッドです。
.beginTo()はこれより先に記述するメソッドなので、「ここから書き始めるよ」っていう合図みたいな役割だと僕はイメージしやすい。

線を引くにはlineTo({x}, {y})メソッドを使用します。
現在の位置(moveTo();の次にこのメソッドがある場合は、現在の位置 = moveToの位置)から、lineToのx軸、y軸を終点としてパスの線を引く感じです。
参考サイトのように決められた図形(例えば三角形)を描く場合はmoveTo→lineTo→lineToで3つの頂点を指定することでパス描画することができます。

パスが引き終わったら、次に行う動作によって使うメソッドが少し変わります。
.fill();を使用してパス指定した内部を塗り潰しした場合、自動的にパスが閉じる為、.closePath();が必要ないようです。
.stroke();の場合にはパスを閉じてから、strokeを使って実際に描画を行う...という流れのようです。

因みに、一度に図形を描く場合で、離れた場所にペンを移動させる場合は、lineToを使わないでmoveToのみを使ってペンを移動させるように使うとここにあるスマイリー君のように描画することもできるようです。
スタンプに近い機能ですね。上手く使えば図形がメインのちょっとした画像や、ER図もどきみたいなのも再現できそうです。
そのまま下部や次のページ以降を見ていくと円弧を描く為のarc();メソッドや2次曲線みたいなのも描けるという例や、オブジェクトを使う例、色、スタイル、画像加工...が紹介されています。

かなり後半のページでcanvasの情報をクリアにするclearRect()が紹介されています。
これを使用することでこれまで描画した内容をまとめて消してしまいたい時に、ボタンを押したら全部消える、というのを再現できるようになります。

またこちらでは、現在のcanvasの全状態をスタックに保存するsave()、最後にセーブされた状態を復元するrestore()が紹介されています。
描画後に自動でセーブされるようにしておけば、うっかり消してしまっても復元できます。
ただこの場合は変な書き込みをした場合にそれを戻すことができないので微妙な気もしますが...

これまでの実装をまとめると

var cvs = $("#canvas")[0]
var conText = cvs.getContext("2d");

conText.beginPath();
conText.moveTo({x}, {y});
conText.lineTo({a}, {b});
conText.closePath();
conText.stroke();

{ }はそれぞれ任意の値が入ります。少なくとも同じ値が入ってなければ、始点であるmoveToの位置から、lineToの終点まで線を引くことができます。

手書きするために

ここまではチュートリアルに沿って決められた内容を描画したりすることができる、と紹介だけになってしまいましたが、本来の目的はフリーハンドでメモを取ることです。

手書きをするにあたっては自分がクリックした座標(画面上の位置)を取得する.offsetX.offsetYを使用します。
数学でやってると思うのでXYについては省略します。

PCで開発していることもあり、実装はクリックやドラッグなど、マウスに対するイベントで実行しています。

window.addEventListener('load', function(){
  var stX;
  var stY;
  var ctX;
  var ctY;
  var cvs = $("#canvas")[0]
  var conText = cvs.getContext("2d");
  conText.lineWidth = 1;
  conText.lineJoin="round";
  conText.lineCap="round";
  var is_drag = false;
});

再宣言が可能なvarを使わずletとかconstにしとけと怒られそうですが...💦
1行目について、Railsでの実装のこともありスクリプトファイルの読み込みタイミングは最も遅い物を使いましたが、turbolinksの前には無意味でした。turbolinksを無効化してしまえば他のものでも問題ない気がしますが、念のためDOM要素が全て表示されてからの方が問題が少ないのでこのタイミングにしています。

2〜5行目ですが、座標XとYを代入させる為の変数を宣言しています。この段階ではまだ中には何も入っていません。
次の2行は前述していたコンテキストの取得の常套句みたいなやつ。上記変数宣言より前でも問題ないです。
その次から3行は記述していないメソッドを使ってますが、これらは全てコンテキストに対して使うメソッドで、.lineWidthは線の太さを決めるものです。特に指定がない場合は勝手に1になります。自分が使う時に見て思い出せるように敢えて書いてます。
.lineJoinは線と線の接合部をどう繋ぐかを設定できるメソッドです。roundを指定すると繋がった部分が丸く滑らかに接合されます。指定がない場合は角張るように繋がります。
.lineCapは線の先端がどういう形状になるかを設定できます。こちらもデフォルトでは角張ってしまうのでroundを指定して丸みを出しています。
最後の変数はマウスがドラッグ状態であるか否かを判定する際に使用するために宣言しています。こちらはデフォルトではしていない状態になっています。常にtrueであれば常にドラッグしていることになるからですね。この辺は僕でも何とか理解できます😅

実際にドラッグイベントを記述していきます。

  $('#canvas').on('mousedown', function(st) {
    stX = st.offsetX;
    stY = st.offsetY;
    is_drag = true;
  });

  $('#canvas').on('mouseup mouseleave', function() {
    is_drag = false;
  });

mousedownはドラッグイベントのスタートの部分になります。イベントハンドラとか言うんでしたっけ?
これはドラッグを開始した時なので「動いてる時ではない」ってのが重要だと思います。
処理内容としては開始した時の座標を変数に代入しているだけです。また、ドラッグ中だという判定を設けるための変数をtrueにしています。
st.offsetの部分はイベントが起こっているセレクタcanvas要素の縁から座標を取得しています。
次のイベントですがmouseup mouseleave一応2つ書いてますがmouseupだけでも動作に影響は出ません。
処理内容としてはドラッグ状態をやめるというだけのイベントです。

ドラッグ中のイベントを記述していきます。

  $('#canvas').on('mousemove', function(ct) {
    if (is_drag === true) {
      ctX = ct.offsetX;
      ctY = ct.offsetY;
      conText.beginPath();
      conText.moveTo(stX, stY);
      conText.lineTo(ctX, ctY);
      stX = ctX;
      stY = ctY;
      conText.closePath();
      conText.stroke();
    }
  });

イベントが始まってすぐif文を書いてます。マウスを動かしているだけならイベントは起きませんが、変数によりドラッグ状態と判定された場合は{ }の処理をします。
現在の座標をまず変数に代入しています。
その後は描画パスの開始宣言。始点をドラッグ開始地点、終点を現在の座標としてパスを描画します。
その後、終点としていた場所を始点に設定します。これをしないとドラッグ開始地点が常に始点になってしまうので放射線みたいな描画になるので注意(それはそれで面白いですが...)。
始点のリセットを行ったらパスの終了宣言をし、パスが描かれた所に実際に描画を行う。

という流れになっています。
一連の流れを以下に記述しておきます

window.addEventListener('load', function(){
  var stX;
  var stY;
  var ctX;
  var ctY;
  var cvs = $("#canvas")[0]
  var conText = cvs.getContext("2d");
  conText.lineWidth = 1;
  conText.lineJoin="round";
  conText.lineCap="round";
  var is_drag = false;

  $('#canvas').on('mousedown', function(st) {
    stX = st.offsetX;
    stY = st.offsetY;
    is_drag = true;
  });

  $('#canvas').on('mouseup mouseleave', function() {
    is_drag = false;
  });

  $('#canvas').on('mousemove', function(ct) {
    if (is_drag === true) {
      ctX = ct.offsetX;
      ctY = ct.offsetY;
      conText.beginPath();
      conText.moveTo(stX, stY);
      conText.lineTo(ctX, ctY);
      stX = ctX;
      stY = ctY;
      conText.closePath();
      conText.stroke();
    }
  });
});

ボタンやバーなどを用意して描画する時の線の太さや色変えなども実装出来ます。

$('.btn').on('click', function(){
  // 線を赤線にする
  conText.strokeStyle = 'red';

  // ポスカみたいな太い線にする
  conText.liceWidth = 15;

  // 橙色のマーカーみたいな線にする
  conText.strokeStyle = 'orange';
  conText.lineWidth = 15;
  conText.globalAlpha = 0.05;
})

globalAlphaは透明度の変更。ただし、連続でイベントが発生するようになっているため、ドラッグ状態でその場で微動するとどんどん色が濃くなっていきます。まぁ実際のペンもその場から動かなければ滲むし、リアルってことで←
値に関しては割と細かく指定できるみたいだったので、透明でない線を簡単に塗りつぶす心配がないよう、0.05としてみました。


簡単に手書き機能が実装できるcanvas、できることがかなり多いのでもっと使ってみたいですね。

したらな❗️ 👋

Heroku, Rails, MySQLでデプロイしてみる

Herokuでデプロイしてみたかったので手っ取り早く魔法の言葉で簡単な投稿サイトを作成し、気持ち程度にcssを付与。
ということで、いつも通り備忘録です。

バージョン情報

Mac OS Catalina 10.15.4
Rails 5.2.3
DB: MySQL

※ 今回は作成したアプリケーションを予めgitによる管理をしている状態にしています。
また、リモートにpushしておいてから始めています。


Herokuについて

Documentation
始め方については公式サイトにいくつか書いてありますね。英語なので一回一回翻訳しないと僕は読み抜けませんが...

Rails5.xでHerokuを使う
ナビゲーションバーで様々な言語での始め方のプルダウンメニューリストがあるので、そこから辿っていくといいのかな、と。

1. まずはHerokuに登録して、アカウントを作成。

ログインページ
主に必要な情報はEmailアドレスと名前。送信すると登録したEmailアドレスに認証メールみたいなのが届くので、ボタンを押すと公式ページに戻される。
その後に、そのアカウントで使用するパスワードの入力画面になる。

ちな、アカウント登録時点で僕は無職だったので会社名は記入してません😅

2. アカウントの種類について

無料と有料アカウントの違いはこちら
無料アカウントでは1Dynoにつき、1ヶ月で550時間までしか稼働できないってあります。
ずっと稼働し続けたとして、22日間強ですね。
クレジットカードを登録すると無料枠で最大の1000時間まで扱うことができるみたいなので、これで1ヶ月間まるっと稼働が可能になります。(31日で744時間)
アカウント情報のBillingタブにadd credit cardってあるので、押すと右から必要事項入力用のボックスが出てくるので入力していけばOK。
また、作成(登録)できるアプリケーションは5つまで。

Dyno(ダイノ)って何?
1Dynoにつきサーバーが1つ立ち上がってるような感じ、ってことですかね。

Herokuのサービスで稼働しているサーバーはLinux OSのあたりで見かけたUbuntuを利用しているみたいな感じのをどっかで見ましたが、今の時点でそれがどう関係あるか微妙なので放置←

3. Heroku CLI をインストール(旧: heroku-toolbelt)

Heroku CLI(コマンドラインインターフェース)をインストール。
ターミナルでHomebrewを使ってインストールする場合(今回はこちらで行ました)

// Mac OSの場合
% brew tap heroku/brew && brew install heroku

Ubuntuなどを利用されている場合は、コマンドが違うので今回使用したコマンドのみ載せています。また、サイト上にあるインストーラからでもインストール可能。
この Heroku CLIを利用することで、コマンドライン(ターミナル)上でHerokuコマンドが使用できるようになる。

% heroku -v

このコマンドでHerokuのバージョンを確認できる

4. コマンドライン上でHerokuにログイン

ターミナル上からHerokuにログイン

% heroku login

ログインの際にアカウント登録の際に使用したメールアドレスとパスワードが要求されるので、入力する。
自分はターミナル上でログインしようとした所

heroku: Press any key to open up the browser to login or q to exit: 

「(訳)任意のキーを押してログインするか、qを押して終了」と出てきて、ブラウザ上でログインすることになりました(現在はこちらがデフォルトのようです)。
ターミナルベースでherokuにログインしたい!って場合は

% heroku login --interactive

のように--interactiveオプションが必要です。

今回の僕のケースで、コマンド打ってブラウザが立ち上がった後ログインしたら、ターミナル上ではLogging in... doneと出て成功しているのが確認できるので、その後ブラウザ自体は切ってしまっても問題ないようです。どちらかのやり方にこだわる必要はなさそうってことで。

Heroku公式(Rails5.xデプロイ)ではここからアプリの作成をして、git管理を行っているようですが、前述したとおり、今回自分は既に作成済みのアプリをデプロイするので流れは一部無視しています。
なお、いくつかの記事で「composer.json」というファイルを作成しないとエラーが出るとあったけど、パッケージの依存管理をしているファイルで.lockの拡張子がついたファイルもあるみたいでした。
Gemfileみたいなものを作らないといけないってことなんでしょうか、今回のRailsでは必要なさそうなので省きます。
Railsにも入っていますが、名前だけ見たら最近npmで使った「package.json」に似ている(ファイルの中身も心なしか似ている)なぁ、なんて。

5. Herokuでアプリを作成

デプロイしたいアプリのディレクトリの中にて

% heroku create {アプリの名前}

これでHerokuのリモートリポジトリがgit(git@heroku.com:{アプリの名前}.git)に登録される。アプリケーションをデプロイした時のURLもこの時点で決まるみたいですね。
Herokuのアプリケーション名には英数字とハイフンしか使用できないので注意。実際には半角でも大文字だとどうも駄目なようでした(freePost みたいな形)。
なお、% heroku createだけにするとランダムで名前をつけるみたいです。
間違えたり、ランダムの名前が気に入らない場合には

% heroku rename {アプリの名前}

で、変更することも可能です。

// Herokuのリモートリポジトリの確認
% $ git remote -v

これでheroku~~~が2行あれば作成されているようです。
失敗してるとoriginしか返しません。(既にgitで管理し、リモートがある状態)

// リモートがプロジェクトに追加されたことを確認
% git config --list | grep heroku

こちらのコマンドではremote.heroku.urlにURLが代入されているような文章が返されました。
どっかで使えるのかな?

6a. clearDBアドオンの設定

※ DB変更が必要なかったりする場合は6b

Herokuには便利なアドオンが色々提供されているみたいで、その中の一つであるclearDBはMySQLを使えるようにするためのサービスのようです。利用するにはクレジットカードの登録は必須ですが、利用自体は無料のようです。

  • ブラウザ上でアプリの名前を選択すると色々タブが出てくるページ行きますが、Overviewの上の方にあるInstalled add-onesの欄のConfigure Add-ons →をクリックする。
  • Add-onsの検索バーにclearDBと入力すると検索に引っかかるので選択。
  • モーダルが開き、利用形態を選ぶセレクトボックスが出てきます、igniteは無料で今回はお試しで上げる程度なのでこれを選択(実際にサービスを立ち上げている人は必要な規模によって変わると思います)。
  • Overviewに戻ってInstalled add-onesの欄にclearDBが追加されていれば成功。
  • SettingsタブのConfig Varsの欄にあるReveal Config VersCLEARDB_DATABASE_URLmysql://〜というURLになっていればmysqlに変わっていることが確認できます(Hide Config Versで再び隠すことができます)。
  • 次にDATABASE_URLの設定。
% heroku config:add DATABASE_URL='mysql2://{mysql://以降と同じ記述をする}'

以上二つを設定できれば良いようです。
ブラウザReveal Config Versもしくはコンソールで% heroku configで2行設定されているのが確認できれば成功しています。

また、今回の一連の流れはコンソールのみでも操作可能

// clearDBのアドオンを追加
% heroku addons:create cleardb:ignite

// Config Versの確認(変更されたかの確認)
% heroku config

// DATABASE_URLの設定
% heroku config:add DATABASE_URL='mysql2:{mysql:以降と同じ記述をする}'

// Config Versの確認(追加されたかの確認)
% heroku config


7. Railsアプリケーション側の設定

config/environments/production.rbの 30行目くらいにあるconfig.assets.compile = falseconfig.assets.compile = trueに変更する。
元々trueなら変更しなくてOK。

※ この後の章で打つコマンドではローカル側からherokuへpushすると思うので問題ないですが、自分としてはリモートとローカルのmasterの内容が違う状態でデプロイするのは気持ち悪かったのでここで一度GitHubへpushしています。

8. コードをdeploy

% git push heroku master

これで成功していれば、herokuにアプリケーションをpush(deploy)完了。

成功した後はheroku上でデータベース移行(マイグレーション)ができる状態になるので

% heroku run rake db:migrate

これでマイグレーション完了。後は% heroku createで生成されたURL「https://{アプリ名}.herokuapp.com/」にアクセスして表示できていればOK。
 
 
※ (公式より)heroku runの後のコマンドはすべて、Heroku dynoで実行され、% heroku run bashを実行すると、対話型のシェルセッションを取得できる。

とあるのでheroku上で何かを実行する際には

% heroku run {実行したいコマンド}


6b~8b. コードをdeploy、以降のコマンド(必要な場合)

自分は既にgit管理されているアプリケーションでかつ、MySQLを使用する為、いくつか設定してからdeployを行っていますが、このままデプロイをすることもできるみたいです。 ※ Herokuでデフォルトで使用されているのはPostgles(PostgleSQL)。

後でアドオンを追加することもできるみたいなので、後からでも設定を変えれるのだろうか...? ちゃんと調べるか、実際にやってみないと分からんですが。

// リモートリポジトリにプッシュしたアプリをherokuのサーバに、デプロイするためのコマンド
% git push heroku master

エラーや警告なしで全てうまくいけばデータベースを移行できるようになるとのこと。

htmlのようなファイルであればこの時点でhttps://{アプリケーション名}.herokuapp.com/でアクセスすることもできるそうです。
もしくは

% heroku open

これで、ブラウザが開き登録したアプリが起動します。
アプリが複数あって起動するアプリを指定する場合には

% heroku open -a {アプリケーション名}

と入力すると、指定したアプリケーションを開くことができるみたいです。

今回のようにアプリケーションでデータベースを使用する場合には% heroku open前に

% heroku run rake db:migrate

でデータベースを手動で移行(マイグレーション)するコマンドが必要です。

9. Herokuアプリケーションと、対象のgitリポジトリの紐付け

gitにpushするだけで自動的にデプロイの設定をすることもできる。
参考にさせていただいた記事: Herokuを使いこなすのに必要な術

Herokuのブラウザにあるタイトルを押すと作成したアプリ名があるので、クリック。 Deployタブ → GitHubのアイコンを押すと上記サイトで貼ってくれてるような画面に行きます。
Connect to GitHubGitHubと連携させる(認証画面の小窓が出ますので、password打って承認)。
連携させたいリポジトリの名前を(一部でもいいので)打って検索、対象リポジトリをクリック。

Automatic deploysってのがあるので、これがGitHubプッシュ時に自動でデプロイするのに相当しているみたいですね。
Enable Automatic Deploysボタンをクリックして、自動デプロイを有効化、これだけ。

また、この画面ではManual Deployの欄で手動で各ブランチを反映させることもできるので、一時的にmaster以外を上げたい場合とかも使えると思います。

流石に触ってると分かるだろって感じですが、僕はこういうの苦手なので全部書いときます(笑)



これでデプロイ、pushで自動デプロイもできるようになりました。

ex. その他

// 現在どのアプリをHerokuで操作しているか確認  
% heroku info

// アプリケーションに追加したアドオンの一覧の表示
% heroku addons

// ローカル環境(ターミナル)からログアウト
% heroku logout

他にもログを確認したり、色々コマンドがありますね。


HerokuでサービスをデプロイするとランダムなURLを発行されるが、独自ドメインを使用することも可能。
Herokuで設定したURL意外に自分でURLを設定する方法があるが、サブドメイン扱いになる為、「www」という接頭がついてしまうので注意。

Herokuアプリはアクセスがない場合、1時間ほどで落ちてしまうらしいです。curl URLコマンドで起こしたり、色々と方法はあるみたいですがアドオンなどを利用すれば簡単に設定できたりするみたいですね。


参考にさせていただいた記事
Railsアプリでherokuを使うときのDBをMySQLに変更する
Herokuへのデプロイ方法【Heroku+Rails+MySQL】



他の方法を試した時はまた別途記事にできたら。
ただ、もっとうまくまとめられるといいんですが、完全にステレオタイプなんだよなぁ...😅

したらな❗️ 👋

CSSのみでアニメーションを実装する

面白そうだったので軽く触りだけ試してみました。
記事を参考に自分が理解できるように説明を加えて、備忘録としておきます。  


<!-- form.html.erb -->
<div class="field">
  <a href="#" class="btn">ボタン</a>
</div>
/* form.scss */
.btn{
  @include inputBorder(1px, 20px, 8px);
  @include mirrorGradient(#888888, #eeeeee, #888888);
  display: inline-block;
  cursor: pointer;
}

.btn:hover{
background: linear-gradient(45deg, #6cb8ff, #fff66c, #ffa36c);
    background-size: 600% 600%;
    animation: gradationTest 10s ease infinite;
}


/* mixin.scss */
@mixin inputBorder($bd, $rd, $pad){
  border: $bd solid #3F3F30;
  border-radius: $rd;
  padding: $pad;
}

@mixin mirrorGradient($a, $b, $c){
  background: linear-gradient(to bottom, $a, $b 55%, $c);
}

@keyframes gradationTest{
  0%{background-position:100% 30%}
  50%{background-position:30% 100%}
  100%{background-position:100% 30%}
}

animation: の後に記載されているgradationTestが@keyframesで作成(定義)したアニメーションの名前です(要はセンス)
mixin機能のように定義した@keyframesの呼び出しをしています。animationプロパティ自体は別記します。
ブラウザ上では多分こんな感じになってると思います(背景は別のscssにてbisqueを指定)
f:id:saka20taku43:20200526180257p:plain
この状態でボタンの上にカーソルを合わせると橙→黄→薄青→黄→橙...のようにボタンの色が変化していきます。
この変化についてはこのままでは僕は理解しづらかったので、background-size: 600% 600%;コメントアウトなりなんなりして本来の倍率に戻してみます。 f:id:saka20taku43:20200526181012p:plain
これをbackground-size: 600% 600%;で大きくし、@keyframes内のプロパティ0%{background-position:100% 30%}と書くことで、アニメーション開始時は背景色の横100%、縦30%の場所を映すようにしています。
f:id:saka20taku43:20200526181841p:plain
animation: gradationTest 10s ease infinite;で10sとあるのがアニメーションを実行し終えるのにかける時間。
5sの時には50%のアニメーションが実行される状態になるようにその間の動作は補完されます。
f:id:saka20taku43:20200526182259p:plain
その為、移動中に黄の部分を通ることにより徐々に色合いが変わっていくような感じになるみたいですね。
100%になる頃には元の0%の位置に戻るようになっています。
easeによって開始時と終了時が緩やかに動作します。
infiniteによってこのアニメーション処理は無限に繰り返されます。
これらの指定は本来は別々で指定できる見たいですが、animationプロパティはいくつかあるプロパティを一括で指定できるものみたいですね。順番も決まっているようです。
 


参考にさせていただいた記事
CSSで背景グラデーション+アニメーションで変化する背景色を実装してみよう!

実装例はもはや写経ですが...💦
border-radiusの値を切り替えることで尖ったり丸まったり、文字の大小、太細... widthの指定を変えることで広くなったり狭くなったり、一度きりのアニメーションにしてしまえば、ゲージを表示するようなアニメーションも実装できそうです。
他にあるプロパティを色々と弄ってみることでもっとグリグリ動くページが作れそうですね!
 
ちなみに自分はRailsのアプリケーションでWebページの実装を行っていたので、.erbと.scssを用いてますが、htmlとcssでも同様に実装できます。
cssでも@import機能が使えるようですが

@import url("./side-bar.css");

などのように僕が知っている書き方とは少し違っていたので、そちらはまた別途調べて実装して行った方が良さそうですね。

したらな❗️ 👋

環境構築関連、Node.jsのインストールなど

Node.jsの記事の時に書いたインストールについて。 仮想環境上とかで個別で環境構築することがあったので、一応軽くまとめてみます。 また、変なことが起こったらその時はログをとって都度記事にできればいいなぁ(願望)。

Node.jsだけ使ってみたい時は途中間に挟んであるrbenvRailsは要らないので飛ばしてOK。

mac OSのバージョンがCatelinaの場合使用するログインシェルはbashではなくzshになる

% chsh -s /bin/zsh

これでzshをデフォルトで使用するように設定することができる。
ターミナル上でcdコマンドを実行した際に表示される自分のディレクトリの後に、$とあった部分が%に変わっている。
...自分はMac自体を買ったのがかなり最近なのでmojaveだった時代がないのですが、bashの時と大分制御構文などの書き方で違いが出てるみたいですね。
シェルスクリプトコマンドみたいなのを使用する場合は気をつけないとね...。 設定できたかを確認するにはechoを使用する。

% echo $SHELL

実行結果が
=> bin/zsh
になっていれば成功。bashと出ていたら一度読み込み直すためにターミナルを終了するなどした方がいいかも。

Command Line Tools をインストール

既に開発環境を整えてる人なら見聞きしてる単語だけどrbenvをインストールするにはHomebrewが必要で、HomebrewをインストールするにはCommand Line ToolsというWebアプリケーション開発に必要なソフトウェアが必要。
最初は、なんかめんどくせーな(笑)って思ってたんですが、こういったツールやパッケージみたいなので管理しているからこそ、各ブラウザに行ってインストールソフトウェアを揃えるとかバージョン毎に入れ直すとかってことをしなくて済む...みたいな感じで。
ターミナルでコマンドを打つだけで各アプリケーションで使うツールのバージョンを簡単に切り替えられ、バージョン確認もターミナルでコマンド打てば用意に確認できる。
実際の開発でもなんでも、最新バージョンが常に正解とは限らないからこそ、こういう仕組みみたいなのがいるってことですな、多分。今はそうやって納得しておく。

% xcode-select --install

これでxcodeappleからインストールできる。ウィンドウが起動したらインストール ボタンを押し、規約に同意する。
数GBほどあるらしいのでとても時間がかかる。

Homeblew をインストール

homebrewというソフトウェア管理ツール。

% cd
% ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

cd後にpwd/Users/${ユーザ名}と表示されていればOK。2行目のコマンドは公式サイトにあるインストールの下にあるコードのコピペを行う。
...見れば分かるが、公式ではbashとあるのでzshに変えている場合はコマンドを変えなければならないので、公式サイトに載っているコードの意味を考えた上でコピペを行うこと。
途中で続行するか聞かれるのでreturnキーを押して決定し、passwordを聞かれたら、使用しているPCを起動する際のパスワードを入力する。
このコマンドも実行完了まで時間がかかる。

% brew -v

で、=> Homebrew x.y.zzとバージョン情報が表示されていればインストール成功。
Homebrewのアップデートする際は

% brew update

Homebrewコマンドの権限を変更する場合は

% sudo chown -R `whoami`:admin /usr/local/bin
  • sudoスーパーユーザー(root)権限という他のユーザーの権限でコマンドを実行する際に使用する。複数のオプションが存在する。
    僕のイメージではあるが、所有者であっても一般ユーザーとなるってことだと思う、書き換えちゃいけないファイルとかはこれがないと実行できないとかあるしね。
  • chownは指定したファイルやディレクトリのユーザー所有権やグループ所有権を変更できる。グループ所有権はグループ名やグループIDで指定する。chown [オプション] ユーザー[:グループ] ファイルのように書く。
  • -Rchownコマンドのオプションで、指定したディレクトリ以下のファイルやディレクトリの所有権を再帰的に変更する。 このオプションをつけた時のコマンドの動作はまず自分(対象のディレクトリ)自身に対して処理を行う。自己(このコマンド)の結果が自己に返ってくる、みたいな感じ。
  • whoamiは単独で使用した場合、ユーザー名を表示するコマンド
  • adminは管理者の意。whoami: と組み合わせることで、管理者権限としてってことになるのかな。LinuxOSとかでは root 権限を持っているユーザー。sudoさんのことですね←
  • その後の/usr/local/binディレクトリ名とファイル名の構造、パス

rbenv をインストール

Macには元々Rubyが入っているのでそのままでもruby自体は利用できるけど、Webアプリケーション開発をするにあたってはrbenvというバージョン管理を切り替えるツールを使う。
ディレクトリ毎にバージョンを指定することもできる。
rbenvは自身でRubyコンパイル&インストール、もしくはruby-buildを使用して自動インストールできる。
Railsでの公式サイトでもrbenvを利用してRubyをインストールすることを勧めている...らしい。
他にあるRVMというツールよりもシンプルで動作が軽く、Ruby自体のバージョンの切り替えを行っている。

Homebrewを使用してRubyの土台であるrbenvruby-buildをインストール

% brew install rbenv ruby-build

rbenvをPCのどの場所(パス)からでも使用できるようにコマンド入力

% echo 'eval "$(rbenv init -)"' >> ~/.zshrc

.zshrcは設定ファイルの名称。.bashrcみたいなやつの代わり...だよね。
上記コマンドではファイルの内容を変更しているコマンドなので、設定を反映させるためにsourceコマンドを実行

% source ~/.zshrc

環境設定ファイルなどを変更した時はsourceコマンドを使用する。確かデプロイの時なんかも使いました。
続いてはターミナル上のirb(rails cのような対話シェル)で日本語入力を可能にする設定のために、readlineを実行

% brew install readline
% brew link readline --force

linklnとも表記できる。ファイルのリンクを作成する。--forceオプションは、リンクファイルと同じ名前のファイルがあっても強制的に上書きする、と言うオプション。
次にrbenvを使ってRubyのインストールを行う。実行には時間がかかる。

% RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)"
% rbenv install 2.5.1

2行目のコマンドは今回インストールするRubyのバージョン。
使用するRubyのバージョンをglobalで指定する

% rbenv global 2.5.1

このコマンドを実行するとそれまでPCでデフォルトで使用していたRubyから切り替えることができる。
Rubyのバージョンを切り替えたら、rbenvを読み込み変更を反映させる。

% rbenv rehash

現在インストールしているrbenvのバージョン確認、使用しているRubyのバージョンを確認する際には

% rbenv -v
=> rbenv 1.1.2
% ruby -v
=> ruby 2.5.1p57 〜〜〜〜〜〜

コマンド実行した時に表示されていれば、無事にインストール完了

MySQL をインストール

Homebrewを利用してインストールを行っていく、実行完了まで時間がかかる

% brew install mysql@5.6

MySQLはPCが再起動などの度にMySQLも起動する必要があるが、自動で起動できるように設定できる。

% mkdir ~/Library/LaunchAgents
% ln -sfv /usr/local/opt/mysql\@5.6/*.plist ~/Library/LaunchAgents
% launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql\@5.6.plist

続いてrbenvreadlineと同様に、mysqlコマンドをPCのどこからでも実行できるようにして、MySQLを操作できるようにする

% echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.zshrc
$ source ~/.zshrc

上記同様、環境設定のファイルを書き換えた場合、変更反映のコマンドも入力している。
mysqlが実行できるか確認

% which mysql
=> /usr/local/opt/mysql@5.6/bin/mysql

whichコマンドは指定したコマンドを使用した際のフルパスを表示する(今回はmysql)
mysqlの起動しているかの確認

% mysql.server status
=> SUCCESS! MySQL running

SUCCESSと出てれば成功、runningは動作中ってことっすな

Rails の準備

Ruby拡張機能であるgemを管理するためのbundlerと言うツールのインストール。
gemは手動でインストール(gem install "shinatra"など)することができるが、最新バージョン同士では互換性不良などで動作しない場合がある。
また、複数人で開発などを行う際にはバージョンを合わせる必要なども出てくる。
この際にGemfileにインストールしたいgemを記入し、bundlerを介してインストールを行うことで、gem同士の互換性を保ちながら各gemの導入などを行ってくれてる、ってことですね。
bundler本体以外のgemは、基本的にbundle installを使用してインストールすることがオススメ。

% gem install bundler

bundlerのバージョンを確認するコマンド

% bundler -v

Railsのインストール、実行完了まで時間がかかるかも。

% gem install rails --version='5.2.3

開発に必要なものが一通りインストールできたので、rbenvを再読み込みしておく

% rbenv rehash

導入したRailsのバージョンの確認。

rails -v
=> Rails 5.2.3

アプリケーションをrails newで作成する際に_5.2.3_などのように入力すれば個別に指定できるため、ディレクトリ毎に指定も可能となる。

nodeblew をインストール

Railsを動かすにはJavaScriptが必要だが、通常のJSではなく、サーバーサイドで動作するNode.jsが必要(なんだよね、確か) で、nodebrewというNode.js用のバージョン管理ツールがあるのでそれをインストールしていく。

brew install nodebrew

nodebrewのバージョン確認、利用できるコマンドも確認可能

% nodebrew -v

インストールできるNode.jsのバージョン確認コマンド

% nodebrew ls-remote
// バージョンを指定してインストールする場合
% nodebrew install-binary {version}
% nodebrew install-binary v13.0.0

// 最新版をインストールする場合
% nodebrew install-binary latest

// 安定版をインストールする場合
% nodebrew install-binary stable

インストールされたnodejsのバージョン確認

% nodebrew ls
=> v13.0.0
=> current: none

インストール直後は使用するバージョンが有効になっていないので

% nodebrew use v13.0.0
% node-v
=> v13.0.0
=> current: v13.0.0

1行目のコマンドで有効化、2行目のバージョン確認コマンドで4行目のようにバージョンが表示されていれば有効化されている
nodeが使えるように環境パスを通す必要がある。

$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.zprofile

シェルがbashの場合は最後違うので注意。また、追記リダイレクト>>ではなく普通のリダイレクト>をしてしまうと既存の書き込みに上書きをして消してしまうことがある為、注意が必要。環境変数を設定したので

% source ~/.zprofile

で変更を反映するか、ターミナルを再起動すること。
 
 
※ Homebrewから直接nodejsをインストールする場合のコマンド

% brew install nodejs

Node.jsが導入できたかの確認コマンド

% node -v

 
 
※ Linux OS使用時、サーバー上から取得したプログラムをパイプ機能を使ってPerlというプログラミング言語で実行する場合のコマンド

% curl -L git.io/nodebrew | perl - setup

環境変数の設定

% echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.profile

PATHという名前の環境変数に値を代入することで設定している。 上記例では.profileと言うファイルに追記リダイレクトを使用することで設定内容を編集している。.profileはシェルの起動時に自動的に実行されるファイル。
この場合もパスを通したら

% source ~/.profile

sourceコマンドで.profileを再起動することなく再度読み込ませて、変更を反映させる。

npm のバージョン確認

% npm --version

npmではpackage.jsonと言うファイルで依存関係のあるパッケージをインストールできたりタスクも管理できる。


npm、Node.jsの記事書いてからいくつかの記事やスクールのカリキュラムを参考にザッと書き出してみました。
あまり環境構築は何度もやらないので忘れそうですが、仮想環境バンバン作って壊して環境構築慣れしてみるのも面白いのかな?(笑)

したらな❗️ 👋