Skip to content

Instantly share code, notes, and snippets.

@cmgiven
Created August 13, 2016 14:41
Show Gist options
  • Save cmgiven/a325f14550a65dc8ff6898ef0f9feeb4 to your computer and use it in GitHub Desktop.
Save cmgiven/a325f14550a65dc8ff6898ef0f9feeb4 to your computer and use it in GitHub Desktop.
Bouncing Logo
license: mit

Block-a-Day #7. The D3 logo bounces around the screen in a frictionless environment

What I Learned: My first foray into defining a custom force with the v4 API. I'm planning to try adding another custom force into the mix tomorrow, so we'll see how well this holds up.

What I'd Do With More Time: dragged() should have a timeout to zero out the node's velocity when the mouse remains stationary for a certain period of time.

Block-a-Day

Just what it sounds like. For fifteen days, I will make a D3.js v4 block every single day. Rules:

  1. Ideas over implementation. Do something novel, don't sweat the details.
  2. No more than two hours can be spent on coding (give or take).
  3. Every. Single. Day.

Previously

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<svg>
<g id="logo">
<clipPath id="clip">
<path d="M0,0h7.75a45.5,45.5 0 1 1 0,91h-7.75v-20h7.75a25.5,25.5 0 1 0 0,-51h-7.75zm36.2510,0h32a27.75,27.75 0 0 1 21.331,45.5a27.75,27.75 0 0 1 -21.331,45.5h-32a53.6895,53.6895 0 0 0 18.7464,-20h13.2526a7.75,7.75 0 1 0 0,-15.5h-7.75a53.6895,53.6895 0 0 0 0,-20h7.75a7.75,7.75 0 1 0 0,-15.5h-13.2526a53.6895,53.6895 0 0 0 -18.7464,-20z"/>
</clipPath>
<linearGradient id="gradient-1" gradientUnits="userSpaceOnUse" x1="7" y1="64" x2="50" y2="107">
<stop offset="0" stop-color="#f9a03c"/>
<stop offset="1" stop-color="#f7974e"/>
</linearGradient>
<linearGradient id="gradient-2" gradientUnits="userSpaceOnUse" x1="2" y1="-2" x2="87" y2="84">
<stop offset="0" stop-color="#f26d58"/>
<stop offset="1" stop-color="#f9a03c"/>
</linearGradient>
<linearGradient id="gradient-3" gradientUnits="userSpaceOnUse" x1="45" y1="-10" x2="108" y2="53">
<stop offset="0" stop-color="#b84e51"/>
<stop offset="1" stop-color="#f68e48"/>
</linearGradient>
<rect x="-22" y="-22" width="140" height="135" fill="#fff"/>
<g clip-path="url(#clip)">
<path d="M-100,-102m-28,0v300h300z" fill="url(#gradient-1)"/>
<path d="M-100,-102m28,0h300v300z" fill="url(#gradient-3)"/>
<path d="M-100,-102l300,300" fill="none" stroke="url(#gradient-2)" stroke-width="40"/>
</g>
</g>
</svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var width = 960
var height = 500
var logoWidth = 96
var logoHeight = 91
var velocity = 4
var angle = Math.random() * 360
var maxVelocity = 8
var node = {
x: width / 2 - logoWidth / 2,
y: height / 2 - logoHeight / 2,
vx: velocity * Math.cos(angle * Math.PI / 180),
vy: velocity * Math.sin(angle * Math.PI / 180),
width: logoWidth,
height: logoHeight
}
var drag = d3.drag()
.on('start', dragStarted)
.on('drag', dragged)
.on('end', dragEnded)
var logo = d3.select('svg')
.attr('width', width)
.attr('height', height)
.select('#logo')
.datum(node)
.call(drag)
var boxForce = boundedBox()
.bounds([[0, 0], [width, height]])
.size(function (d) { return [d.width, d.height] })
d3.forceSimulation()
.velocityDecay(0)
.alphaTarget(1)
.on('tick', ticked)
.force('box', boxForce)
.nodes([node])
function boundedBox() {
var nodes
var bounds
var size
var sizes
function force() {
var node
var size
var i = -1
while (++i < nodes.length) {
node = nodes[i]
size = sizes[i]
if (node.x + node.vx < bounds[0][0] || node.x + node.vx + size[0] > bounds[1][0]) {
node.x += node.vx
node.vx = -node.vx
}
if (node.y + node.vy < bounds[0][1] || node.y + node.vy + size[1] > bounds[1][1]) {
node.y += node.vy
node.vy = -node.vy
}
}
}
force.initialize = function (_) {
sizes = (nodes = _).map(size)
}
force.bounds = function (_) {
return (arguments.length ? (bounds = _, force) : bounds)
}
force.size = function (_) {
return (arguments.length
? (size = typeof _ === 'function' ? _ : constant(_), force)
: size)
}
return force
}
function ticked() {
logo.attr('transform', function (d) { return 'translate(' + d.x + ',' + d.y + ')' })
}
var px, py, vx, vy, offsetX, offsetY
function dragStarted(d) {
vx = 0
vy = 0
offsetX = (px = d3.event.x) - (d.fx = d.x)
offsetY = (py = d3.event.y) - (d.fy = d.y)
}
function dragged(d) {
vx = d3.event.x - px
vy = d3.event.y - py
d.fx = Math.max(Math.min((px = d3.event.x) - offsetX, width - d.width), 0)
d.fy = Math.max(Math.min((py = d3.event.y) - offsetY, height - d.height), 0)
}
function dragEnded(d) {
var vScalingFactor = maxVelocity / Math.max(Math.sqrt(vx * vx + vy * vy), maxVelocity)
d.fx = null
d.fy = null
d.vx = vx * vScalingFactor
d.vy = vy * vScalingFactor
}
function constant(_) {
return function () { return _ }
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment