Skip to content

Instantly share code, notes, and snippets.

@bwswedberg
Last active March 25, 2019 21:13
Show Gist options
  • Save bwswedberg/00f8a9767750b7183c048ea5a4fb9525 to your computer and use it in GitHub Desktop.
Save bwswedberg/00f8a9767750b7183c048ea5a4fb9525 to your computer and use it in GitHub Desktop.
Sine Water Effect

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment