Independent Web and Educational Software Developer
Mouse-over or touch/drag to interact with Winter Scene in Moonlight. Or, view fullscreen.
Winter Scene in Moonlight, painted in 1869, is the first known watercolor landscape painted by Henry Farrer. Farrer was the younger brother of another artist (Thomas Farrer) who specialized in painting landscapes as well, but usually worked in oils. Farrer is one of only a few artists at the time to work in watercolors almost exclusively. "Winter Scene in Moonlight" likely represents a location in Brooklyn.
I've added animation to three components of the painting - the clouds, the moon, and the land. I'll go through them.
Clouds - Those are pretty simple. I add them to the stage behind the foreground and they simply update their x position so they float by. Interaction either slows them down or speeds them up depending on the side.
Moon - The moon follows an elliptical path through the sky. It has an angular velocity that can be effected by interaction. In order to get the moon to follow the elliptical path, I consulted Wikipedia's Ellipse page and then wrote the following code:
// Ellipse parameters
var a = size.width * .8; // semi-major axis
var b = size.height * .65; // semi-minor axis
var cx = size.width; // center x
var cy = 870 * scale; // center y
function updateMoon() {
// Too impatient to let the moon do a full cycle
if (moonAngle > maxAngle) {
moonAngle = minAngle;
}
var interactPhi = 0;
if (trackLoc.x > 0) {
var dist = distance(trackLoc.x, trackLoc.y,
moon.x, moon.y);
if (dist < maxDist) {
interactPhi = (maxDist - dist)/(700 *maxDist)
* Math.sign(moon.x - trackLoc.x);
}
}
moonAngle += moonPhi + interactPhi;
// Parametric equations as per the link above
var u = Math.tan(moonAngle/2);
moon.x = a * (1 - u*u)/(u*u + 1) + cx;
moon.y = 2 * b * u / (u*u + 1) + cy;
}
// Linear interpolation of the tint on the foreground (y = mx + b)
var minimumTint = 88;
var tintM = (255 - minimumTint)/(3.9 - Math.PI);
var tintB = 255 - tintM * 3.9;
function updateTint() {
var targetTint = minimumTint;
if (moonAngle > 3.9) {
targetTint = 255;
} else if (moonAngle >= Math.PI) {
// Use the linear interpolation (y is the target tint and x is the angle of the moon)
targetTint = tintM * moonAngle + tintB;
}
var diff = targetTint - foreground.targetTint;
// Tween the difference between the tint we have and the tint that we want
if ( Math.abs(diff) > 0.5) {
foreground.targetTint += diff/40;
// get a tint and turn it into hexadecimal
var tint = Math.floor(foreground.targetTint).toString(16);
var hex = '0x' + tint + tint + tint;
// The tint is a shade of gray, so it darkens the whole land
foreground.tint = hex;
}
}