<html>
<head>
<meta name="description" content="Trigonometry with D3.js">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 Playground</title>
</head>
<body>
<svg id="svg-container" width="800" height="450">
<!-- large base circle -->
<circle id="c1" cx="110" cy="110" r="100" />
<!-- marker circle on circumference -->
<circle id="c2" cx="110" cy="110" r="4" />
<!-- faint x and y axis which intersect origin -->
<line id="ax" class="axis" x1="10" x2="300" y1="110" y2="110" />
<line id="ay" class="axis" x1="110" x2="110" y1="10" y2="300" />
<!-- line from center of circle -->
<line id="l0" />
<!-- sin and cos axis -->
<line id="l1" />
<line id="l2" />
<!-- dashed connectors to outer axis -->
<line id="l3" class="dashed" />
<line id="l4" class="dashed" />
<!-- amgle, cos, sin labels-->
<text id="t1" x="110" y="120" />
<text id="t2" x="370" y="120" />
<text id="t3" x="110" y="330" />
<!-- angle segment -->
<path id="p1" />
<!-- highlighted angle's arc -->
<path id="p2" class="arc" />
</svg>
<p>This is a D3 playground, from the article <a href="http://andyshora.com/tweening-shapes-paths-d3-js.html" target="_blank">Tweening Custom Shapes and Paths in D3.js</a>.</p>
</body>
</html>
circle {
stroke: black;
stroke-width: 1;
fill: none;
}
path {
fill: powderblue;
fill-opacity: 0.4;
}
line {
stroke: red;
stroke-width: 1;
}
.dashed {
stroke-dasharray: 2, 2;
stroke: blue;
}
.axis {
stroke: red;
stroke-opacity: 0.2;
fill: none;
}
p {
font-size: 12px;
}
text {
font-family: "Helvetica Neue", Helvetica, Arial;
font-size: 15px;
text-anchor: middle;
}
.arc {
fill: none;
stroke: blue;
stroke-width: 2;
}
var c1, c2, l0, l1, l2, l3, l4, p1, p2;
let origin = { x: 110, y: 110 };
var lineX = 300;
var lineY = 300;
window.onload = onLoad();
var lastPos;
function getRadialPosition(value, maxValue, radius, origin) {
let degrees = (value / maxValue) * 360;
let theta = degrees * (Math.PI / 180);
let pos = {
x: origin.x + (Math.sin(theta) * radius),
y: origin.y - (Math.cos(theta) * radius),
sin: Math.sin(theta),
cos: Math.cos(theta),
angle: degrees
};
lastPos = pos;
return pos;
}
function updateLines() {
l3.attr('x1', lastPos.x);
l3.attr('x2', 300);
l3.attr('y1', lastPos.y);
l3.attr('y2', lastPos.y);
l4.attr('x1', lastPos.x);
l4.attr('x2', lastPos.x);
l4.attr('y1', lastPos.y);
l4.attr('y2', 300);
l0.attr('x2', lastPos.x);
l0.attr('y2', lastPos.y);
t1.text(lastPos.angle.toFixed(2) + '°');
t2.text('cos(θ) = ' + lastPos.cos.toFixed(2));
t3.text('sin(θ) = ' + lastPos.sin.toFixed(2));
p1.attr('d', generateSVGSegment(origin.x, origin.y, 100, 0, lastPos.angle));
p2.attr('d', generateSVGArc(origin.x, origin.y, 100, 0, lastPos.angle));
}
function d2r(degs) {
return degs * (Math.PI / 180);
}
function generateSVGSegment(x, y, r, startAngle, endAngle) {
// convert angles to Radians
startAngle *= (Math.PI / 180);
endAngle *= (Math.PI / 180);
var largeArc = endAngle - startAngle <= Math.PI ? 0 : 1; // 1 if angle > 180 degrees
var sweepFlag = 1; // is arc to be drawn in +ve direction?
return ['M', x, y, 'L', x + Math.sin(startAngle) * r, y - (Math.cos(startAngle) * r),
'A', r, r, 0, largeArc, sweepFlag, x + Math.sin(endAngle) * r, y - (Math.cos(endAngle) * r), 'Z'
].join(' ');
}
function generateSVGArc(x, y, r, startAngle, endAngle) {
// convert angles to Radians
startAngle = d2r(startAngle);
endAngle = d2r(endAngle);
if (startAngle > endAngle) {
var s = startAngle;
startAngle = endAngle;
endAngle = s;
}
// if arc is > 180 degrees, we must set a large arc flag
// so the path isnt drawn via shortest path
var largeArcFlag = endAngle - startAngle <= Math.PI ? 0 : 1;
// path command, start by moving cursor
var arr = ['M', x + Math.sin(startAngle) * r, y - (Math.cos(startAngle) * r)];
var rx = r;
var ry = r;
var sweepFlag = 1;
// arc command
arr = arr.concat(['A', rx, ry, 0, +largeArcFlag, sweepFlag]);
// arc end position
arr = arr.concat([x + Math.sin(endAngle) * r, y - (Math.cos(endAngle) * r)]);
return arr.join(' ');
}
function tweenPoint() {
c2.transition()
.duration(4000)
.ease('linear')
.attrTween('cx', () => {
return t => {
var pos = getRadialPosition(t, 1, 100, origin);
return pos.x;
};
})
.attrTween('cy', () => {
return t => {
var pos = getRadialPosition(t, 1, 100, origin);
updateLines();
return pos.y;
};
})
.each('end', tweenPoint);
}
function onLoad() {
var svg = d3.select('#svg-container');
t1 = svg.select('#t1');
t2 = svg.select('#t2');
t3 = svg.select('#t3');
p1 = svg.select('#p1');
p2 = svg.select('#p2');
c1 = svg.select('#c1');
c2 = svg.select('#c2');
l0 = svg.select('#l0');
l1 = svg.select('#l1');
l2 = svg.select('#l2');
l3 = svg.select('#l3');
l4 = svg.select('#l4');
l0.attr('x1', 110);
l0.attr('y1', 110);
l1.attr('x1', lineX);
l1.attr('x2', lineX);
l1.attr('y1', 10);
l1.attr('y2', 210);
l2.attr('y1', lineY);
l2.attr('y2', lineY);
l2.attr('x1', 10);
l2.attr('x2', 210);
tweenPoint();
}
Output
300px
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |