📗 Most computer displays are flicker-based: Wikipedia.
📗 The HTML Canvas is erased and redrawn frame by frame.
📗 Drawing could be too slow or too fast.
📗 Double buffering is used: two images (pixel arrays), one used for drawing in the background, one used for display, are swapped so the display only shows finished images and this also helps with frame rate constancy.
📗 The web browser handles double buffering: JavaScript cannot control this process.
📗 window.requestAnimationFrame waits until after a buffer swap: Doc.
📗 Animation is drawing a different image for every frame.
📗 With HTML Canvas, animation is a drawing function of time, so a typical animation function looks like:
let draw = function(t) {
\\ draw frame at time t, measured in milliseconds;
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
📗 Why does the box bounce with the formula \(450 - 250 \left| \sin\left(t\right) \right|\)?
📗 How to make the box bounce faster?
📗 How to make the box move horizontally too? Demo bounce
📗 Note: sin(t) is a simple mathematical trick to make sure the position of the box is bounded by -1 and 1 (and abs(sin(t)) bounds it by 0 and 1), the correct trajectory of a falling or bouncing object is better approximated by a quadratic function: Wikipedia. Try this in Workbook 2: Galleries.
📗 Why does the box move in a circle with the formula \(\begin{bmatrix} 250 + 200 \sin\left(t\right) \\ 250 + 200 \cos\left(t\right) \end{bmatrix}\)?
📗 How to make the box move faster?
📗 How to make the box move in an "8"-shaped path instead? Demo animate_time
📗 Making a sequence of images, each image as a mathematical function of time can be difficult.
📗 Alternatively, a display list (a list of objects, or primitives), including their position, velocity, acceleration etc can be stored and updated every time requestAnimationFrame is called:
let objects = [];
let draw = function() {
\\ update objects and draw objects
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
📗 Why does the box move in a circle with the update \(\begin{bmatrix} x \\ y \end{bmatrix} \to \begin{bmatrix} x + \dfrac{y - 250}{100} \\ y - \dfrac{x - 250}{100} \end{bmatrix}\)? Math Notes
The velocity is the derivative of position as a function of time:
\(\dfrac{d}{dt} \begin{bmatrix} x \\ y \end{bmatrix}\) = \(\dfrac{d}{dt} \begin{bmatrix} 250 + 200 \sin\left(t\right) \\ 250 + 200 \cos\left(t\right) \end{bmatrix}\)
= \(\begin{bmatrix} 200 \cos\left(t\right) \\ -200 \sin\left(t\right) \end{bmatrix}\)
= \(\begin{bmatrix} 200 \dfrac{y - 250}{200} \\ -200 \dfrac{x - 250}{200} \end{bmatrix}\)
= \(\begin{bmatrix} y - 250 \\ - \left(x - 250\right) \end{bmatrix}\)
= \(c \begin{bmatrix} \dfrac{y - 250}{100} \\ \dfrac{-\left(x - 250\right)}{100} \end{bmatrix}\), c = 100.
📗 Is the box really moving in a circle? How to make it better? Demo animate_position
📗 Note: for Workbook 2, the projectile motion can be approximated this way: Galleries, but in general, approximating the velocity by derivatives when moving objectives along a curve is not good due to accumulated discretization errors.
📗 Animation can be driven by events, and in this case, the sequence of images can be functions of the event parameters, for example, mouse position or slider value.
📗 The HTML Canvas (not its context) receives events, for example, MouseEventDoc, TouchEventDoc, KeyboardEventDoc.
📗 Typically, a drawing function is defined for each event, for example,
canvas.onmousemove = function(event) {
\\ draw objects based on the mouse move event
}
canvas.onclick = function() {
\\ draw objects if a click event is received
}
📗 It is easier to work with mouse position in the HTML Canvas coordinate, but a mouse event returns the mouse position in the window coordinate system; wherefore, the following conversion is usually done.
let box = event.target.getBoundingClientRect();
let x = event.clientX - box.left;
let y = event.cleintY - box.top;
📗 event.ctrlKey, event.shiftKey, event.altKey checks if the "Ctrl", "Shift", or "Alt" keys are pressed during the mouse event.
📗 Currently, the mouse is creating small rectangles: change the code so that creates lines instead. Demo animate_event
📗 Note: in the demo, the "onmouseenter" function does the context.beginPath() and context.moveTo(...mouse).
# Moving Things in Circles Again, Again [TopHat, Updated]
📗 Now draw a rectangle and make it move in a circle facing the direction of the movement.
📗 Coming up with the formula to compute the vertices of the rectangle is complicated (fillRect function cannot rotate the rectangle).
📗 Trick: always draw the same rectangle then translate, rotate, and scale. Demo animate_transform Math Notes
The rotation angle is arctan of the change in y position \(250 + 200 \cos\left(t\right)\) over the change in x position \(250 + 200 \sin\left(t\right)\):
\(arctan\left(\dfrac{dy}{dx}\right)\) = \(arctan\left(\dfrac{\dfrac{dy}{dt}}{\dfrac{dx}{dt}}\right)\)
= \(arctan\left(\dfrac{-\left(200 \sin\left(t\right)\right)}{200 \cos\left(t\right)}\right)\)
= \(-arctan\left(\dfrac{\sin\left(t\right)}{\cos\left(t\right)}\right)\)
= \(-arctan\left(\tan\left(t\right)\right)\)
= \(-t\).
That is the reason why there should be a negative sign.
📗 Unfortunately, HTML Canvas does not have functions to translate, rotate, and scale primitives and groups of primitives; it does have functions to translate, rotate, and scale the context.
📗 Using the HTML Canvas pen analogy: instead of translating, rotating, and scaling the primitives: first translate, rotate, and scale the paper, then draw the primitives.
📗 Professor Gleicher's demo: Link.
📗 The usual order to place a single object centered at origin is TRS:
➭ When writing code: context.translate(x, y);context.rotate(r);context.scale(sx, sy);.
➭ When reading in reverse: scale, rotate, translate. Demo transform_list
📗 To scale around (x, y):
➭ context.translate(x, y);
➭ context.scale(sx, sy);
➭ context.translate(-x, -y);
➭ draw();
📗 Read this in reverse:
➭ Draw,
➭ Move the center to origin,
➭ Scale at the origin (so the origin does not move),
➭ Move the center back.
📗 Negative scaling is reflection in x-axis or y-axis.
📗 To rotate around (x, y):
➭ context.translate(x, y);
➭ context.rotate(r);
➭ context.translate(-x, -y);
➭ draw();
📗 Read this in reverse:
➭ Draw,
➭ Move the center to origin,
➭ Rotate around the origin (so the origin does not move),
➭ Move the center back.
📗 A transformation is a rigid transformation if:
➭ Distances between points are preserved.
➭ Handedness is preserved: preserves rotation from x-axis to y-axis (HTML Canvas is left-handed, math graph coordinate system is right-handed).
📗 Two transformations that are rigid:
➭ Translation.
➭ Rotation.
➭ Scaling does not preserve distance.
➭ Mirror reflection (negative scaling) does not preserve handedness.
# Moving Things in Circles, the Last Time [TopHat]
📗 The rectangle can move around a circle by rotating it around the center of the Canvas.
📗 Unfortunately, this trick only works for moving things in a circle (or ellipse with scaling). Demo animate_time
📗 A common good practice to draw an object consisting of multiple parts (primitives or other objects) is:
➭ Draw at a meaningful zero (origin).
➭ Parent object places it in the appropriate location (position, rotation and scale).
📗 Write the robotic arm movement as a function of the sliders (v1, v2, v3).
📗 Change the code so that middle piece is shorter and all pieces would fit on the screen.
📗 Change the code so that middle piece can only rotate 180 degrees. Demo arm
📗 Canvas frame buffering.
📗 Animation as image sequence as a function of time.
📗 Animation using display lists.
📗 Animation driven by events.
📗 Transformations: translate, rotate, scale.
📗 Rigid transformations.
📗 Order and composition of transformation.
📗 Notes and code adapted from the course taught by Professor Michael Gleicher.
📗 Please use Ctrl+F5 or Shift+F5 or Shift+Command+R or Incognito mode or Private Browsing to refresh the cached JavaScript: Code.
📗 You can print the notes: .
📗 Anonymous feedback can be submitted to: Form.