Skip to content

Instantly share code, notes, and snippets.

@steveharoz
Last active January 13, 2022 23:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steveharoz/14995297063c0bca267d262efd3f7bd4 to your computer and use it in GitHub Desktop.
Save steveharoz/14995297063c0bca267d262efd3f7bd4 to your computer and use it in GitHub Desktop.
animacy
height: 600
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body { background-color: black; color: white; font-family: sans-serif; }
span, div { margin: 1em; float: left; }
input { vertical-align: bottom; }
svg { float: left; border: white 3px solid; border-radius: 4px; cursor: none; margin-top: 1em; }
#cursor { fill: #293; }
</style>
<body>
<div>
<span>Show where they look: <input type="checkbox" id="showLook" checked></span> <br>
<span>Perpendicular offset: <input type="range" id="offsetSlider" value="50" min="0" max="100"></span> <br>
<span>Absolute offset: <input type="range" id="absoluteOffsetSlider" value="0" min="0" max="100"></span> <br>
<span>Look perpendicular: <input type="checkbox" id="lookPerpendicular"></span> <br>
</div>
<svg></svg>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script>
var margin = 15;
var width = 600 - margin*2,
height = 500 - margin*2;
var dartSpeed = 2;
var perpendicularOffset = 50;
var angleOffset = 0;
var mouseLocation = [0,0];
class Dart {
constructor(index) {
this.x = Math.random() * width;
this.y = Math.random() * height;
this.velocityAngle = Math.random() * 360;
this.velocityX = dartSpeed * Math.cos(this.velocityAngle);
this.velocityY = dartSpeed * Math.sin(this.velocityAngle);
this.lookAngle = 0; // the angle where this dart is looking
this.lookX = 0; // the position where this dart is looking
this.lookY = 0; // the position where this dart is looking
this.lookSign = index % 2 ? 1 : -1; // looking to the right or left
}
updateMove() {
this.x += this.velocityX;
this.y += this.velocityY;
if (this.x < 0 || this.x > width) { this.velocityX *= -1; }
if (this.x < 0) { this.x *= -1; }
if (this.x > width) { this.x = 2*width - this.x; }
if (this.y < 0 || this.y > height) { this.velocityY *= -1; }
if (this.y < 0) { this.y *= -1; }
if (this.y > height) { this.y = 2*height - this.y; }
this.lookAtMe();
}
lookAtMe() {
var absoluteOffset = +d3.select('#absoluteOffsetSlider').property('value');
var angle = Math.atan2(mouseLocation[1]-absoluteOffset-this.y, mouseLocation[0]-absoluteOffset-this.x);
var perpendicular = angle - Math.PI/2 * this.lookSign;
this.lookX = mouseLocation[0] - absoluteOffset + perpendicularOffset * Math.cos(perpendicular);
this.lookY = mouseLocation[1] - absoluteOffset + perpendicularOffset * Math.sin(perpendicular);
this.lookAngle = Math.atan2(this.lookY-this.y, this.lookX-this.x) * 180/Math.PI + angleOffset;
}
}
var darts = d3.range(12).map(i => new Dart(i));
var svg = d3.select("body").select("svg")
.attr("width", width + margin*2)
.attr("height", height + margin*2)
.append("g")
.attr("transform", 'translate(' + [margin, margin] + ')' )
.attr("width", width)
.attr("height", height);
var dartGlyphs = svg.selectAll('path')
.data(darts)
.enter()
.append('path')
.attr("d", " M 10 0 L -10 -10 L -5 0 L -10 10") // arrow shape
.attr("fill", (d,i) => d3.schemeCategory20[i % 20])
.attr("transform", d => 'translate(' + [d.x, d.y] + ')');
var cursor = svg.append('circle')
.attr('id', 'cursor')
.attr("r", 13);
var lookAtGlyphs = svg.selectAll('.lookAt')
.data(darts)
.enter()
.append('circle')
.attr('class', 'lookAt')
.attr("r", 3)
.attr("fill", (d,i) => d3.schemeCategory20[i % 20])
.attr("transform", d => 'translate(' + [d.lookX, d.lookY] + ')');
var lookAtMean = svg.append('path').attr('class', 'lookAtMean')
.attr("fill", 'white')
.attr("d", d3.symbol().size(10).type(d3.symbolStar)());
d3.select(window).on("mousemove", () => { mouseLocation = d3.mouse(svg.node()); });
d3.timer(function() {
// update settings
angleOffset = d3.select('#lookPerpendicular').property('checked') ? 90 : 0;
perpendicularOffset = +d3.select('#offsetSlider').property('value');
// update darts
for(i=0; i < darts.length; i++)
darts[i].updateMove();
// update glyphs
cursor.attr("transform", 'translate(' + mouseLocation + ')');
dartGlyphs
.attr("fill", (d,i) => d3.select('#showLook').property('checked') ? d3.schemeCategory20[i % 20] : 'white' )
.attr("transform", d =>
'translate(' + [d.x, d.y] + ')' +
'rotate(' + d.lookAngle + ')');
lookAtGlyphs
.attr("opacity", +d3.select('#showLook').property('checked'))
.attr("transform", d => 'translate(' + [d.lookX, d.lookY] + ')');
lookAtMean
.attr("opacity", +d3.select('#showLook').property('checked'))
.attr("transform", 'translate(' + [
darts.map(d => d.lookX).reduce((a, b) => a + b) / darts.length,
darts.map(d => d.lookY).reduce((a, b) => a + b) / darts.length] + ')');
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment