Skip to content

Instantly share code, notes, and snippets.

@Keiji-Sugou
Last active September 29, 2016 03:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Keiji-Sugou/b9e46c3e276ff0bc94cad87346376c96 to your computer and use it in GitHub Desktop.
Save Keiji-Sugou/b9e46c3e276ff0bc94cad87346376c96 to your computer and use it in GitHub Desktop.
ヨーグルトの栄養成分比較
license: mit
/*
変更可能な設定
- メニューとして表示したい名称
- 使いたい色リスト
- valueNumの値をいくつかにグルーピング
- 円の大きさの調整用
- 値と色を関連づける
- 円の大きさと値を関連づける
*/
$(document).ready(function(){
var Eventer=function(){if(!(this instanceof Eventer))return new Eventer;this.publish=function(c,d){topics=b(c),topics.forEach(function(b){"object"==typeof a[b]&&a[b].forEach(function(a){a.apply(this,d||[])})})},this.subscribe=function(b,c){var d=[].concat(c);return d.forEach(function(c){a[b]||(a[b]=[]),a[b].push(c)}),[b,c]},this.unsubscribe=function(b,c){a[b]&&a[b].forEach(function(d,e){d==c&&a[b].splice(e,1)})},this.queue=function(){return a},this.on=this.subscribe,this.off=this.unsubscribe,this.trigger=this.publish;var a={},b=function(a){return"string"==typeof a?a.split(" "):a};return this};
var eventer = new Eventer;
/*
メニューとして表示したい名称
*/
var varAll = "すべて";
//定性的データ
var varString1 = "メーカー名";
var varString2 = "特徴";
//定量的データ
var varNum1 = "容量(g)";
var varNum2 = "エネルギー(kcal)";
var kirikuchiLabel = [
varAll,
varString1,
varString2,
varNum1,
varNum2
];
var keyString1, keyValue2;
var varForColor;
var labelsArray; //選択した項目の選択肢を表示を入れる入れ物。
/*
使いたい色リスト
*/
var colorArray = [
"#CA03B0",
"#60B00A",
"#AB1008",
"#243753",
"#D0ABE5",
"#225232",
"#F869B6",
"#7E0B21",
"#E96754"
];
/*
valueNumの値をいくつかにグルーピング
*/
// varNum1
var lengthArray1 = [
{min: 80, max: 100},
{min: 101, max: 150},
{min: 151, max: 220}
];
// varNum2
var lengthArray2 = [
{min: 30, max: 90},
{min: 91, max: 120},
{min: 121, max: 150},
{min: 151, max: 180}
];
/*
円の大きさの調整用
*/
var circleScale = 2;
var Chart = function() {
/* --------------------
変数(データの入れ物)の設定
-------------------- */
var margin = 80;
/* --------------------
描画エリアの設定
-------------------- */
var width = 962 - margin * 2,
height = 600 - margin * 2;
var svg = d3.select('#content').append('svg')
.attr('width', width + margin)
.attr('height', height + margin);
var dataContainer = svg.append("svg:g")
.attr("id", "dataCircle")
.attr('width', width + margin)
.attr('height', height + margin)
.attr('transform', 'translate(' + [margin/2, margin/2].join(',') + ')');
var peopleContainer = svg.append("svg:g")
.attr("id", "peopleCircle")
.attr('width', width + margin)
.attr('height', height + margin)
.attr('transform', 'translate(' + [margin/2, margin/2].join(',') + ')');
/* --------------------
スケールの設定
-------------------- */
var wScale = d3.scale.linear();
/* --------------------
イベントリスナー
-------------------- */
var self = this;
this.e = new Eventer;
this.init = function() {
this.e.subscribe( 'load', [this.getData] );
this.e.subscribe( 'load:data', [this.canvas.setup] );
this.e.subscribe( 'draw:menu', [this.drawMenu] );
this.e.subscribe( 'draw', [this.canvas.draw] );
this.e.publish( 'load' );
};
/* --------------------
色んな機能
-------------------- */
//ボタンメニューの生成
this.drawMenu = function() {
var menuItems = d3.select("#menuBlock").select('form').selectAll("span")
.data( kirikuchiLabel )
.enter().append("span").attr("class", "navColumn");
menuItems.append("input")
.attr({
type: "radio",
class: "nav",
name: "nav",
value: function(d, i) {return i;}
})
.attr('id', function(d, i) {
return "id" + i;
})
.attr('value', function(d, i) {
return i;
})
.property("checked", function(d, i) {
if (i === 0) { return true; } else { return false; };
})
.on("change", function(d,i){
self.e.publish(['draw'], [self.data, {selected: i, value: this.value}]);
});
menuItems.append("label")
.attr('for', function(d, i) {
return "id" + i;
})
.text(function(d,i) {
return d;
});
}
/* --------------------
データの取得
-------------------- */
this.getData = function() {
d3.tsv('data_rev02.tsv', function(error, data){
//データ型を確定させる
data.forEach(function(d){
d.valueString1 = String(d.valueString1);
d.valueString2 = String(d.valueString2);
d.valueNum1 = parseInt(d.valueNum1);
d.valueNum2 = parseInt(d.valueNum2);
});
self.data = data;
self.e.publish('load:data', [data]);
});
};
/* --------------------
データの描画
-------------------- */
this.canvas = {
setup: function(data) {
// force layoutの初期化
self.force = d3.layout.force().nodes(data);
//定性的データに含まれるユニークな値を配列化する
keyString1 = d3.set( data.map(function(d){ return d.valueString1 }) ).values(),
options = keyString1.map(function(d) {
return '<option>' + d + '</option>';
}).join('');
keyString2 = d3.set( data.map(function(d){ return d.valueString2 }) ).values(),
options = keyString2.map(function(d) {
return '<option>' + d + '</option>';
}).join('');
/*
値と色を関連づける
*/
varForColor = keyString1;
self.colors = {};
for(i = 0; i < varForColor.length; i++) {
var key = varForColor[i];
self.colors[varForColor[i]] = [colorArray[i]].join('');
}
self.e.publish('draw', [ data ]);
self.e.publish('draw:menu');
self.e.publish('draw:emptyinfo');
},
draw: function(data, filter) {
self.force
.nodes(data)
.charge(function(d){ return -d.valueNum1/2 })
.size([width, height / 2])
.on('tick', function(e){
var center = {
x: width / 2,
y: height / 2 + height / 4
},
centerx;
self.data.forEach(function(o, i) {
if((filter || {}).value) {
switch (filter.selected){
case 0:
labelsArray = [varAll];
centerx = 1;
break;
case 1:
labelsArray = keyString1.slice();
var _l = keyString1.length;
wScale.domain([0, _l]).range([0, 3]);
for (var j=0; j<_l; j++) {
if (o.valueString1 == keyString1[j]) {
centerx = wScale(j);
}
};
break;
case 2:
labelsArray = keyString2.slice();
var _l = keyString2.length;
wScale.domain([0, _l]).range([0, 3]);
for (var j=0; j<_l; j++) {
if (o.valueString2 == keyString2[j]) { centerx = wScale(j); }
};
break;
case 3:
labelsArray.length=0;
for (var i=0; i<lengthArray1.length; i++) {
labelsArray[i] = lengthArray1[i].min + "〜" + lengthArray1[i].max;
};
var _l = lengthArray1.length;
wScale.domain([0, _l]).range([0, 3]);
for (var j=0; j<_l; j++) {
if ((o.valueNum1 >= parseInt(lengthArray1[j].min)) && (o.valueNum1 <= parseInt(lengthArray1[j].max))) {
centerx = wScale(j);
}
};
break;
case 4:
labelsArray.length=0;
for (var i=0; i<lengthArray2.length; i++) {
labelsArray[i] = lengthArray2[i].min + "〜" + lengthArray2[i].max;
};
var _l = lengthArray2.length;
wScale.domain([0, _l]).range([0, 3]);
for (var j=0; j<_l; j++) {
if ((o.valueNum2 >= parseInt(lengthArray2[j].min)) && (o.valueNum2 <= parseInt(lengthArray2[j].max))) {
centerx = wScale(j);
}
};
break;
}
} else {
labelsArray = [varAll];
centerx = 1;
}
o.x += (center.x * centerx - o.x) * e.alpha * 0.04;
o.y += (center.y - o.y) * e.alpha * 0.04;
});
d3.select("#labels").text( labelsArray.join(" - ") );
d3.selectAll('.peopleC').attr("transform", function(d) { return ['translate(', d.x, ', ', d.y, ')'].join(''); });
d3.selectAll('.dataC').attr("transform", function(d) { return ['translate(', d.x, ', ', d.y, ')'].join(''); });
})
.start();
/*
各円の中心にある白い円
*/
var peopleCircles = peopleContainer.selectAll('.peopleC')
.data(data)
peopleCircles.enter()
.append('circle')
.attr('class', 'peopleC')
.attr('r', 0)
.attr('fill', "#FFF")
.call( self.force.drag )
peopleCircles
.transition()
.duration(1100)
.delay(function(d, i) { return i * 10; })
.attr('fill', "#FFF")
.attr('r', 2);
/*
データによりサイズが変化する色のついた円
*/
var dataCircles = dataContainer.selectAll('.dataC')
.data(data)
dataCircles.enter()
.append('circle')
.attr('class', 'dataC')
.attr('r', 0)
.attr('fill', function(d){
return "#FFFFFF";
})
.call( self.force.drag )
dataCircles
.transition()
.duration(1100)
.delay(function(d, i) { return i * 10; })
/* 円を描画 */
.attr('fill', function(d){
return self.colors[d.valueString1];
})
//円の大きさと値を関連づける
.attr('r', function(d){
return Math.sqrt(d.valueNum1) * circleScale;
});
dataCircles.on('mouseenter', function(d){
d3.select(this)
.attr('class', 'dataC active')
d3.select('#tooltip')
.attr('class', 'active')
.html([
'<p class="name">', d.name, '</p>',
'<p class="valData">', varString1 + ":", d.valueString1, '</p>',
'<p class="valData">', varString2 + ":", d.valueString2, '</p>',
'<p class="valData">', varNum1 + ":", d.valueNum1, '</p>',
'<p class="valData">', varNum2 + ":", d.valueNum2, '</p>'
].join(''));
});
dataCircles.on('mouseleave', function(d){
d3.select(this).attr('class', 'dataC')
d3.select('#tooltip').attr('class', 'deactive');
});
}
};
this.init.apply( this, arguments );
};
var chart = new Chart;
});
name valueString1 valueNum1 valueNum2
明治ブルガリアヨーグルトLB81 低糖 180g 明治 180 144
明治ブルガリアヨーグルト脂肪0 苺 180g 明治 180 121
明治ブルガリアヨーグルト脂肪0 ブルーベリー 180g 明治 180 120
明治ブルガリアヨーグルト脂肪0白桃 180g 明治 180 119
明治ブルガリアヨーグルト脂肪0 朝のフルーツミックスアロエプラス 180g 明治 180 117
明治プロビオヨーグルトLG21 明治 112 89
明治プロビオヨーグルトLG21低脂肪112g 明治 112 72
明治プロビオヨーグルトLG21 砂糖0 112g 明治 112 56
明治プロビオヨーグルトLG21 アロエ脂肪0 112g 明治 112 68
明治ヨーグルトR-1 明治 112 89
明治ヨーグルトR-1 ブルーベリー脂肪0 112g 明治 100 68
明治ヨーグルトR-1 低脂肪112g 明治 112 72
ラクトフェリンヨーグルト 森永 112 100
濃密ギリシャヨーグルト PARTHENOはちみつ付 森永 90 108
濃密ギリシャヨーグルト PARTHENOグリーンアップルソース付 森永 90 107
濃密ギリシャヨーグルト PARTHENOラズベリーソース付 森永 90 106
濃密ギリシャヨーグルト PARTHENOプレーン 森永 110 110
Doubleiマンゴー 森永 90 88
Doubleiブルーベリー 森永 90 86
ビヒダスBB536 プレーン加糖タイプ 森永 120 117
森永アロエヨーグルト 森永 125 108
森永大粒アロエ&ヨーグルト 森永 140 115
森永アロエヨーグルト +な素材 アセロラ&クランベリー 森永 118 100
森永アロエヨーグルト 脂肪0 森永 120 60
カラダ強くするヨーグルト ラクトフェリンとビフィズス菌BB536 森永 100 93
素材そのまま ブルーベリーのヨーグルト 森永 100 95
素材そのまま マンゴーのヨーグルト 森永 100 88
素材そのまま いちごのヨーグルト 森永 100 91
これでフルーツ10種類ヨーグルト フルーツミックス 森永 220 132
これでフルーツ10種類ヨーグルト 黄色のビタミンフルーツミックス 森永 220 126
森永ヨーグルト 森永 100 99
恵 megumi ガセリ菌SP株ヨーグルト 100g 雪印メグミルク 100 35
恵 megumi ガセリ菌SP株ヨーグルト アロエ 雪印メグミルク 100 53
恵 megumi ビフィズス菌SP株カプセルヨーグルト 100g 雪印メグミルク 100 81
バニラヨーグルト 日本ルナ 100 111
バニラヨーグルトシチリアレモン 日本ルナ 100 104
バニラヨーグルト あまおう苺 日本ルナ 100 111
ヨーグルト&シリアル 玄米フレーク 日本ルナ 108 121
ヨーグルト&シリアル ライスチョコ 日本ルナ 107 116
アサイーヨーグルトボウル 日本ルナ 110 99
HN019ヨーグルト 豊富に食物センイ 日本ルナ 105 54
さくさくパン×ドライフルーツヨーグルト 日本ルナ 121 158
PIERRE HERME' ヨーグルト Envie オハヨー乳業 120 179
PIERRE HERME' ヨーグルト Infiniment Mandarine オハヨー乳業 120 174
甘熟 アップルマンゴーヨーグルト オハヨー乳業 130 92
季節の果実 キウイ&ヨーグルト オハヨー乳業 130 100
贅沢果実 いちごヨーグルト オハヨー乳業 130 112
贅沢果実 ブルーベリーヨーグルト オハヨー乳業 130 112
Hanakoとコラボした生乳ヨーグルト オハヨー乳業 100 93
Hanakoとコラボした1日分の鉄分ヨーグルト オハヨー乳業 110 87
小岩井 生乳100%ヨーグルト 200g 小岩井乳業 200 130
小岩井 プレミアムクリームヨーグルトグルメファン 130g 小岩井乳業 130 143
小岩井 クリームヨーグルト クレマ 85g オレンジはちみつ入り 小岩井乳業 85 117
小岩井 クリームヨーグルト クレマ 85g ピーチクリーム 小岩井乳業 85 119
小岩井 大人の元気ヨーグルト 105g 小岩井乳業 105 79
小岩井 金色ヨーグルト 120g 小岩井乳業 120 127
小岩井 まきばヨーグルト こだわりクリーミー 95g 小岩井乳業 95 101
増加型ビフィズス菌 LKM512ヨーグルト メイトー 100 85
ドール アサイーミックス&ヨーグルト メイトー 190 118
ドール マスカットミックス&ヨーグルト メイトー 190 117
ドール フルーツミックス&ヨーグルト メイトー 200 126
ドール ベリーミックス&ヨーグルト メイトー 200 126
ドール マンゴーミックス&ヨーグルト メイトー 200 124
ドール アロエミックス&ヨーグルト メイトー 200 126
朝食BifiXヨーグルト グリコ乳業 150 106
朝食BifiXヨーグルト脂肪0 グリコ乳業 100 38
朝食BifiXヨーグルト フルーツプルーン グリコ乳業 145 122
朝食りんごヨーグルト グリコ乳業 145 123
朝食みかん&ヨーグルト グリコ乳業 145 117
ヨーグルト健康 グリコ乳業 125 126
ドクターPiroヨーグルト 110g グリコ乳業 110 93
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="style.css" />
<script src="//code.jquery.com/jquery-1.12.0.min.js"></script>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//d3js.org/queue.v1.min.js" charset="utf-8"></script>
<script src="app.js"></script>
</head>
<body>
<div id="header">
<div id="menuBlock">
<form></form>
</div>
</div>
<div id="subheader">
<div id="labels"></div>
</div>
<div id="content"></div>
<div id="tooltip" class="deactive"></div>
</body>
</html>
/* ----------
COMMON
---------- */
body {
font: 13px/1.5 helvetica, sans-serif;
text-align:center;
}
* {
-webkit-font-smoothing:anitaliased;
}
/* ----------
LAYOUT
---------- */
#header {
height: 60px;
margin-bottom: 30px;
}
#subheader {
width: 100%;
position: absolute;
left: 0%;
}
#labels {
text-align: center;
}
/* ----------
ELEMENT
---------- */
#tooltip {
padding:.5em .8em;
background:rgb(255,255,255);
box-shadow:0 0 .3em silver;
font-size:1.2em;
border-radius:.5em;
position:absolute;
z-index:9999;
min-width:100px;
left: 20px;
top: 100px;
text-align: left;
}
#tooltip.active {
display: block;
}
#tooltip.deactive {
display: none;
}
.dataC {
stroke:black;
stroke-width:2px;
stroke-opacity:.1;
transition: all .4s ease;
fill-opacity: 0.4;
}
.dataC.active {
stroke-width:4px;
stroke-opacity:.2;
fill-opacity: 0.8;
}
.valData {
font-size:1.1em;
line-height:1.4;
margin-top:.2em;
}
/* ----------
NAVIGATION
---------- */
input[type=radio] {
display:none;
}
input[type=radio] + label {
display:inline-block;
margin:-2px;
padding: 8px 10px;
margin-bottom: 0;
font-size: 0.8em;
line-height: 20px;
color: #666;
text-align: center;
text-shadow: 0 1px 1px rgba(255,255,255,0.75);
vertical-align: middle;
cursor: pointer;
background-color: #f5f5f5;
background-image: -moz-linear-gradient(top,#fff,#e6e6e6);
background-image: -webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));
background-image: -webkit-linear-gradient(top,#fff,#e6e6e6);
background-image: -o-linear-gradient(top,#fff,#e6e6e6);
background-image: linear-gradient(to bottom,#fff,#e6e6e6);
background-repeat: repeat-x;
border: 1px solid #ccc;
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);
border-bottom-color: #b3b3b3;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
-webkit-border-radius: 10;
-moz-border-radius: 10;
border-radius: 10px;
margin: 0 2px;
}
input[type=radio] + label:hover {
color: #000000;
}
input[type=radio] + label:active{
background-color: #e6e6e6;
background-image: -moz-linear-gradient(top,#e6e6e6,#e6e6e6);
background-image: -webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#e6e6e6));
background-image: -webkit-linear-gradient(top,#e6e6e6,#e6e6e6);
background-image: -o-linear-gradient(top,#e6e6e6,#e6e6e6);
background-image: linear-gradient(to bottom,#e6e6e6,#e6e6e6);
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0),0 1px 2px rgba(0,0,0,0);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0),0 1px 2px rgba(0,0,0,0);
box-shadow: inset 0 1px 0 rgba(255,255,255,0),0 1px 2px rgba(0,0,0,0);
}
input[type=radio]:checked + label {
background-image: none;
outline: 0;
-webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,0.05),0 1px 2px rgba(0,0,0,0.05);
-moz-box-shadow: inset 0 2px 4px rgba(0,0,0,0.05),0 1px 2px rgba(0,0,0,0.05);
box-shadow: inset 0 2px 4px rgba(0,0,0,0.05),0 1px 2px rgba(0,0,0,0.05);
background-color:#e0e0e0;
color: #888;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment