Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<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

Dismiss x
public
Bin info
anonymouspro
0viewers