Skip to content

Instantly share code, notes, and snippets.

@russellgoldenberg
Last active May 2, 2017
Embed
What would you like to do?
weighted scatter plot v1
license: mit
height: 500
border: no
*{box-sizing:border-box}body{color:#333;font-family:Helvetica,Arial,sans-serif}.graphic__hed{text-align:center;color:#333}.chart{max-width:40rem;margin:0 auto;text-align:center}.slider{max-width:20rem;margin:1rem auto;position:relative;padding-top:16.5px}.slider input{display:block;width:100%}.slider:before{content:'Quality';text-transform:uppercase;font-size:11px;letter-spacing:.05em;color:#888;display:inline-block;position:absolute;top:0;left:0}.slider:after{content:'Quantity';text-transform:uppercase;font-size:11px;letter-spacing:.05em;color:#888;display:inline-block;position:absolute;top:0;right:0}.g-axis line,.g-axis path{stroke:#ccc}.g-axis text{fill:#888}.g-axis .axis__label{text-transform:uppercase;font-size:11px;letter-spacing:.05em;fill:#888}.item{fill-opacity:.75}
"use strict";function weightData(t){var a=t.x,e=t.y;return dummyData.map(function(t){return _extends({},t,{score:t.x*a+t.y*e})}).sort(function(t,a){return d3.descending(t.score,a.score)}).map(function(t,a){return _extends({},t,{rank:a})}).reverse()}function resize(){var t=.8*Math.min(el.node().offsetWidth,window.innerHeight);chart.width(t).height(t),el.call(chart)}function scatterplot(){function t(t,a){return"translate("+t+", "+a+")"}function a(t){var a=t.container,e=t.data,n=a.selectAll("svg").data([e]),r=n.enter().append("svg"),i=r.append("g"),c=i.append("g").attr("class","g-axis"),s=c.append("g").attr("class","axis axis--x"),l=c.append("g").attr("class","axis axis--y");s.append("text").attr("class","axis__label").attr("text-anchor","middle").text("Quantity"),l.append("text").attr("class","axis__label").attr("text-anchor","middle").text("Quality"),i.append("g").attr("class","g-plot")}function e(t){t.container,t.data}function n(t){var a=t.data;l.domain([0,MAX_VAL]).range([0,g]),o.domain([0,MAX_VAL]).range([p,0]),d.domain([0,a.length]).range([20,2]),u.domain(a.map(function(t){return t.rank})).range(COLORS)}function r(a){var e=a.container,n=(a.data,e.select("svg"));n.attr("width",f).attr("height",h);var r=n.select("g");r.attr("transform",t(s,s));var i=r.select(".g-plot"),c=i.selectAll(".item").data(function(t){return t},function(t){return t.index});c.enter().append("circle").attr("class","item").merge(c).transition().duration(100).ease(d3.easeLinear).attr("x",0).attr("y",0).attr("r",function(t){return d(t.rank)}).style("fill",function(t){return u(t.rank)}).style("stroke",function(t){return d3.color(u(t.rank)).darker(.7)}).attr("transform",function(a){return t(l(a.x),o(a.y))})}function i(a){var e=a.container,n=(a.data,e.select(".g-axis")),r=d3.axisLeft(o),i=d3.axisBottom(l),c=n.select(".axis--x");c.attr("transform",t(0,p)).call(i);var d=n.select(".axis--y");d.call(r),c.select(".axis__label").attr("x",g/2).attr("y",s-1),d.select(".axis__label").attr("x",-p/2).attr("y",-s+FONT_SIZE).attr("transform","rotate(-90)")}function c(t){var c=t.datum();a({container:t,data:c}),e({container:t,data:c}),n({container:t,data:c}),r({container:t,data:c}),i({container:t,data:c})}var s=3*FONT_SIZE,l=d3.scaleLinear(),o=d3.scaleLinear(),d=d3.scaleSqrt(),u=d3.scaleQuantile(),f=500,h=500,g=f-3*s,p=h-3*s;return c.width=function(){return arguments.length?(f=arguments.length<=0?void 0:arguments[0],g=f-3*s,c):f},c.height=function(){return arguments.length?(h=arguments.length<=0?void 0:arguments[0],p=h-3*s,c):h},c}function handleInput(){var t=+this.value,a=t,e=100-t,n=weightData({x:a,y:e});el.datum(n),el.call(chart)}function init(){el.datum(weightData({x:50,y:50})),el.call(chart),resize(),window.addEventListener("resize",resize),graphic.select(".slider input").on("input",handleInput)}var _extends=Object.assign||function(t){for(var a=1;a<arguments.length;a++){var e=arguments[a];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])}return t},graphic=d3.select(".graphic--1"),COLORS=["#ff3814","#fe5c34","#fc764f","#f88d69","#f2a385","#e8b8a0","#dbcdbd"],FONT_SIZE=11,MAX_VAL=10,dummyData=d3.range(0,50).map(function(t){return{index:t,x:.5+Math.random()*(MAX_VAL-1),y:.5+Math.random()*(MAX_VAL-1)}}),chart=scatterplot(),el=graphic.select(".chart");init();
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNjcmlwdC5qcyJdLCJuYW1lcyI6WyJ3ZWlnaHREYXRhIiwiX3JlZiIsIngiLCJ5IiwiZHVtbXlEYXRhIiwibWFwIiwiZCIsIl9leHRlbmRzIiwic2NvcmUiLCJjaGFydCIsInNjYXR0ZXJwbG90Iiwic29ydCIsImEiLCJiIiwiZDMiLCJkZXNjZW5kaW5nIiwicmFuayIsImkiLCJyZXZlcnNlIiwicmVzaXplIiwiTWF0aCIsIm1pbiIsImVsIiwibm9kZSIsIm9mZnNldFdpZHRoIiwid2luZG93IiwiaW5uZXJIZWlnaHQiLCJ3aWR0aCIsInNjYWxlWCIsInNjYWxlTGluZWFyIiwiZW50ZXIiLCJfcmVmMiIsImNvbnRhaW5lciIsImRhdGEiLCJzZWxlY3RBbGwiLCJoZWlnaHQiLCJzdmciLCJhcHBlbmQiLCJjaGFydFdpZHRoIiwibWFyZ2luIiwiYXhpcyIsImdFbnRlciIsImF0dHIiLCJ0ZXh0Iiwic3ZnRW50ZXIiLCJfcmVmMyIsInVwZGF0ZVNjYWxlcyIsIl9yZWY0IiwiZG9tYWluIiwiTUFYX1ZBTCIsInNjYWxlWSIsImV4aXQiLCJyYW5nZSIsInNjYWxlUiIsImxlbmd0aCIsInNjYWxlQyIsIkNPTE9SUyIsIl9yZWY1Iiwic2VsZWN0IiwiZyIsInVwZGF0ZURvbSIsInBsb3QiLCJpbmRleCIsIml0ZW0iLCJ0cmFuc2l0aW9uIiwiZHVyYXRpb24iLCJlYXNlIiwiZWFzZUxpbmVhciIsInN0eWxlIiwiY29sb3IiLCJkYXJrZXIiLCJ0cmFuc2xhdGUiLCJ1cGRhdGVBeGlzIiwiX3JlZjYiLCJheGlzTGVmdCIsImF4aXNCb3R0b20iLCJjaGFydEhlaWdodCIsImNhbGwiLCJGT05UX1NJWkUiLCJkYXR1bSIsInNjYWxlU3FydCIsInN6IiwiYXJndW1lbnRzIiwiaGFuZGxlSW5wdXQiLCJ2YWwiLCJ0aGlzIiwidmFsdWUiLCJ3ZWlnaHRlZCIsImluaXQiLCJhZGRFdmVudExpc3RlbmVyIiwib24iLCJncmFwaGljIiwicmFuZG9tIl0sIm1hcHBpbmdzIjoiWUFL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b250ZW50IjpbImNvbnN0IGdyYXBoaWMgPSBkMy5zZWxlY3QoJy5ncmFwaGljLS0xJylcblxuY29uc3QgQ09MT1JTID0gWycjZmYzODE0JywgJyNmZTVjMzQnLCAnI2ZjNzY0ZicsICcjZjg4ZDY5JywgJyNmMmEzODUnLCAnI2U4YjhhMCcsICcjZGJjZGJkJ11cbmNvbnN0IEZPTlRfU0laRSA9IDExXG5jb25zdCBNQVhfVkFMID0gMTBcbmNvbnN0IGR1bW15RGF0YSA9IGQzLnJhbmdlKDAsIDUwKS5tYXAoZCA9PiAoe1xuXHRpbmRleDogZCxcblx0eDogMC41ICsgTWF0aC5yYW5kb20oKSAqIChNQVhfVkFMIC0gMSksXG5cdHk6IDAuNSArIE1hdGgucmFuZG9tKCkgKiAoTUFYX1ZBTCAtIDEpLFxufSkpXG5cbmNvbnN0IGNoYXJ0ID0gc2NhdHRlcnBsb3QoKVxuY29uc3QgZWwgPSBncmFwaGljLnNlbGVjdCgnLmNoYXJ0JylcblxuZnVuY3Rpb24gd2VpZ2h0RGF0YSh7IHgsIHkgfSkge1xuXHRyZXR1cm4gZHVtbXlEYXRhLm1hcChkID0+ICh7XG5cdFx0Li4uZCxcblx0XHRzY29yZTogZC54ICogeCArIGQueSAqIHksXG5cdH0pKVxuXHQuc29ydCgoYSwgYikgPT4gZDMuZGVzY2VuZGluZyhhLnNjb3JlLCBiLnNjb3JlKSlcblx0Lm1hcCgoZCwgaSkgPT4gKHtcblx0XHQuLi5kLFxuXHRcdHJhbms6IGksXG5cdH0pKVxuXHQucmV2ZXJzZSgpXG59XG5cbmZ1bmN0aW9uIHJlc2l6ZSgpIHtcblx0Y29uc3Qgc3ogPSBNYXRoLm1pbihlbC5ub2RlKCkub2Zmc2V0V2lkdGgsIHdpbmRvdy5pbm5lckhlaWdodCkgKiAwLjhcblx0Y2hhcnQud2lkdGgoc3opLmhlaWdodChzeilcblx0ZWwuY2FsbChjaGFydClcbn1cblxuZnVuY3Rpb24gc2NhdHRlcnBsb3QoKSB7XG5cdGNvbnN0IG1hcmdpbiA9IEZPTlRfU0laRSAqIDNcblx0Y29uc3Qgc2NhbGVYID0gZDMuc2NhbGVMaW5lYXIoKVxuXHRjb25zdCBzY2FsZVkgPSBkMy5zY2FsZUxpbmVhcigpXG5cdGNvbnN0IHNjYWxlUiA9IGQzLnNjYWxlU3FydCgpXG5cdGNvbnN0IHNjYWxlQyA9IGQzLnNjYWxlUXVhbnRpbGUoKVxuXG5cdGxldCB3aWR0aCA9IDUwMFxuXHRsZXQgaGVpZ2h0ID0gNTAwXG5cdGxldCBjaGFydFdpZHRoID0gd2lkdGggLSBtYXJnaW4gKiAzXG5cdGxldCBjaGFydEhlaWdodCA9IGhlaWdodCAtIG1hcmdpbiAqIDNcblxuXHRmdW5jdGlvbiB0cmFuc2xhdGUoeCwgeSkge1xuXHRcdHJldHVybiBgdHJhbnNsYXRlKCR7eH0sICR7eX0pYFxuXHR9XG5cblx0ZnVuY3Rpb24gZW50ZXIoeyBjb250YWluZXIsIGRhdGEgfSkge1xuXHRcdGNvbnN0IHN2ZyA9IGNvbnRhaW5lci5zZWxlY3RBbGwoJ3N2ZycpLmRhdGEoW2RhdGFdKVxuXHRcdGNvbnN0IHN2Z0VudGVyID0gc3ZnLmVudGVyKCkuYXBwZW5kKCdzdmcnKVxuICAgICAgXHRjb25zdCBnRW50ZXIgPSBzdmdFbnRlci5hcHBlbmQoJ2cnKVxuXHRcdFxuXHRcdGNvbnN0IGF4aXMgPSBnRW50ZXIuYXBwZW5kKCdnJykuYXR0cignY2xhc3MnLCAnZy1heGlzJylcblxuXHRcdGNvbnN0IHggPSBheGlzLmFwcGVuZCgnZycpLmF0dHIoJ2NsYXNzJywgJ2F4aXMgYXhpcy0teCcpXG5cblx0XHRjb25zdCB5ID0gYXhpcy5hcHBlbmQoJ2cnKS5hdHRyKCdjbGFzcycsICdheGlzIGF4aXMtLXknKVxuXG5cdFx0eC5hcHBlbmQoJ3RleHQnKS5hdHRyKCdjbGFzcycsICdheGlzX19sYWJlbCcpXG5cdFx0XHQuYXR0cigndGV4dC1hbmNob3InLCAnbWlkZGxlJylcblx0XHRcdC50ZXh0KCdRdWFudGl0eScpXG5cblx0XHR5LmFwcGVuZCgndGV4dCcpLmF0dHIoJ2NsYXNzJywgJ2F4aXNfX2xhYmVsJylcblx0XHRcdC5hdHRyKCd0ZXh0LWFuY2hvcicsICdtaWRkbGUnKVxuXHRcdFx0LnRleHQoJ1F1YWxpdHknKVxuXG5cdFx0Z0VudGVyLmFwcGVuZCgnZycpLmF0dHIoJ2NsYXNzJywgJ2ctcGxvdCcpXG5cdH1cblxuXHRmdW5jdGlvbiBleGl0KHsgY29udGFpbmVyLCBkYXRhIH0pIHtcblx0fVxuXG5cdGZ1bmN0aW9uIHVwZGF0ZVNjYWxlcyh7IGRhdGEgfSkge1xuXHRcdHNjYWxlWFxuXHRcdFx0LmRvbWFpbihbMCwgTUFYX1ZBTF0pXG5cdFx0XHQucmFuZ2UoWzAsIGNoYXJ0V2lkdGhdKVxuXG5cdFx0c2NhbGVZXG5cdFx0XHQuZG9tYWluKFswLCBNQVhfVkFMXSlcblx0XHRcdC5yYW5nZShbY2hhcnRIZWlnaHQsIDBdKVxuXG5cdFx0c2NhbGVSXG5cdFx0XHQuZG9tYWluKFswLCBkYXRhLmxlbmd0aF0pXG5cdFx0XHQucmFuZ2UoWzIwLCAyXSlcblxuXHRcdHNjYWxlQ1xuXHRcdFx0LmRvbWFpbihkYXRhLm1hcChkID0+IGQucmFuaykpXG5cdFx0XHQucmFuZ2UoQ09MT1JTKVxuXHR9XG5cblx0ZnVuY3Rpb24gdXBkYXRlRG9tKHsgY29udGFpbmVyLCBkYXRhIH0pIHtcblx0XHRjb25zdCBzdmcgPSBjb250YWluZXIuc2VsZWN0KCdzdmcnKVxuXHRcdFxuXHRcdHN2Z1xuXHRcdFx0LmF0dHIoJ3dpZHRoJywgd2lkdGgpXG5cdFx0XHQuYXR0cignaGVpZ2h0JywgaGVpZ2h0KVxuXG5cdFx0Y29uc3QgZyA9IHN2Zy5zZWxlY3QoJ2cnKVxuXHRcdFxuXHRcdGcuYXR0cigndHJhbnNmb3JtJywgdHJhbnNsYXRlKG1hcmdpbiwgbWFyZ2luKSlcblxuXHRcdGNvbnN0IHBsb3QgPSBnLnNlbGVjdCgnLmctcGxvdCcpXG5cblx0XHRjb25zdCBpdGVtID0gcGxvdC5zZWxlY3RBbGwoJy5pdGVtJykuZGF0YShkID0+IGQsIGQgPT4gZC5pbmRleClcblx0XHRcblx0XHRpdGVtLmVudGVyKCkuYXBwZW5kKCdjaXJjbGUnKVxuXHRcdFx0LmF0dHIoJ2NsYXNzJywgJ2l0ZW0nKVxuXHRcdC5tZXJnZShpdGVtKVxuXHRcdFx0LnRyYW5zaXRpb24oKVxuXHRcdFx0LmR1cmF0aW9uKDEwMClcblx0XHRcdC5lYXNlKGQzLmVhc2VMaW5lYXIpXG5cdFx0XHQuYXR0cigneCcsIDApXG5cdFx0XHQuYXR0cigneScsIDApXG5cdFx0XHQuYXR0cigncicsIGQgPT4gc2NhbGVSKGQucmFuaykpXG5cdFx0XHQuc3R5bGUoJ2ZpbGwnLCBkID0+IHNjYWxlQyhkLnJhbmspKVxuXHRcdFx0LnN0eWxlKCdzdHJva2UnLCBkID0+IGQzLmNvbG9yKHNjYWxlQyhkLnJhbmspKS5kYXJrZXIoMC43KSlcblx0XHRcdC5hdHRyKCd0cmFuc2Zvcm0nLCAgZCA9PiB0cmFuc2xhdGUoc2NhbGVYKGQueCksIHNjYWxlWShkLnkpKSlcblx0fVxuXG5cdGZ1bmN0aW9uIHVwZGF0ZUF4aXMoeyBjb250YWluZXIsIGRhdGEgfSkge1xuXHRcdGNvbnN0IGF4aXMgPSBjb250YWluZXIuc2VsZWN0KCcuZy1heGlzJylcblxuXHRcdGNvbnN0IGF4aXNMZWZ0ID0gZDMuYXhpc0xlZnQoc2NhbGVZKVxuXHRcdGNvbnN0IGF4aXNCb3R0b20gPSBkMy5heGlzQm90dG9tKHNjYWxlWClcblxuXHRcdGNvbnN0IHggPSBheGlzLnNlbGVjdCgnLmF4aXMtLXgnKVxuXHRcdFxuXHRcdHguYXR0cigndHJhbnNmb3JtJywgdHJhbnNsYXRlKDAsIGNoYXJ0SGVpZ2h0KSlcblx0XHRcdC5jYWxsKGF4aXNCb3R0b20pXG5cblx0XHRjb25zdCB5ID0gYXhpcy5zZWxlY3QoJy5heGlzLS15JylcblxuXHRcdHkuY2FsbChheGlzTGVmdClcblxuXHRcdHguc2VsZWN0KCcuYXhpc19fbGFiZWwnKVxuXHRcdFx0LmF0dHIoJ3gnLCBjaGFydFdpZHRoIC8gMilcblx0XHRcdC5hdHRyKCd5JywgbWFyZ2luIC0gMSlcblxuXHRcdHkuc2VsZWN0KCcuYXhpc19fbGFiZWwnKVxuXHRcdFx0LmF0dHIoJ3gnLCAtY2hhcnRIZWlnaHQgLyAyKVxuXHRcdFx0LmF0dHIoJ3knLCAtbWFyZ2luICsgRk9OVF9TSVpFKVxuXHRcdFx0LmF0dHIoJ3RyYW5zZm9ybScsIGByb3RhdGUoLTkwKWApXG5cblxuXHR9XG5cblx0ZnVuY3Rpb24gY2hhcnQoY29udGFpbmVyKSB7XG5cdFx0Y29uc3QgZGF0YSA9IGNvbnRhaW5lci5kYXR1bSgpXG5cdFx0XG5cdFx0ZW50ZXIoeyBjb250YWluZXIsIGRhdGEgfSlcblx0XHRleGl0KHsgY29udGFpbmVyLCBkYXRhIH0pXG5cdFx0dXBkYXRlU2NhbGVzKHsgY29udGFpbmVyLCBkYXRhIH0pXG5cdFx0dXBkYXRlRG9tKHsgY29udGFpbmVyLCBkYXRhIH0pXG5cdFx0dXBkYXRlQXhpcyh7IGNvbnRhaW5lciwgZGF0YSB9KVxuXHR9XG5cblx0Y2hhcnQud2lkdGggPSBmdW5jdGlvbiguLi5hcmdzKSB7XG5cdFx0aWYgKCFhcmdzLmxlbmd0aCkgcmV0dXJuIHdpZHRoXG5cdFx0d2lkdGggPSBhcmdzWzBdXG5cdFx0Y2hhcnRXaWR0aCA9IHdpZHRoIC0gbWFyZ2luICogM1xuXHRcdHJldHVybiBjaGFydFxuXHR9XG5cblx0Y2hhcnQuaGVpZ2h0ID0gZnVuY3Rpb24oLi4uYXJncykge1xuXHRcdGlmICghYXJncy5sZW5ndGgpIHJldHVybiBoZWlnaHRcblx0XHRoZWlnaHQgPSBhcmdzWzBdXG5cdFx0Y2hhcnRIZWlnaHQgPSBoZWlnaHQgLSBtYXJnaW4gKiAzXG5cdFx0cmV0dXJuIGNoYXJ0XG5cdH1cblxuXHRyZXR1cm4gY2hhcnRcbn1cblxuZnVuY3Rpb24gaGFuZGxlSW5wdXQoKSB7XG5cdGNvbnN0IHZhbCA9ICt0aGlzLnZhbHVlXG5cdGNvbnN0IHggPSB2YWxcblx0Y29uc3QgeSA9IDEwMCAtIHZhbFxuXHRjb25zdCB3ZWlnaHRlZCA9IHdlaWdodERhdGEoeyB4LCB5IH0pXG5cdGVsLmRhdHVtKHdlaWdodGVkKVxuXHRlbC5jYWxsKGNoYXJ0KVxufVxuXG5mdW5jdGlvbiBpbml0KCkge1xuXHRlbC5kYXR1bSh3ZWlnaHREYXRhKHsgeDogNTAsIHk6IDUwIH0pKVxuXHRlbC5jYWxsKGNoYXJ0KVxuXHRyZXNpemUoKVxuXHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigncmVzaXplJywgcmVzaXplKVxuXHRncmFwaGljLnNlbGVjdCgnLnNsaWRlciBpbnB1dCcpLm9uKCdpbnB1dCcsIGhhbmRsZUlucHV0KVxufVxuXG5pbml0KClcbiJdfQ==
<!DOCTYPE html>
<title>weighted pivot scatter plot v1</title>
<link href='dist.css' rel='stylesheet' />
<body>
<div class='graphic graphic--1'>
<div class='chart'></div>
<div class='slider'><input type='range' min='0' max='100' value='50'></div>
</div>
<script src='https://d3js.org/d3.v4.min.js'></script>
<script src='dist.js'></script>
</body>
const graphic = d3.select('.graphic--1')
const COLORS = ['#ff3814', '#fe5c34', '#fc764f', '#f88d69', '#f2a385', '#e8b8a0', '#dbcdbd']
const FONT_SIZE = 11
const MAX_VAL = 10
const dummyData = d3.range(0, 50).map(d => ({
index: d,
x: 0.5 + Math.random() * (MAX_VAL - 1),
y: 0.5 + Math.random() * (MAX_VAL - 1),
}))
const chart = scatterplot()
const el = graphic.select('.chart')
function weightData({ x, y }) {
return dummyData.map(d => ({
...d,
score: d.x * x + d.y * y,
}))
.sort((a, b) => d3.descending(a.score, b.score))
.map((d, i) => ({
...d,
rank: i,
}))
.reverse()
}
function resize() {
const sz = Math.min(el.node().offsetWidth, window.innerHeight) * 0.8
chart.width(sz).height(sz)
el.call(chart)
}
function scatterplot() {
const margin = FONT_SIZE * 3
const scaleX = d3.scaleLinear()
const scaleY = d3.scaleLinear()
const scaleR = d3.scaleSqrt()
const scaleC = d3.scaleQuantile()
let width = 500
let height = 500
let chartWidth = width - margin * 3
let chartHeight = height - margin * 3
function translate(x, y) {
return `translate(${x}, ${y})`
}
function enter({ container, data }) {
const svg = container.selectAll('svg').data([data])
const svgEnter = svg.enter().append('svg')
const gEnter = svgEnter.append('g')
const axis = gEnter.append('g').attr('class', 'g-axis')
const x = axis.append('g').attr('class', 'axis axis--x')
const y = axis.append('g').attr('class', 'axis axis--y')
x.append('text').attr('class', 'axis__label')
.attr('text-anchor', 'middle')
.text('Quantity')
y.append('text').attr('class', 'axis__label')
.attr('text-anchor', 'middle')
.text('Quality')
gEnter.append('g').attr('class', 'g-plot')
}
function exit({ container, data }) {
}
function updateScales({ data }) {
scaleX
.domain([0, MAX_VAL])
.range([0, chartWidth])
scaleY
.domain([0, MAX_VAL])
.range([chartHeight, 0])
scaleR
.domain([0, data.length])
.range([20, 2])
scaleC
.domain(data.map(d => d.rank))
.range(COLORS)
}
function updateDom({ container, data }) {
const svg = container.select('svg')
svg
.attr('width', width)
.attr('height', height)
const g = svg.select('g')
g.attr('transform', translate(margin, margin))
const plot = g.select('.g-plot')
const item = plot.selectAll('.item').data(d => d, d => d.index)
item.enter().append('circle')
.attr('class', 'item')
.merge(item)
.transition()
.duration(100)
.ease(d3.easeLinear)
.attr('x', 0)
.attr('y', 0)
.attr('r', d => scaleR(d.rank))
.style('fill', d => scaleC(d.rank))
.style('stroke', d => d3.color(scaleC(d.rank)).darker(0.7))
.attr('transform', d => translate(scaleX(d.x), scaleY(d.y)))
}
function updateAxis({ container, data }) {
const axis = container.select('.g-axis')
const axisLeft = d3.axisLeft(scaleY)
const axisBottom = d3.axisBottom(scaleX)
const x = axis.select('.axis--x')
x.attr('transform', translate(0, chartHeight))
.call(axisBottom)
const y = axis.select('.axis--y')
y.call(axisLeft)
x.select('.axis__label')
.attr('x', chartWidth / 2)
.attr('y', margin - 1)
y.select('.axis__label')
.attr('x', -chartHeight / 2)
.attr('y', -margin + FONT_SIZE)
.attr('transform', `rotate(-90)`)
}
function chart(container) {
const data = container.datum()
enter({ container, data })
exit({ container, data })
updateScales({ container, data })
updateDom({ container, data })
updateAxis({ container, data })
}
chart.width = function(...args) {
if (!args.length) return width
width = args[0]
chartWidth = width - margin * 3
return chart
}
chart.height = function(...args) {
if (!args.length) return height
height = args[0]
chartHeight = height - margin * 3
return chart
}
return chart
}
function handleInput() {
const val = +this.value
const x = val
const y = 100 - val
const weighted = weightData({ x, y })
el.datum(weighted)
el.call(chart)
}
function init() {
el.datum(weightData({ x: 50, y: 50 }))
el.call(chart)
resize()
window.addEventListener('resize', resize)
graphic.select('.slider input').on('input', handleInput)
}
init()
$gray = #888
$gray-light = #aaa
$gray-lighter = #ccc
$gray-dark = #333
$sans = Helvetica, Arial, sans-serif
$small-font = 11px
*
box-sizing border-box
body
color $gray-dark
font-family $sans
.graphic__hed
text-align center
color $gray-dark
.chart
max-width 40rem
margin 0 auto
text-align center
.slider
max-width 20rem
margin 1rem auto
position relative
padding-top $small-font * 1.5
input
display block
width 100%
&:before
content 'Quality'
text-transform uppercase
font-size $small-font
letter-spacing 0.05em
color $gray
display inline-block
position absolute
top 0
left 0
&:after
content 'Quantity'
text-transform uppercase
font-size $small-font
letter-spacing 0.05em
color $gray
display inline-block
position absolute
top 0
right 0
.g-axis
line, path
stroke $gray-lighter
text
fill $gray
.g-axis .axis__label
text-transform uppercase
font-size $small-font
letter-spacing 0.05em
fill $gray
.item
fill-opacity 0.75
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment