webプロダクトいんふぉ

webの気になった情報を発信中!!

ファイルアップロードをドラッグ&ドロップでも可能にする

ファイルアップロードするには?

フロント側のHTMLもしくはPHPファイルにフォームの記述をします。

<form action="" method="post" enctype="multipart/form-data">
  <input type="file" value="ファイルを選択" name="image">
  <input type="value" value="送信">
</form>

画像を扱う場合にはenctype部分が必須になります。他にもinputタグにaccept属性を指定することにより、画像の形式をpng、jpgのみ受け付けるようにすることなどもできますが、今回は必要最低限で説明していきます。

次に画像ファイルを受け取る裏側を書いていきます。詳しくは説明しないんですが、1行目のif文で画像データが問題なく送られているかチェックします。問題がなければ4・5行目でアップロードしたファイル名と拡張子を取得します
ex)test.png→$filename=test、$extension=png

$tmp_pathでアップロード画像が仮で置かれているパスを取得します。

$file_dir_pathでアップロードした画像をどこに置くかを選択。今回は同階層のuploadフォルダに格納するようにする。

$uniq_nameは同じファイル名でアップロードした場合は上書きされて古い画像が消えてしまうのを避けるために格納する際にユニークな名前をつけて画像が上書きされるのを避けます。

入れ子になった最初のif文でPOSTでアップロードされたファイルか確認。次のif文でアップロードされたファイルを新しい位置に移動する。

移動できたら、パーミッション(アクセス権限)を設定。所有者は読み書きOK、それ以外は読み込みだけOK

if (!isset($_FILES['image']['error']) || !is_int($_FILES['image']['error'])){
  echo "ファイルアップロードエラー";
} else {
  $file_name = $_FILES['image']['name'];
  $extension = pathinfo($file_name, PATHINFO_EXTENSION); //拡張子取得
  $tmp_path = $_FILES['image']['tmp_name'];
  $file_dir_path = "upload/";
  $uniq_name = date("YmdHis").md5(uniqid(microtime(),1)).session_id() . "." . $extension;

  if (is_uploaded_file($tmp_path)) {
    if(move_uploaded_file( $tmp_path, $file_dir_path . $uniq_name)) {
      chmod($file_dir_path . $uniq_name, 0644);
    } else {
      echo "Error:アップロードできませんでした。";
    }
  }
}

これで必要最低限のシステムはできました。

ファイルアップロードをドラッグ&ドロップでも可能にするには?

今回のゴールは下記のようなドラッグ&ドロップでもアップロードを可能にするシステムです。

f:id:front-end-engineer:20160708150321p:plain

簡単な話ではありますが、上記で作ったシステムとjsを使えば実装できます。

jQueryは使わずに生のjsで書いてみます。その前に、フロント部分の記述を少し変更します。

cssは各々で調整してください。

<form action="" method="post" enctype="multipart/form-data">
  <div id="drag-drop-area">
    <div class="drag-drop-inside">
      <p class="drag-drop-info">ここにファイルをドロップ</p>
      <p>または</p>
      <input type="file" value="ファイルを選択" name="image">
      <p class="drag-drop-buttons"><input id="fileInput" type="file" value="ファイルを選択" name="image"></p>
      <input type="value" value="送信">
    </div>
  </div>
</form>

fileAreaでドラッグ&ドロップの有効範囲を設定。fileInputにfile属性のinputタグを代入しておく。

dragoverイベントはドラッグしている要素がドロップ領域にある間に発生

dragleaveイベントはドラッグしている要素がドロップ領域から出たときに発生

dropイベントはドラッグしている要素がドロップ領域にドロップされたときに発生

その他にもドラッグ&ドロップのイベントはあります。もっと知りたい方は下記のリンクを参考にしてください。
https://app.codegrid.net/entry/dnd-api-1

files変数にドロップされた画像のデータ情報が入り、そのデータをfileInputに渡しているだけです。

これで完成です。

※ドロップした時点でアップロードしたい際には、changeイベントを使えば可能です

var fileArea = document.getElementById('drag-drop-area');
var fileInput = document.getElementById('fileInput');


fileArea.addEventListener('dragover', function(evt){
  evt.preventDefault();
  fileArea.classList.add('dragover');
});

fileArea.addEventListener('dragleave', function(evt){
    evt.preventDefault();
    fileArea.classList.remove('dragover');
});
fileArea.addEventListener('drop', function(evt){
    evt.preventDefault();
    fileArea.classList.remove('dragenter');
    var files = evt.dataTransfer.files;
    fileInput.files = files;
});

スマホRPGで例えてみるオブジェクト指向

そもそもオブジェクト指向って何?

 オブジェクトとは、物体(スマホとか)を、属性(データ)と操作(メソッド)の集合として、定義して、コンピューターで扱えるようにしたもの。

このオブジェクトを組み合わせて、開発を行うことをオブジェクト指向プログラミングと呼びます。

何を言っているか分かりませんね。

オブジェクト指向の考え方

・クラス/インスタンス化/オブジェクト

・継承

カプセル化

ポリモーフィズム

クラス

たとえば、キャラ毎の詳細を見てみます。

f:id:front-end-engineer:20160706125507p:plain

これをレア度が「レジェンド」のクラスと呼びます。

レジェンドキャラの設計図、つまり「クラス」

インスタンス

?の箇所を埋めないと使えるキャラとして成り立ちませんよね。

設計図=クラスの空白部分を埋めて実体化(使えるよう)することをインスタンス化といいます。

f:id:front-end-engineer:20160706123231p:plain

 元になっている設計図=クラスさえあれば量産することが可能です。

※キャラ画像とステータスさえ変えれば、別物になる

 ↓

オブジェクトを組み合わせて、1つのソフトウェアを構築するのがオブジェクト指向

ex)上のオブジェクトを組み合わせてパーティー作ったり

継承

オブジェクトの定義を定めた雛型を「クラス」と呼び、上位のクラスを性質を引き継いだ上で、他の性質を加えた新しいクラスを作成することを「継承」と呼ぶ。

ノーマルキャラ

スーパークラス(基本クラス)

レジェンドキャラ(ノーマルキャラにはないスキルが使えたりetc...)

カプセル化

データとそれを動かす(メソッド)操作を一体化して「オブジェクト」として定義し、オブジェクト内の細かい仕様や構造を外部から隠ぺいすること。

外部からは公開された手続きを利用することでしかデータを操作できないようにすることで、ここのオブジェクトの独立性が高まる。

 ※カプセル化を進めることによりオブジェクト内部の仕様変更が外部に影響しなくなり、ソフトウェアの保守性や開発効率が高まり、プログラムの部分的な再利用が容易になる。

f:id:front-end-engineer:20160706143814p:plain

HPのデータに直接減算するようなことはしない

ダメージを与えるメソッドを呼ぶことでHPを減らす処理を行う

ポリモーフィズム

「多様性」「多態性」「多様性」

オブジェクト指向プログラミング言語の持つ性質のひとつであり、同盟のメソッドや型などをオブジェクトの種類に応じて使い分けることができる性質のこと

オブジェクト指向プログラミングでは、関連するひとまとまりのデータとそれを操作する関数などの組み合わせ「オブジェクト」として定義していき、これを組み合わせてプログラムを記述していく。

継承を行う際に、上位のクラスの関数を新しい関数の定義で置き換えることを「オーバーライド」と呼び、クラスによって同一のメソッドで異なる処理が行われる性質をポリモーフィズムといいます。

ポリモーフィズムの利点としては、メソッド名を統一することで名前を覚える必要がなくなることや、記述ミスを減らせることなどが挙げられます。

/*-----------------------------------

ex)攻撃のメソッドで考えてみる。

①敵のHPにダメージ与える

②敵の攻撃力にダメージを与える(弱体化)

 -----------------------------------*/

同じ攻撃でも処理が異なる。

ポリモーフィズムという考え方を使わないと、同じ攻撃でも異なるメソッドを使わなくてはならない。

スキルと攻撃みたいに明確に分ける必要があれば、ポリモーフィズムを使わない方がいい。

 

 

 

JavaScriptで現在から数日~数週間前の日時を取得するには?

まず、JavaScriptで日時を扱うには?

js側で用意している組み込み関数のDateオブジェクトを使用します。

使用方法は

var 変数名 = new Date();

で、Dateオブジェクトを生成します。

今回、次のような日時を使ったグラフを作っていきます。

f:id:front-end-engineer:20160621111642p:plain

ステップ1

グラフを簡単に作成するjsのライブラリとしてchart.jsを使っていきます。

http://www.chartjs.org/

リンクをクリックしたら、下の画面に飛ぶので、Get Startedをクリック

f:id:front-end-engineer:20160620185329p:plain

ステップ2

GitHubのページに飛ぶので、clone or downloadボタンをクリック後、Download ZIPからダウンロードをしてください

f:id:front-end-engineer:20160620185617p:plain

ステップ3

今回使うファイルは、Chart.js-master→distフォルダ→Chart.min.jsのみを使います

Chart.min.jsを任意の場所に移動して置いてください。

上記ような操作が面倒な方はCDNも公開されているのでそちらを使っても大丈夫です

https://cdnjs.com/libraries/Chart.js

まず、HTMLファイルの雛型を用意して、グラフを描画するためのcanvasタグを書きつつ、chart.jsを読み込んでおきます

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title></title>
</head>
<body>
	<div style="width:600px;height: 450px;margin: 0 auto;">
		<canvas id="canvas" height="450" width="600"></canvas>
	</div>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.min.js"></script>
	<script>

	</script>
</body>
</html>

ステップ4

次に、scriptタグの中にchart.jsを使用するためのコードを書いてあげます

chart.jsの解説はしませんが、興味がある方は調べてみてください

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title></title>
</head>
<body>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.min.js"></script>
	<script>
	var config = {
	type: 'line',
		data: {
			labels : ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
			datasets: [{
				label: "ページビュー数",
				fill: false,
				backgroundColor: "#3A7AC9",
				borderWidth: 2,
				borderColor: "rgba(2,63,138,0.8)",
				pointBorderColor: "#fff",
				pointBackgroundColor: "rgba(2,63,138,0.8)",
				pointBorderWidth: 2,
				pointHoverRadius: 5,
				pointHoverBackgroundColor: "#1D5191",
				pointHoverBorderColor: "#fff",
				pointHoverBorderWidth: 2,
				tension: 0,
				data:  [1, 59, 90, 81, 56, 55, 40, 30, 10, 40, 48, 58]
			}, {
				label: "セッション",
				fill: false,
				backgroundColor: "#DB514E",
				borderWidth: 2,
				borderColor: "rgba(201,60,58,0.8)",
				pointBorderColor: "#fff",
				pointBackgroundColor: "rgba(201,60,58,0.8)",
				pointBorderWidth: 2,
				pointHoverRadius: 5,
				pointHoverBackgroundColor: "#9A1B19",
				pointHoverBorderColor: "#fff",
				pointHoverBorderWidth: 2,
				tension: 0,
				data: [28, 48, 40, 19, 96, 27, 100, 33, 63, 31, 64, 51]
			}]
		},
		options: {
			responsive: true,
			scales: {
				xAxes: [{
					display: true,
					stacked: false,
					gridLines: {
						display: false
					}
				}],
				yAxes: [{
					display: true,
					ticks: {
						reverse: false,
						callback: function(value){
							return value;
						}
					}
				}]
			}
		}
	};

	// ▼上記のグラフを描画するための記述
	window.onload = function() {
	   var ctx = document.getElementById("canvas").getContext("2d");
	   window.myLine = new Chart(ctx, config);
	};
	</script>
</body>
</html>

上記のコードを書いてあげてブラウザで見てみると、グラフが出来上がっていると思います

f:id:front-end-engineer:20160620193456p:plain

ステップ5

赤枠で囲ってる部分を現在の月と日時に書き換えてあげたいと思います

