|
|
|
(function () { |
|
d3.select('#svg').on('click', function () { |
|
draw('svg'); |
|
}); |
|
d3.select('#canvas').on('click', function () { |
|
draw('canvas'); |
|
}); |
|
if (d3.resolution() > 1) { |
|
d3.select('#paper').append('label').html( |
|
"<input id='canvas-low' name='type' type='radio'><span>canvas low resolution</span>" |
|
); |
|
d3.select('#canvas-low').on('click', function () { |
|
draw('canvas', 1); |
|
}); |
|
} |
|
|
|
var color = d3.scaleSequential(d3.interpolateBlues), |
|
waves, particles, x, y, r, data, boat; |
|
|
|
draw('svg'); |
|
d3.timer(animate); |
|
|
|
|
|
function draw(type, r) { |
|
|
|
var example = d3.select("#example"), |
|
width = d3.getSize(example.style('width')), |
|
height = Math.min(500, width); |
|
x = d3.scaleLinear().range([0, width]); |
|
y = d3.scaleLinear().range([height, 0]); |
|
|
|
if (!data) |
|
data = [0.7, 0.6, 0.4, 0.2].map(function (d, i) { |
|
var w = wave() |
|
.radius(0.02*(i+1)*height) |
|
.waveLength(0.2*(i+1)) |
|
.y(d); |
|
w.area.x(function (dd) { |
|
return x(dd.x) + dd.dx; |
|
}).y1(function (dd) { |
|
return y(dd.y) - dd.dy; |
|
}).y0(function () { |
|
return y(0); |
|
}); |
|
return w; |
|
}); |
|
|
|
example.select('.paper').remove(); |
|
|
|
var paper = example |
|
.append(type) |
|
.classed('paper', true) |
|
.attr('width', width).attr('height', height).canvasResolution(r).canvas(true) |
|
.style('stroke-width', 0.5); |
|
|
|
sun(paper) |
|
.append('rect') |
|
.attr('width', width) |
|
.attr('height', height) |
|
.style('fill', 'url(#sun)'); |
|
|
|
waves = paper |
|
.append('g') |
|
.classed('waves', true) |
|
.selectAll('path') |
|
.data(data) |
|
.enter() |
|
.append('path') |
|
.style('stroke', 'none') |
|
.each(function (d) { |
|
d3.select(this).attr('d', d.context(null)).style('fill', color(d.y())); |
|
}); |
|
|
|
var circles = paper.selectAll('g.circles') |
|
.data(data) |
|
.enter() |
|
.append('g') |
|
.classed('circles', true) |
|
.selectAll('circles') |
|
.data(function (d) {return d.points();}) |
|
.enter() |
|
.append('circle') |
|
.attr('r', function (d) {return d.radius;}) |
|
.style('fill', 'none') |
|
.style('stroke', '#666') |
|
.attr('cx', function (d) {return x(d.x);}) |
|
.attr('cy', function (d) {return y(d.y);}); |
|
|
|
particles = paper.selectAll('g.particles') |
|
.data(data) |
|
.enter() |
|
.append('g') |
|
.classed('particles', true) |
|
.selectAll('circles') |
|
.data(function (d) {return d.points();}) |
|
.enter() |
|
.append('circle') |
|
.attr('r', 3) |
|
.style('fill', '#666') |
|
.style('stroke-width', 0) |
|
.attr('cx', function (d) {return x(d.x) + d.dx;}) |
|
.attr('cy', function (d) {return y(d.y) - d.dy;}); |
|
|
|
boat = paper.append('text') |
|
.text("⛵") |
|
.style('text-anchor', 'middle') |
|
.style('alignment-baseline', 'middle') |
|
.style("font-size", "60px"); |
|
|
|
moveBoat(); |
|
} |
|
|
|
function animate () { |
|
waves.each(function (d) { |
|
d3.select(this).attr('d', d.tick()); |
|
}); |
|
particles.data(function (d) {return d.points();}) |
|
.attr('cx', function (d) {return x(d.x) + d.dx;}) |
|
.attr('cy', function (d) {return y(d.y) - d.dy;}); |
|
moveBoat(); |
|
} |
|
|
|
function moveBoat() { |
|
var d = data[1].point(20); |
|
boat.attr("transform", "translate(" + (x(d.x) + d.dx) + ", " + (y(d.y) - d.dy) + ")"); |
|
} |
|
|
|
function sun (paper) { |
|
paper |
|
.append('defs') |
|
.append('radialGradient') |
|
.attr('id', 'sun') |
|
.attr('cx', '70%') |
|
.attr('cy', '30%') |
|
.attr('fx', '60%') |
|
.attr('fy', '30%') |
|
.selectAll('stop') |
|
.data([ |
|
{color: "#e31a1c", offset: '0%'}, |
|
{color: '#fd8d3c', offset: '60%'} |
|
]) |
|
.enter() |
|
.append('stop') |
|
.attr('offset', function (d) { |
|
return d.offset; |
|
}) |
|
.attr('stop-color', function (d) { |
|
return d.color; |
|
}); |
|
return paper; |
|
} |
|
|
|
function wave() { |
|
var radius = 0.1, // intensity of wave |
|
waveLength = 1, // wave length |
|
y = 0, |
|
area = d3.area().curve(d3.curveNatural), |
|
extent = [0, 1], |
|
pi = Math.PI, |
|
cos = Math.cos, |
|
sin = Math.sin, |
|
N = 8, |
|
speed = 0.01, |
|
time = 0; |
|
|
|
function wave (d) { |
|
return area(wave.points(d)); |
|
} |
|
|
|
wave.area = area; |
|
|
|
wave.tick = function () { |
|
time += 1; |
|
return wave; |
|
}; |
|
|
|
wave.context = function (_) { |
|
if (!arguments.length) return area.context(); |
|
area.context(_); |
|
return wave; |
|
}; |
|
|
|
wave.extent = function (_) { |
|
if (!arguments.length) return extent; |
|
extent = _; |
|
return wave; |
|
}; |
|
|
|
wave.N = function (_) { |
|
if (!arguments.length) return N; |
|
N = +_; |
|
return wave; |
|
}; |
|
|
|
wave.waveLength = function (_) { |
|
if (!arguments.length) return waveLength; |
|
waveLength = _; |
|
return wave; |
|
}; |
|
|
|
wave.y = function (_) { |
|
if (!arguments.length) return y; |
|
y = _; |
|
return wave; |
|
}; |
|
|
|
wave.radius = function (_) { |
|
if (!arguments.length) return radius; |
|
radius = _; |
|
return wave; |
|
}; |
|
|
|
wave.speed = function (_) { |
|
if (!arguments.length) return speed; |
|
speed = _; |
|
return wave; |
|
}; |
|
|
|
wave.points = function () { |
|
var w = extent[1] - extent[0] + 2*waveLength, |
|
dx = waveLength/N, |
|
Nx = Math.round(w/dx) + 1; |
|
|
|
return d3.range(Nx).map(point); |
|
}; |
|
|
|
wave.point = point; |
|
|
|
function point (i) { |
|
var da = 2*pi/N, |
|
dx = waveLength/N, |
|
x0 = extent[0] - waveLength, |
|
a = i*da - time*speed*pi; |
|
|
|
return { |
|
x: x0 + i * dx, |
|
y: y, |
|
angle: a, |
|
radius: radius, |
|
dx: radius * cos(a), |
|
dy: radius * sin(a) |
|
}; |
|
} |
|
|
|
return wave; |
|
} |
|
|
|
}()); |