Skip to content

Instantly share code, notes, and snippets.

@Keiji-Sugou
Last active September 29, 2016 04:34
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/8edc06df004c1b3a8243383b6a0c36ea to your computer and use it in GitHub Desktop.
Save Keiji-Sugou/8edc06df004c1b3a8243383b6a0c36ea 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 varNum3 = "1gあたりエネルギー(kcal)";
var kirikuchiLabel = [
varAll,
varString1,
varString2,
varNum1,
varNum2,
varNum3
];
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}
];
// varNum3
var lengthArray3 = [
{min: 0.0, max: 0.4},
{min: 0.41, max: 0.80},
{min: 0.81, max: 1.00},
{min: 1.01, max: 1.50}
];
/*
円の大きさの調整用
*/
var circleScale = 16;
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.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 = keyString2;
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;
case 5:
labelsArray.length=0;
var valueNum3 = o.valueNum2/o.valueNum1;
for (var i=0; i<lengthArray3.length; i++) {
labelsArray[i] = lengthArray3[i].min + "〜" + lengthArray3[i].max;
};
var _l = lengthArray3.length;
wScale.domain([0, _l]).range([0, 3]);
for (var j=0; j<_l; j++) {
if ((valueNum3 >= parseFloat(lengthArray3[j].min)) && (valueNum3 <= parseFloat(lengthArray3[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.valueString2];
})
//円の大きさと値を関連づける
.attr('r', function(d){
var valueNum3 = d.valueNum2/d.valueNum1;
return valueNum3 * circleScale;
//return Math.sqrt(d.valueNum2) * 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>',
'<p class="valData">', varNum3 + ":", d.valueNum2/d.valueNum1, '</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 valueString2
明治ブルガリアヨーグルト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