var config = { の上にscriptを記述していきます

2つDateオブジェクトを生成してあげます

空の配列を用意して、for文で今日の日付から1週間前の経過日数を出して、配列に挿入しています。

<script>
var date = new Date();
var before = new Date();
var week = [];
for(var i = 0;i < 7;i++) {
	week.unshift(date.getDate() - i);
}

var config = {
</script>

ステップ6

出力結果が下記になります。今日が21日なので、1週間分がしっかりと取れてますね

f:id:front-end-engineer:20160621104837p:plain

ただ、これだと問題があります。今回は月をまたいでいないので、問題が無いように見えますが、例えば、6/1の前日の日付を取ろうとした場合に本来であれば、5/31にならなければならないのに、5/0になります。試してみましょう

for文で回す値を大きくしてあげます。

<script>
var date = new Date();
var before = new Date();
var week = [];
for(var i = 0;i < 25;i++) {
	week.unshift(date.getDate() - i);
}

var config = {
</script>

ステップ7

そうすると意図した結果にならないのが分かると思います。これを避けるためには、setDate()メソッドを使用します。

簡単に説明すると、指定した値が1から31の範囲外の場合は、Dateオブジェクトの日付の情報の更新を試みます。例えば0が値として指定された場合、前月の最終日に設定されます。

f:id:front-end-engineer:20160621105542p:plain

<script>
var date = new Date();
var before = new Date();
var week = [];
for(var i = 0;i < 25;i++) {
	before.setDate(date.getDate() - i);
	week.unshift(before.getDate());
}

var config = {
</script>

ステップ8

先月の日時がしっかりとれてますね。

そしたら、配列に月も挿入してあげつつ、chart.jsのlabelsの中身をweekに変更してあげれば完成です

getMonth()に+1している理由はgetMonthメソッドでは1月が0、2月が1といった具合に、1つ下にずれた数字が取得されるからです
※for文の値も直しておきます

f:id:front-end-engineer:20160621110528p:plain

<script>
var date = new Date();
var before = new Date();
var week = [];
for(var i = 0;i < 7;i++) {
	before.setDate(date.getDate() - i);
	week.unshift(before.getMonth() + 1 + '月' + before.getDate() + '日');
}

var config = {
type: 'line',
	data: {
		labels : week,
</script>

英語のみ横幅(width)をはみ出る挙動

HTMLの「div」タグなどでボックスを作り、classやidを用いてcssのwidthプロパティを使用して横幅を指定したとしても、英語は枠におさまらない場合があります。

※今回の場合はoverflow:hiddenをかけているので、文字が途切れている

f:id:front-end-engineer:20160616144013p:plain

結論から言ってしまえば、

  1. word-wrap: break-word;

を使用すれば英語であっても折り返し可能です。

f:id:front-end-engineer:20160616144437p:plain

大手のサービスであってもこういった対策していないところもあるので注意が必要です。日本であっても英単語を羅列する場合はあるので(IT業界では多い)、多様なパターンでデバックするようにしましょう。

 

テキストエディタ「ATOM」を日本語化

atomは初期状態では、デフォルトで英語になっています。

このままでは使いづらいユーザーがいると思うので、日本語化できるようにします。

とは言っても、やることは至って単純です。

atomを起動して、上部のメニューバーからHelpを選択し、Welcome Guideをクリック

f:id:front-end-engineer:20160611163832p:plain

②Install a Packegeをクリックします

f:id:front-end-engineer:20160611164218p:plain

③2の操作を行うとスライドして、Open Installerというボタンがあるので、そちらをクリック

f:id:front-end-engineer:20160611164553p:plain

④左カラムのテキスト入力欄に「japanese」と入れて、Packagesを押すと、少し時間が経った後に、パッケージが一覧が出てきます

f:id:front-end-engineer:20160611164915p:plain

「japanese-menu」を探して、Installボタンを押せば完了です!

※ダウンロード数は違うのですが、顔写真と名前は一緒なのでそこにだけ注意

f:id:front-end-engineer:20160611165104p:plain