Skip to content

Instantly share code, notes, and snippets.

@notlion
Created November 22, 2012 07: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 notlion/4129760 to your computer and use it in GitHub Desktop.
Save notlion/4129760 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<body>
<style>
html, body {
padding: 0; margin: 0;
}
#tris > path {
stroke: #fff;
stroke-width: 3px;
stroke-linejoin: round;
stroke-linecap: round;
}
#text > path {
fill: #000;
}
</style>
<script src="http://d3js.org/d3.v2.js"></script>
<script>
'use strict'
var colors = [
'#f69d3b',
'#bec0c2',
'#f8e470',
'#434648',
'#71cbe4',
'#9eca62'
]
var letters = [
'M-16.257,3.368c-2.179,0-3.317-1.27-3.317-3.368c0-2.099,1.139-3.368,3.317-3.368c2.18,0,3.318,1.269,3.318,3.368C-12.938,2.099-14.077,3.368-16.257,3.368z M-16.257-2.729c-1.499,0-2.378,0.889-2.378,2.729c0,1.569,0.879,2.729,2.378,2.729c1.5,0,2.379-1.16,2.379-2.729C-13.878-1.839-14.757-2.729-16.257-2.729z',
'M-9.187-0.65h-1.488v2.659c0,0.38,0.069,0.72,0.499,0.72c0.49,0,0.52-0.48,0.52-0.86h0.7c0,0.939-0.36,1.5-1.34,1.5c-1.159,0-1.199-0.79-1.199-1.719V-0.65h-0.89v-0.64h0.89v-1l0.82-0.28v1.279h1.488V-0.65z',
'M-8.413,2.628h0.79v-5.257h-0.79v-0.64h1.609v2.809h0.021c0.22-0.58,0.85-0.929,1.528-0.929c1.14,0,1.619,0.569,1.619,1.659v2.359h0.79v0.64h-2.398v-0.64h0.789V0.439c0-0.729-0.25-1.189-1.049-1.189c-0.88,0-1.3,0.62-1.3,1.429v1.949h0.79v0.64h-2.399V2.628z',
'M-1.783,1.219c0,1.049,0.59,1.509,1.659,1.509c0.52,0,1.239-0.29,1.289-0.88h0.76c-0.03,1.12-1.199,1.52-2.159,1.52c-1.479,0-2.429-0.89-2.429-2.379s0.95-2.378,2.429-2.378c1.619,0,2.189,1.149,2.199,2.608H-1.783z M1.146,0.58C1.096-0.25,0.596-0.75-0.294-0.75c-0.789,0-1.489,0.49-1.489,1.329H1.146z',
'M2.519,2.628h0.789V-0.65H2.519v-0.64h1.609v0.93h0.02c0.14-0.69,0.71-1.029,1.399-1.029c0.149,0,0.26,0.01,0.37,0.02V-0.7c-0.13-0.02-0.3-0.05-0.49-0.05c-1.149,0-1.299,1.209-1.299,2.139v1.239h0.939v0.64H2.519V2.628z',
'M5.983,2.628h0.789v-5.257H5.983v-0.64h1.609v5.897h0.789v0.64H5.983V2.628z',
'M12.165,2.329h-0.02c-0.2,0.72-0.8,1.04-1.529,1.04c-0.91,0-1.789-0.39-1.789-1.409c0-1.39,1.359-1.469,2.459-1.469h0.879V0.22c0-0.729-0.38-0.969-1.1-0.969c-0.619,0-1.129,0.13-1.189,0.779H8.997c0.06-1.049,0.979-1.419,1.959-1.419c1.079,0,2.028,0.229,2.028,1.409v2.608h0.8v0.64h-1.619V2.329z M10.716,2.729c1.01,0,1.449-0.73,1.449-1.599h-0.689c-0.59,0-1.77,0-1.77,0.83C9.706,2.559,10.227,2.729,10.716,2.729z',
'M14.107,2.628h0.8v-5.257h-0.8v-0.64h1.619v2.809h0.021c0.26-0.59,0.959-0.929,1.579-0.929c1.409,0,2.248,0.919,2.248,2.378c0,1.429-0.799,2.379-2.248,2.379c-0.64,0-1.359-0.36-1.579-1.03h-0.021v0.93h-1.619V2.628z M17.326,2.729c0.869,0,1.369-0.71,1.369-1.739c0-0.919-0.48-1.739-1.369-1.739c-1.109,0-1.6,0.819-1.6,1.739C15.727,2.019,16.236,2.729,17.326,2.729z'
]
var w, h, svg, root, text_root, tris_root, force, vertices
function init() {
svg = d3.select('body').append('svg')
root = svg.append('g')
tris_root = root.append('g')
.attr('id', 'tris')
.attr('transform', 'translate(0,-40)')
text_root = root.append('g')
.attr('id', 'text')
.attr('transform', 'translate(0,125)')
size()
var rndSmall = d3.random.normal(0, 50)
var rndBig = d3.random.normal(0, Math.max(w, h) / 2)
vertices = d3.range(17).map(function (d) {
return { x: rndBig(), y: rndBig() }
})
force = d3.layout.force()
.charge(-300)
.gravity(0.25)
.size([ 0, 0 ])
.on('tick', stepTris)
.nodes(vertices)
.start()
function makeTransform(elem, x, y, r) {
return 'translate(' + x + ',' + y + ')'
+ 'scale(8)'
+ 'rotate(' + r + ',' + elem.getAttribute('data-center') + ')'
}
var text = text_root.selectAll('path')
.data(letters)
text.enter().append('path')
.attr('d', function(d) { return d })
.attr('data-center', function(d) {
var bb = this.getBBox()
return (bb.x + bb.width / 2) + ',' + (bb.y + bb.height / 2)
})
.attr('transform', function() {
return makeTransform(this, rndBig(), rndBig(), Math.random() * 360 - 180)
})
.style('fill-opacity', 0)
.transition()
.duration(1000)
.ease('cubic-out')
.delay(function(d, i) {
return 1000 + i * 150
})
.attr('transform', function() {
return makeTransform(this, 0, 0, 0)
})
.style('fill-opacity', 1)
text.on('mouseover', function() {
d3.select(this).transition()
.duration(200)
.attr('transform', makeTransform(this, rndSmall(), rndSmall(), Math.random() * 360 - 180))
.style('fill', rndColor())
.transition()
.delay(1000)
.duration(400)
.attr('transform', makeTransform(this, 0, 0, 0))
.style('fill', '#000')
})
var prev_mouse;
tris_root.on('mousemove', function() {
var m = d3.mouse(tris_root.node())
if(prev_mouse) {
var alpha = Math.min(0.1, force.alpha() + 0.01)
force.alpha(alpha)
var vx = m[0] - prev_mouse[0]
var vy = m[1] - prev_mouse[1]
force.nodes().forEach(function(node) {
var d = 100
var power = alpha * alpha * 30 * Math.max(d - dist(m[0], m[1], node.x, node.y), 0) / d
node.x += vx * power
node.y += vy * power
})
}
prev_mouse = [ m[0], m[1] ]
})
}
function rndColor() {
return colors[Math.floor(Math.random() * colors.length)]
}
function size() {
w = window.innerWidth
h = window.innerHeight
svg.attr('width', w).attr('height', h)
root.attr('transform', 'translate(' + (w / 2) + ', ' + (h / 2) + ')')
}
function stepTris() {
var delaunay_tris = d3.geom.delaunay(vertices.map(function(v) {
return [ v.x, v.y ]
}))
var triangles = tris_root.selectAll('path').data(delaunay_tris)
triangles.enter().append('path').attr('data-fill', rndColor)
triangles.exit().remove()
var hole = searchArray(delaunay_tris, function(d) {
var x = 0, y = 0
for(var i = 3; --i >= 0;) {
x += d[i][0]
y += d[i][1]
}
return length(x, y) / 3
})
triangles
.attr('d', function(d) {
return 'M' + d.join('L') + 'Z'
})
.style('fill', function(d) {
// If the center of the triangle is near the origin, simulate a hole.
return d === hole ? 'rgba(255,255,255,0)'
: this.getAttribute('data-fill')
})
var in_radius = 40
, out_radius = 110
vertices.forEach(function(v, j) {
var len, prev, next, margin = 0.001
for(var i = 0; i < 3; i++) {
if(dist(v.x, v.y, hole[i][0], hole[i][1]) < margin) {
len = regularPolygonRadiusToSideLength(in_radius, 3)
spring(0, 0, v, in_radius, 0.01)
prev = hole[i == 0 ? 2 : i - 1]
next = hole[i == 2 ? 0 : i + 1]
spring(prev[0], prev[1], v, len, 0.01)
spring(next[0], next[1], v, len, 0.01)
return;
}
}
if(length(v.x, v.y) > out_radius) {
spring(0, 0, v, out_radius, 0.01)
}
})
}
function vec2Eq(v1, v2) {
return v1.x === v2.x && v1.y === v2.y
}
function regularPolygonRadiusToSideLength(r, n) {
return 2.0 * r * Math.sin(Math.PI / n)
}
function spring(x, y, v, len, power) {
var ox = x - v.x, oy = y - v.y
var m = length(ox, oy) + 0.00001
var e = power * (m - len)
v.x += (ox / m) * e
v.y += (oy / m) * e
}
function searchArray(arr, ranker) {
var r, rank = Number.MAX_VALUE, index = 0;
for(var i = arr.length; --i >= 0;) {
r = ranker.call(arr, arr[i])
if(r < rank) {
rank = r
index = i
}
}
return arr[index]
}
function length(x, y) {
return Math.sqrt(x * x + y * y)
}
function dist(x1, y1, x2, y2) {
return length(x2 - x1, y2 - y1)
}
window.addEventListener('load', init)
window.addEventListener('resize', size)
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment