Published unlisted
Edited
Aug 29, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function simpleAvg(p1, p2) {
return (p1 + p2) / 2 }
Insert cell
function oppositeAvg(p1, p2) {
return (simpleAvg(p1, p2) + 180) % 360 }
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
nCircStyle = html`<style>
.n-circ-explainer #unit-circle-points {
opacity: ${phase > 1 ? 1 : 0} !important;
}
.n-circ-explainer #unit-circle-points-anim {
opacity: ${phase > 1 ? 1 : 0} !important;
}
.n-circ-explainer #unit-circle-points-anim > circle {
${phase >= 3 ? `
cx: ${realifyCoords(avgPoint)[0]}px;
cy: ${realifyCoords(avgPoint)[1] + 12}px;
fill: white;
stroke-width: 4px;
` : ""}
stroke: black;
transition-duration: 0.5s;
transition-property: cx, cy, fill, stroke-width;
}
.n-circ-explainer #unit-circle-avg-line {
opacity: ${phase > 3 ? 1 : 0} !important;
}
.n-circ-explainer .unit-circle-phase {
transition: 0.333s opacity;
}
/* only for blog post */
#explainer-area li#explainer-phase-2 { color: ${phase >= 2 ? "black" : "inherit"} }
#explainer-area li#explainer-phase-3 { color: ${phase >= 3 ? "black" : "inherit"} }
#explainer-area li#explainer-phase-4 { color: ${phase >= 4 ? "black" : "inherit"} }
</style>`
Insert cell
nCircExplainer = {
const id = Math.random().toString(36).split(".")[1];
return html`
<svg width="310" height="300" viewBox="-10 -10 320 320" class="n-circ-explainer${injectAnimation.length ? "-autoanimated" : ""}">
${genUnitCircle(id)}
<g id="unit-circle-avg-line" class="unit-circle-phase" style="opacity: 0">${unitCircleAngle(avgPoint)}</g>
<g id="unit-circle-points" class="unit-circle-phase" style="opacity: 0">${POINTS.map(p => unitCirclePoint(p))}</g>
<g id="unit-circle-points-anim" class="unit-circle-phase" style="opacity: 0">${POINTS.map(p => unitCirclePoint(p))}</g>
<style>
${injectAnimation.length ? svg`
<style>
@keyframes phase2 {
from, ${(((0.25 * ANIM_LENGTH) - 0.333) / ANIM_LENGTH_WITH_GAP) * 100}% {
opacity: 0;
}
${((0.25 * ANIM_LENGTH) / ANIM_LENGTH_WITH_GAP) * 100}%, to {
opacity: 1;
}
}
@keyframes phase4 {
from, ${(((0.75 * ANIM_LENGTH) - 0.333) / ANIM_LENGTH_WITH_GAP) * 100}% {
opacity: 0;
}
${((0.75 * ANIM_LENGTH) / ANIM_LENGTH_WITH_GAP) * 100}%, to {
opacity: 1;
}
}
#unit-circle-points, #unit-circle-points-anim {
animation-name: phase2;
}
#unit-circle-avg-line {
animation-name: phase4;
}
.unit-circle-phase {
animation-duration: ${ANIM_LENGTH_WITH_GAP}s;
animation-iteration-count: infinite;
}
</style>
` : ""}
</svg>
`;
}
Insert cell
Insert cell
Insert cell
viewof injectAnimation = Inputs.checkbox(["Inject animation CSS"])
Insert cell
Insert cell
ANIM_LENGTH_WITH_GAP = 11
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function realifyCoords(coords) {
const width = (300 / 2);
return coords.map(v => v * width + width);
}
Insert cell
function unitCirclePoint(coords, calcCoords = true, color = "black") {
if (calcCoords) {
const rad = deg2rad(coords - 90);
coords = [Math.cos(rad), Math.sin(rad)]
}
coords = realifyCoords(coords);
return svg`
<circle cx=${coords[0]} cy=${coords[1]} r="12" fill="${color}" />
`;
}
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more