Built with blockbuilder.org
forked from GitNoise's block: 2 axis to 1 axis and back again!
forked from GitNoise's block: Icons on the side of a scatter plot
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
text.right {
text-anchor: end;
}
text.rotate {
transform: rotate(-90deg) translateY(1em);
}
.text line {
marker-end: url(#arrow)
}
line.axis {
stroke: black;
stroke-opacity: 0.3;
stroke-dasharray: 10 5;
}
line.legend {
stroke: black;
}
.position circle, .position path {
fill-opacity: 0.7;
}
.connector {
stroke-width: 2px;
fill: none;
stroke-opacity: 0.4;
}
svg {
margin: 20px;
}
.field {
fill: #efefef;
stroke: #969696;
}
.arrow {
fill: black;
shape-rendering: geometricPrecision;
}
.button {
margin: 20px;
border: 1px solid black;
display: inline-block;
cursor: pointer;
padding: 4px 24px;
font-family: sans-serif;
border-radius: 5px;
background: #efefef;
}
.button:hover {
background: #c9c9c9;
}
</style>
</head>
<body>
<div>
<div class="button" onclick="move()">MOVE</div>
</div>
<svg>
<defs>
<marker id="arrowEnd" markerWidth="15" markerHeight="7"
markerUnits="strokeWidth"
refx="0" refy="3" orient="auto">
<path class="arrow" d="M0,0 L15,3 L0,6 z"></path>
</marker>
<marker id="arrowStart" markerWidth="15" markerHeight="7"
markerUnits="strokeWidth"
refx="15" refy="3" orient="auto">
<path class="arrow" d="M15,0 L0,3 L15,6 z"></path>
</marker>
</defs>
</svg>
<script>
const height = 300;
const width = 300;
const radius = 10;
const margin = radius * 3 + 4;
// Feel free to change or delete any of the code you see in this editor!
var svg = d3.select("body").select("svg")
.attr("width", width)
.attr("height", height);
const axisData = [
{id:"horisontal", x1: 0, x2: width-margin/2, y1: height/2, y2:height/2},
{id: "vertical", x1: width/2, x2: width/2, y1: margin/2, y2:height},
];
const scaleY = d3.scaleLinear()
.domain([0, 100])
.range([margin, height - margin]);
const scaleX = d3.scaleLinear()
.domain([0, 100])
.range([margin, width - margin]);
const rand = () => Math.random() * 101
const palette = ["#b6ae44",
"#8460cc",
"#61b858",
"#c858a9",
"#5f7c3a",
"#7789cd",
"#ce4a3c",
"#4cb6a9",
"#c56179",
"#c37e3f"];
const dotData1 = [
{id: "c1", "cx": 50, "cy": rand()},
{id: "c2", "cx": 50, "cy": rand()},
{id: "c3", "cx": 50, "cy": rand()},
{id: "c4", "cx": 50, "cy": rand()},
{id: "c5", "cx": 50, "cy": rand()},
{id: "c6", "cx": 50, "cy": rand()},
{id: "c7", "cx": 50, "cy": rand()},
{id: "c8", "cx": 50, "cy": rand()},
]
const dotData2 = [
{id: "c1", "cx": rand(), "cy": rand()},
{id: "c2", "cx": rand(), "cy": rand()},
{id: "c3", "cx": rand(), "cy": rand()},
{id: "c4", "cx": rand(), "cy": rand()},
{id: "c5", "cx": rand(), "cy": rand()},
{id: "c6", "cx": rand(), "cy": rand()},
{id: "c7", "cx": rand(), "cy": rand()},
{id: "c8", "cx": rand(), "cy": rand()},
]
const iconsData = [
{id: "i1", color: palette[0]},
{id: "i2", color: palette[1]},
{id: "i3", color: palette[2]},
{id: "i4", color: palette[3]},
{id: "i5", color: palette[4]},
{id: "i6", color: palette[5]},
{id: "i7", color: palette[6]},
{id: "i8", color: palette[7]},
]
const markerLenght = 15;
const legendData = [
{ id: "lr", "x1": margin + markerLenght , "x2": width - margin - markerLenght, "y1": height - margin + 10, "y2": height - margin + 10},
{ id: "pk", "x1": margin - 10, "x2": margin - 10, "y1": margin + markerLenght, "y2": height-margin - markerLenght},
]
const textData = [
{ id: "l", "x": margin, "y": height-4, "text": "vänster", classes:'' },
{ id: "r", "x": width - margin, "y": height-4, "text": "höger", classes:'right'},
{ id: "p", "x": -5, "y": margin, "text": "progressiv", classes: 'rotate right' },
{ id: "k", "x": -5, "y": height-margin, "text": "konservativ", classes:'rotate' },
]
const t = d3.transition()
.duration(1000);
function drawText(data) {
svg.selectAll(".text")
.data(data)
.enter()
.append("g")
.classed("text", true)
.attr("transform", d => `translate(${d.x} ${d.y})`)
.append("text")
.attr("class", d => d.classes)
.text(d => d.text)
}
function drawLegend(data) {
svg.selectAll(".legend")
.data(data)
.enter()
.append("line")
.classed("legend", true)
.attr("x1", d => d.x1)
.attr("x2", d => d.x2)
.attr("y1", d => d.y1)
.attr("y2", d => d.y2)
.attr("marker-end", "url(#arrowEnd)")
.attr("marker-start", "url(#arrowStart)")
}
function drawField() {
svg.append("rect")
.classed("field", true)
.attr("x", scaleX.range()[0])
.attr("y", scaleY.range()[0])
.attr("width", scaleX.range()[1] - scaleX.range()[0])
.attr("height", scaleY.range()[1] - scaleY.range()[0])
}
function drawAxis(data) {
const d = svg.selectAll(".axis")
.data(data, key=>key.id);
d.enter()
.append("line")
.classed("axis", true)
.attr("x1", d => d.x1)
.attr("x2", d => d.x2)
.attr("y1", d => d.y1)
.attr("y2", d => d.y2);
}
function drawArc(x1, y1, x2, y2, r) {
var path = d3.path();
path.moveTo(x1, y1);
path.quadraticCurveTo(
x1 + (x1 < width / 2 ? 50 : -50),
y1 + (y2 > y1 ? 50 : -50),
x2,
y2);
return path.toString();
}
function drawPositions(data) {
const d = svg.selectAll("g.position").data(data);
// update
d.select(".point")
.transition(t)
.attr("cx", d => d.endCx)
.attr("cy", d => d.endCy);
d.select(".connector")
.transition(t)
.attr('d', d => drawArc(d.startCx, d.startCy, d.endCx, d.endCy))
// enter
const g = d.enter().append("g").classed("position", true);
g.append("circle")
.classed("point", true)
.attr("cx", d => d.endCx)
.attr("cy", d => d.endCy)
.attr("r", 4)
.style("fill", d => d.color)
g.append("path")
.classed("connector", true)
.attr('d', d => drawArc(d.startCx, d.startCy, d.endCx, d.endCy))
.style("stroke", d => d.color)
}
function drawIcon(data) {
svg.selectAll("circle.icon")
.data(data, d => d.id)
.enter()
.append("circle")
.classed("icon", true)
.attr("cx", d => d.cx)
.attr("cy", d => d.cy)
.attr("r", radius)
.style("fill", d => d.color)
}
function createIconPositionsData(data) {
const iconsOnEachSide = Math.floor(data.length / 2) - 1;
const distanceY = (scaleY.range()[1] - scaleY.range()[0]) / iconsOnEachSide;
const distanceX = (scaleX.range()[1] - scaleX.range()[0]) / iconsOnEachSide;
const offsetFromTop = (height - iconsOnEachSide * distanceY) / 2;
const offsetFromSide = (height - iconsOnEachSide * distanceX) / 2;
data = data.map((d, i) => {
d.cx = i <= iconsOnEachSide
? width - radius
: (i % (iconsOnEachSide + 1)) * distanceX + offsetFromSide;
d.cy = i <= iconsOnEachSide
? (i % (iconsOnEachSide + 1)) * distanceY + offsetFromTop
: 0 + radius;
return d;
});
return data;
}
function mergeDotAndIconData(dotData, iconsData) {
const result = [];
dotData.forEach((d, i) =>
result.push({
id: "p" + i,
color: iconsData[i].color,
endCx: scaleX(d.cx),
endCy: scaleX(d.cy),
startCx: iconsData[i].cx,
startCy: iconsData[i].cy,
}));
return result;
}
const iconPositionData = createIconPositionsData(iconsData);
const positionData = mergeDotAndIconData(dotData1, iconPositionData);
drawText(textData);
drawLegend(legendData);
drawField();
drawAxis(axisData);
drawPositions(positionData);
drawIcon(iconPositionData)
let first = true;
function move() {
const positionData = mergeDotAndIconData(
first ? dotData2 : dotData1,
iconPositionData);
drawPositions(positionData);
first = !first;
}
</script>
</body>
https://d3js.org/d3.v4.min.js