Experimental demo of a water-like effect using sine waves. This demo uses a tiny d3 plugin d3-sine-wave
to help manage sine wave related state.
Related:
screenshots |
Experimental demo of a water-like effect using sine waves. This demo uses a tiny d3 plugin d3-sine-wave
to help manage sine wave related state.
Related:
(function(d3) { | |
d3.sineWave = function() { | |
var amplitude = 1; | |
var wavelength = 2; | |
var phase = 2 | |
var samplesCount = 20; | |
var xTransform = function(d) { return d; }; | |
var yTransform = function(d) { return d; }; | |
function sineWave() { } | |
sineWave.amplitude = function(_) { | |
if (!arguments.length) return amplitude; | |
amplitude = _; | |
return sineWave; | |
} | |
sineWave.wavelength = function(_) { | |
if (!arguments.length) return wavelength; | |
wavelength = _; | |
return sineWave; | |
} | |
sineWave.phase = function(_) { | |
if (!arguments.length) return phase; | |
phase = _; | |
return sineWave; | |
} | |
sineWave.samplesCount = function(_) { | |
if (!arguments.length) return samplesCount; | |
samplesCount = _; | |
return sineWave; | |
} | |
/** | |
* Transforms x value post calculation. Transformation function is passed a x value that ranges between 0 and 1 inclusively. | |
* @param {function} _ Transforming function | |
*/ | |
sineWave.xTransform = function(_) { | |
if (!arguments.length) return xTransform; | |
xTransform = _; | |
return sineWave; | |
} | |
/** | |
* Transforms y value post calculation. Transformation function reflects sine parameters. If amplitude is 1 values will range from -1 to 1. | |
* @param {function} _ Transforming function | |
*/ | |
sineWave.yTransform = function(_) { | |
if (!arguments.length) return yTransform; | |
yTransform = _; | |
return sineWave; | |
} | |
sineWave.samples = function() { | |
return d3.range(samplesCount).map(function(d) { | |
var t = d / (samplesCount - 1); | |
var y = Math.sin(2 * Math.PI * t * wavelength + phase) * amplitude; | |
return [xTransform(t), yTransform(y)]; | |
}); | |
} | |
return sineWave; | |
} | |
}(window.d3 )); |
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
svg { | |
display: block; | |
width: 100%; | |
} | |
</style> | |
<svg viewBox="0 0 960 500"></svg> | |
<script src="https://d3js.org/d3.v5.min.js"></script> | |
<script src="./d3-sine-wave.js"></script> | |
<script> | |
var svg = d3.select('svg'); | |
var viewBox = svg.attr('viewBox').split(' '); | |
var width = +viewBox[2]; | |
var height = +viewBox[3]; | |
var value = 0.5; | |
var valueScale = d3.scaleLinear().domain([0, 1]).range([0, height]); | |
var waves = [ | |
d3.sineWave() | |
.amplitude(10) | |
.wavelength(2) | |
.phase(0) | |
.xTransform(function(d) { return d * width; }) | |
.yTransform(function(d) { return d + valueScale(value) - 30; }), | |
d3.sineWave() | |
.amplitude(10) | |
.wavelength(1.5) | |
.phase(3) | |
.xTransform(function(d) { return d * width; }) | |
.yTransform(function(d) { return d + valueScale(value); }), | |
d3.sineWave() | |
.amplitude(10) | |
.wavelength(1) | |
.phase(6) | |
.xTransform(function(d) { return d * width; }) | |
.yTransform(function(d) { return d + valueScale(value) + 30; }) | |
]; | |
var line = d3.line() | |
.curve(d3.curveBasis); | |
var sine = svg.append('g') | |
.attr('class', 'sine'); | |
var color = d3.scaleOrdinal() | |
.domain([0, 1, 2]) | |
.range([d3.color('steelblue').brighter(), d3.color('steelblue'), d3.color('steelblue').darker()]); | |
d3.timer(tick, 150); | |
function tick(elapsed) { | |
// Sine wave will complete phase every 5 seconds | |
var tweenValue = elapsed % 5000 / 5000; | |
var data = waves.map(function(wave, i) { | |
var reverse = i % 2 ? 1 : -1; | |
// update the phase based on tweenValue | |
wave.phase(2 * Math.PI * tweenValue * reverse); | |
return { | |
fill: color(i), | |
samples: wave.samples() | |
}; | |
}); | |
var path = sine.selectAll('path') | |
.data(data); | |
path.enter().append('path') | |
.style('fill', function(d) { return d.fill; }) | |
.merge(path) | |
.attr('d', function(d) { | |
// extend path area to bottom of viewport so that it can have a fill | |
return line(d.samples) + 'L' + width + ',' + height + 'L0,' + height + 'L' + d.samples[0][0] + ',' + d.samples[0][1]; | |
}); | |
} | |
</script> |