makeVertexShader = function(props) {
let vertexShader = `
precision mediump float;
// variable to send to the fragment shader
// since the fragment shader does not have access to attributes
varying vec4 fragColor;
varying float pointRadius;
attribute vec4 startColor;
attribute vec2 positionStart;
attribute float startR;
attribute float index;
// These are all optional and should be added conditionally
${props.endColorField ? 'attribute vec4 endColor;' : ''}
${props.endPosField ? 'attribute vec2 positionEnd;' : ''}
${props.endR ? 'attribute float endR;' : ''}
uniform float stageWidth;
uniform float stageHeight;
uniform float pixelRatio;
uniform float delayByIndex;
uniform float duration;
uniform float elapsed;
// Stolen from Peter Beshai's great blog post:
// http://peterbeshai.com/beautifully-animate-points-with-webgl-and-regl.html
// helper function to transform from pixel space to normalized device coordinates (NDC)
// in NDC (0,0) is the middle, (-1, 1) is the top left and (1, -1) is the bottom right.
vec2 normalizeCoords(vec2 position) {
// read positions into x and y vars
float x = position[0];
float y = position[1];
return vec2(
2.0 * ((x / stageWidth) - 0.5),
// invert y since 1 is at the top of normalized coordinates
// and [0,0] is in the center
-(2.0 * ((y / stageHeight) - 0.5))
);
}
// Helper function to handle cubic easing
// There are also premade easing functions available via glslify
float easeCubicInOut(float t) {
t *= 2.0;
t = (t <= 1.0 ? t * t * t : (t -= 2.0) * t * t + 2.0) / 2.0;
if (t > 1.0) {
t = 1.0;
}
return t;
}
void main () {
float delay = delayByIndex * index;
float t;
float pointWidth;
// if there is no duration show end state immediately
if (duration == 0.0) {
t = 1.0;
// still in the delay interval before animating
} else if (elapsed < delay) {
t = 0.0;
} else {
t = easeCubicInOut((elapsed - delay) / duration);
}
pointWidth = ${props.endR ? 'mix(startR, endR, t)' : 'startR'};
fragColor = ${props.endColorField ? 'mix(startColor, endColor, t)' : 'startColor'};
pointRadius = pointWidth;
// Slightly less than total radius to add a bit of padding
gl_PointSize = pointWidth * 1.95;
// interpolating position
vec2 position = ${props.endPosField ? 'mix(positionStart, positionEnd, t)' : 'positionStart' };
// scale to normalized device coordinates
gl_Position = vec4(normalizeCoords(position), 0.0, 1.0);
}
`;
return vertexShader;
}