Skip to content

Instantly share code, notes, and snippets.

@YSRKEN
Created May 6, 2018 10:45
Show Gist options
  • Save YSRKEN/3c0070a098ecac432a54b4434558f0de to your computer and use it in GitHub Desktop.
Save YSRKEN/3c0070a098ecac432a54b4434558f0de to your computer and use it in GitHub Desktop.
【TypeScript+D3.js】Web初心者がWebアプリケーションをでっちあげるまでの道のり ref: https://qiita.com/YSRKEN/items/6683ec1c1de085935a69
// インポート部分
import ccc = require("./models/Hoge"); //ファイルパスを拡張子抜きで指定
import DDD = ccc.AAA; //要するにcccは中間変数(省略不可)。以降は「DDD」という名前でAAAクラスを使用できる
// 使用例
let val: DDD = new DDD();
// ここでtsファイルじゃないのにimportできるのは、後述する.d.tsを作成した上で、
// tsconfig.jsonにその置き場所を指定しているから。なおこれらのファイルパス指定は、
// 「そのソースコードの置き場所」から相対的に決めるので注意!
import csv = require('../files/ExpList.csv');
// TypeScriptでもasync/awaitが使える感動……
// ただし、tsconfig.jsonでlibに"es2015.promise"や"es2015"を追加する必要がある。
// 「最低限どれを追加すればいいのか」は、大変クソなことにググった先のWebページ毎に
// 違う有様なので、もう「動けばいいや」精神に落ち着いた
async initialize(){
// 1. ファイルcsvを読み込み、dataと言う名のd3.DSVRowStringの配列に変換する
this.list = await d3.csv(csv).then((data) => {
// row(=d3.DSVRowString)は、row["CSVの列の名前"]と入力すると
// その列における値を返してくれる。要するに連想配列
return data.map(row => {
return this.func(data);
})
});
}
// 呼び出し先までasync/awaitを書き続けるのはasync/awaitのお約束
window.onload = async function(){
// データベースを初期化
await this.initialize();
}
REM npmを初期化
REM (参考→https://techacademy.jp/magazine/16151)
npm init
REM 必要なライブラリをインストールしまくる
REM リモートリポジトリからcloneしてきた時のように、あらかじめpackage.jsonが
REM 整っている場合は「npm install」だけで全て揃うが、そうではない場合は
REM ライブラリ名を指定していく必要がある
npm install -D webpack webpack-cli typescript typings ts-loader url-loader
npm install d3 @types/d3
export class AAA {
}
// windows.sessionStorageだとセッションストレージになる。両者の差はググれ
var storage = window.localStorage;
// データの読み取り(文字列が返ってくる。読み取れない際はnullを返す。データはドメイン単位で保持される)
const val = storage.getItem("キー");
// データの書き込み(KeyもValueも文字列を与える)
storage.setItem("キー", val);
// データの削除
storage.removeItem("キー");
// データの要素数
const count = storage.length;
// データの指定したインデックスにあるキーの名前(引数に渡すインデックスは0スタート)
const key = storage.key(5);
// データの全削除
storage.clear();
import * as d3 from 'd3';
// CSSセレクタで、DOM要素を選択することができる。selectAllは全要素選択
d3.select("p")
d3.select("g > rect.hoge")
d3.selectAll("#sample")
// 選択したDOM要素のプロパティを参照・変更する
val = d3.select("a").property("href");
d3.select("#piyoFlg").property("checked", piyoFlg);
// 選択したDOM要素の中の文字列を参照する/文字列を追加する
// (\nで改行はできないので、後述する.html()内で"<br>"入りのHTML文字列を付加すること)
val = d3.select("td").text();
d3.select("strong").text("Strong Text");
// 選択したDOM要素の中のHTMLを参照する/HTMLを追加する
val = d3.select("td").html();
d3.select("strong").html("<a>Strong Text</a>");
// 選択したDOM要素にCSS効果を適用する
d3.selectAll("h2").style("font-size", "40px");
// 選択したDOM要素にイベントを貼り付ける
d3.select("#changeTask").on("click", this.changeTask.bind(this));
// 別にfilterしても良いのだろう?
d3.selectAll("g > rect").filter((d, i) => (i === index)).attr("x", data.rx).attr("y", data.ry);
// JavaScriptの仕様により、ラムダ式を渡すとthisの意味が変化してしまう。
// それを嫌う場合は.bind(hoge)とすることにより、ラムダ式の先のメソッドにおける
// thisの中身をhogeに設定できる(Function.prototype.bind())
d3.select("p.foobar").text("font-size", "40px");
// 選択したDOM要素を削除する(selectAllだともちろん全削除)
d3.select("option").remove();
// SVGとして描画する際の土台を作る
canvas = d3.select("div.canvas").append("svg")
.attr("width", width)
.attr("height", height);
// 要素追加はappendで出来る。SVG要素以外でも<p>や<a>など何でも追加できる。
// メソッドチェーンは「それ以前で選択・追加された要素に操作を行う」ので、
// 大きさなどの属性を後付けすることになる。次の例は、
// 「50x50の矩形を座標(100, 200)に配置し、枠線を不透明度100%の黒・不透明度80%のskyblueで一様に塗り潰す」
canvas.append("rect").attr("x", 100).attr("y", 200).attr("width", 50).attr("height", 50)
.attr("stroke", "black").style("opacity", 0.8).attr("fill","skyblue");
// 「テキストを位置(200, 300)に配置し、フォントサイズを36pxにして"Hello"と表示」
canvas.append("text").attr("x", 200).attr("y", 300).attr("font-size", "36px").text("Hello");
// 直線を(a, b)から(c, d)に引く。その際太さは5、線の色は白とする
canvas.append("line").attr("x1", a).attr("y1", b).attr("x2", c).attr("y2", d)
.attr("stroke-width", 5).attr("stroke", "white");
// data(list).enter()とすると、以降のメソッドチェーンは配列listを対象にできる。
// すると、データ(list)からまとめてオブジェクトを作成できるので便利。
// 次の例は、canvasに対して直線をいっぺんに引く例
canvas.data(nameList).enter().append("line")
.attr("x1", function(i){return Utility.func1(i)})
.attr("y1", function(i){return Utility.func2(i, flg)})
.attr("x2", i => Utility.func3(i))
.attr("y2", i => Utility.func4(i,flg))
.attr("stroke-width", 1)
.attr("stroke", "red");
// attrの第二引数に関数オブジェクトを渡す際、第一引数が「dataで設定した配列の各中身」、
// 第二引数が「その中身のインデックス」を表す
canvas.data(dataList).enter().append("rect").attr("x1", (中身, 中身のインデックス) => {~});
// オブジェクトに対するドラッグ操作を扱う際の例。ドラッグ開始・途中・終了時の操作を記述する。
// ここで言うところの「d」とは……何だったっけ?
canvas.select("rect").call(d3.drag()
.on("start", function(d){this.dragstartedFunc(d);})
.on("drag", d => this.draggedFunc(d))
.on("end", this.dragendedFunc)
);
// 対象オブジェクトにおけるドラッグイベントの無効化
canvas.select("rect").on(".drag", null);
// ドラッグ時の対象イベントで、現在のマウス座標やマウス座標の変化量などを読み取ることができる
draggedFunc(){
x = d3.event.x;
y = d3.event.y;
dx = d3.event.dx;
dy = d3.event.dy;
}
// それぞれ、[0, 1, ..., 9]と[3, 4, ..., 19]を表す
d3.range(10);
d3.range(3, 20);
// id="areaName"である要素に、配列areaNameListの中身をセレクトボックスの要素として設定
// その際、「<option>におけるvalue属性の値」と「<option>で囲った表示テキスト」は同じとしたd3.select("#areaName").selectAll("option").data(areaNameList).enter()
.append("option").attr("value", d => d).text(d => d);
// セレクトボックスの変化時にonChangeMethodを発火させる。
// この場では「1つ目のセレクトボックスで選択した中身」を渡せないのが残念
d3.select("#areaName").on("change", this.onChangeMethod)
.selectAll("option").data(areaNameList).enter()
.append("option").attr("value", d => d).text(d => d);
onChangeMethod(){
// 1つ目のセレクトボックスで選択した中身を取得する
var areaName = d3.select("#areaName").property('value');
// そこから2つ目のセレクトボックスで使う文字列配列を生成
var nameList = DataStore.func(areaName);
// 何度も生成するものなので、まず2つ目のセレクトボックスの中身を全部削除
d3.select("#expName")
.selectAll("option").remove();
// その後に要素を追加していく
d3.select("#expName").selectAll("option")
.data(nameList).enter()
.append("option").attr("value", d => d).text(d => d);
}
dragstartedFunc(){
this.mx = d3.event.x;
this.my = d3.event.y;
}
dragendedFunc(){
const dx = this.mx - d3.event.x;
const dy = this.my - d3.event.y;
if(dx * dx + dy * dy <= 100){ //この閾値は真面目に測定して決めたわけではないので注意
// クリック時の記述
}else{
// ドラッグ時の記述
}
}
{
"compilerOptions": {
"module": "commonjs", // モジュール管理をCommonJSで行う
"target": "es5", // 出力するJavaScriptをES5基準にする
"noImplicitAny": false, // ソースにおける暗黙のany型は全てエラーとする
"outDir": "public", // publicフォルダに出力
"rootDir": ".", // コンパイルの起点とするフォルダ
"sourceMap": true, // ソースマップを出力するか
"lib": [ // 使用するライブラリ
"dom",
"es5",
"scripthost",
"es2015.promise",
"es2015"
]
},
"exclude": [
"node_modules" // node_modulesフォルダは(既にJavaScriptなので)コンパイルしない
],
"typeRoots" : [
"src/typings", // TypeScriptは型情報を使う言語なので、ここにフォルダパスを指定して
"node_modules/@types/" // おくと、そこの型定義ファイルを読み込んでくれる。プログラミングに便利
]
}
// path.某メソッドを使うために使用。ファイルパスの各種処理を簡単にしてくれるそうな。
// 参考→http://koukitips.net/post1825/
const path = require('path');
module.exports = {
// このソースコードから起動する、ということを明示する
entry: [
'./src/app.ts'
],
// 最終的にどこに出力するかを明示する。「__dirname」はカレントディレクトリを
// 指すので、結局「変換してpublicフォルダのbundle.jsに書き出す」という意味
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js'
},
// ソースマップ(後述)を利用するために記述
devtool: 'source-map',
// ソースコードとして処理する拡張子を指定。Webpackは新しいバージョンだと「""」の空白文字を
// この一覧に含められないようになったので、古い記事を読む際は注意!
resolve: {
extensions: [".ts", ".tsx", ".js"]
},
module: {
// ある拡張子について、どういった処理を行うかについてのオプション
rules: [
{
// 「.tsか.tsxについては、ts-loaderで読み出す。ただしnode_modulesフォルダ以下は除く」
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/
},
{
// 「.csvについては、url-loaderで読み出す。ファイルパスは自然な形式で……」
// 参考→https://qiita.com/tomi_shinwatec/items/ef66a60950939618c449
test: /\.csv$/,
loader: 'url-loader',
options: {
name: '[path][name].[ext]'
}
}
]
},
performance: { hints: false }
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment