javascript
exercises
exercises.jsā”javascript
/**
* Canvas and WebGL Exercises
*
* Practice 2D graphics and basic WebGL
*/
// =============================================================================
// Exercise 1: Basic Shape Drawing
// =============================================================================
/**
* Implement a shape drawing utility
*/
class BasicShapes {
constructor(ctx) {
this.ctx = ctx;
}
/**
* Draw a rectangle
* @param {number} x - X position
* @param {number} y - Y position
* @param {number} width - Width
* @param {number} height - Height
* @param {Object} options - { fill, stroke, lineWidth }
*/
rectangle(x, y, width, height, options = {}) {
// TODO:
// 1. If options.fill is provided, fillRect with that color
// 2. If options.stroke is provided, strokeRect with that color
// 3. Apply lineWidth if provided
throw new Error('Not implemented');
}
/**
* Draw a circle
* @param {number} x - Center X
* @param {number} y - Center Y
* @param {number} radius - Radius
* @param {Object} options - { fill, stroke, lineWidth }
*/
circle(x, y, radius, options = {}) {
// TODO:
// 1. Begin path
// 2. Draw arc from 0 to 2*PI
// 3. Fill and/or stroke based on options
throw new Error('Not implemented');
}
/**
* Draw a triangle
* @param {number} x1, y1 - First point
* @param {number} x2, y2 - Second point
* @param {number} x3, y3 - Third point
* @param {Object} options - { fill, stroke }
*/
triangle(x1, y1, x2, y2, x3, y3, options = {}) {
// TODO:
// 1. Begin path
// 2. moveTo first point
// 3. lineTo second and third points
// 4. closePath
// 5. Fill and/or stroke
throw new Error('Not implemented');
}
/**
* Draw a star
* @param {number} cx - Center X
* @param {number} cy - Center Y
* @param {number} outerRadius - Outer radius
* @param {number} innerRadius - Inner radius
* @param {number} points - Number of points
* @param {Object} options - { fill, stroke }
*/
star(cx, cy, outerRadius, innerRadius, points, options = {}) {
// TODO:
// 1. Calculate vertices alternating between outer and inner radius
// 2. Draw path through all vertices
// 3. Fill and/or stroke
throw new Error('Not implemented');
}
}
// Test
function testExercise1() {
// Create test canvas in memory
const canvas = document.createElement('canvas');
canvas.width = 400;
canvas.height = 400;
const ctx = canvas.getContext('2d');
const shapes = new BasicShapes(ctx);
// Test drawing (visual verification needed)
shapes.rectangle(10, 10, 100, 50, { fill: 'blue' });
shapes.circle(200, 100, 40, { fill: 'red', stroke: 'black' });
shapes.triangle(300, 50, 350, 100, 250, 100, { fill: 'green' });
shapes.star(200, 250, 50, 25, 5, { fill: 'gold' });
console.log('Exercise 1 passed - verify visually');
}
// =============================================================================
// Exercise 2: Gradient Generator
// =============================================================================
/**
* Create various gradient effects
*/
class GradientGenerator {
constructor(ctx) {
this.ctx = ctx;
}
/**
* Create linear gradient
* @param {number} x1, y1 - Start point
* @param {number} x2, y2 - End point
* @param {Array} stops - [{offset: 0-1, color: string}]
* @returns {CanvasGradient}
*/
linear(x1, y1, x2, y2, stops) {
// TODO: Create and return linear gradient with color stops
throw new Error('Not implemented');
}
/**
* Create radial gradient
* @param {number} x1, y1, r1 - Inner circle
* @param {number} x2, y2, r2 - Outer circle
* @param {Array} stops - Color stops
* @returns {CanvasGradient}
*/
radial(x1, y1, r1, x2, y2, r2, stops) {
// TODO: Create and return radial gradient
throw new Error('Not implemented');
}
/**
* Create rainbow gradient
* @param {number} x1, y1, x2, y2 - Gradient line
* @returns {CanvasGradient}
*/
rainbow(x1, y1, x2, y2) {
// TODO: Create gradient with rainbow colors
throw new Error('Not implemented');
}
/**
* Create sunset gradient
*/
sunset(x1, y1, x2, y2) {
// TODO: Create gradient with sunset colors
throw new Error('Not implemented');
}
}
// Test
function testExercise2() {
const canvas = document.createElement('canvas');
canvas.width = 400;
canvas.height = 400;
const ctx = canvas.getContext('2d');
const gradients = new GradientGenerator(ctx);
const linear = gradients.linear(0, 0, 400, 0, [
{ offset: 0, color: 'red' },
{ offset: 1, color: 'blue' },
]);
ctx.fillStyle = linear;
ctx.fillRect(0, 0, 400, 100);
console.log('Exercise 2 passed - verify visually');
}
// =============================================================================
// Exercise 3: Text Renderer with Effects
// =============================================================================
/**
* Advanced text rendering
*/
class TextEffects {
constructor(ctx) {
this.ctx = ctx;
}
/**
* Draw outlined text
* @param {string} text
* @param {number} x, y
* @param {Object} options - { font, fillColor, strokeColor, strokeWidth }
*/
outlinedText(text, x, y, options = {}) {
// TODO:
// 1. Set font
// 2. Draw stroke first (thicker line)
// 3. Draw fill on top
throw new Error('Not implemented');
}
/**
* Draw text with shadow
*/
shadowText(text, x, y, options = {}) {
// TODO:
// 1. Set shadowColor, shadowBlur, shadowOffsetX/Y
// 2. Draw text
// 3. Reset shadow properties
throw new Error('Not implemented');
}
/**
* Draw gradient text
*/
gradientText(text, x, y, gradient, options = {}) {
// TODO: Set fillStyle to gradient and draw text
throw new Error('Not implemented');
}
/**
* Draw text along a curve
*/
curvedText(text, centerX, centerY, radius, startAngle, options = {}) {
// TODO:
// 1. For each character
// 2. Calculate position on arc
// 3. Save, translate, rotate, draw character, restore
throw new Error('Not implemented');
}
}
// Test
function testExercise3() {
const canvas = document.createElement('canvas');
canvas.width = 400;
canvas.height = 400;
const ctx = canvas.getContext('2d');
const textFx = new TextEffects(ctx);
textFx.outlinedText('Hello', 50, 50, {
font: '48px Arial',
fillColor: 'white',
strokeColor: 'black',
strokeWidth: 3,
});
console.log('Exercise 3 passed - verify visually');
}
// =============================================================================
// Exercise 4: Image Filters
// =============================================================================
/**
* Apply filters to canvas images
*/
class ImageFilters {
constructor(ctx) {
this.ctx = ctx;
}
/**
* Get image data from canvas region
*/
getPixels(x, y, width, height) {
return this.ctx.getImageData(x, y, width, height);
}
/**
* Apply grayscale filter
*/
grayscale(imageData) {
// TODO:
// 1. For each pixel (every 4 values: R, G, B, A)
// 2. Calculate average of R, G, B
// 3. Set R, G, B to average
// 4. Return modified imageData
throw new Error('Not implemented');
}
/**
* Apply sepia filter
*/
sepia(imageData) {
// TODO: Apply sepia transformation matrix
// newR = R * 0.393 + G * 0.769 + B * 0.189
// newG = R * 0.349 + G * 0.686 + B * 0.168
// newB = R * 0.272 + G * 0.534 + B * 0.131
throw new Error('Not implemented');
}
/**
* Invert colors
*/
invert(imageData) {
// TODO: Set each channel to 255 - value
throw new Error('Not implemented');
}
/**
* Adjust brightness
*/
brightness(imageData, amount) {
// TODO: Add amount to each R, G, B (clamp 0-255)
throw new Error('Not implemented');
}
/**
* Adjust contrast
*/
contrast(imageData, amount) {
// TODO: Apply contrast formula
// factor = (259 * (amount + 255)) / (255 * (259 - amount))
// newValue = factor * (value - 128) + 128
throw new Error('Not implemented');
}
/**
* Apply blur (simple box blur)
*/
blur(imageData, radius) {
// TODO: For each pixel, average surrounding pixels within radius
throw new Error('Not implemented');
}
}
// Test
function testExercise4() {
const canvas = document.createElement('canvas');
canvas.width = 100;
canvas.height = 100;
const ctx = canvas.getContext('2d');
// Draw test pattern
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = 'blue';
ctx.fillRect(50, 0, 50, 50);
ctx.fillStyle = 'green';
ctx.fillRect(0, 50, 50, 50);
ctx.fillStyle = 'yellow';
ctx.fillRect(50, 50, 50, 50);
const filters = new ImageFilters(ctx);
const imageData = filters.getPixels(0, 0, 100, 100);
const grayscale = filters.grayscale(imageData);
console.assert(grayscale instanceof ImageData, 'Should return ImageData');
console.log('Exercise 4 passed');
}
// =============================================================================
// Exercise 5: Animation System
// =============================================================================
/**
* Create an animation system
*/
class Animator {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.animations = [];
this.running = false;
this.lastTime = 0;
}
/**
* Add an animated object
* @param {Object} object - Must have update(dt) and draw(ctx) methods
*/
add(object) {
// TODO: Add to animations array
throw new Error('Not implemented');
}
/**
* Remove an animated object
*/
remove(object) {
// TODO: Remove from animations array
throw new Error('Not implemented');
}
/**
* Start animation loop
*/
start() {
// TODO:
// 1. Set running = true
// 2. Start requestAnimationFrame loop
throw new Error('Not implemented');
}
/**
* Stop animation loop
*/
stop() {
// TODO: Set running = false
throw new Error('Not implemented');
}
/**
* Main loop (private)
*/
loop(currentTime) {
// TODO:
// 1. Calculate deltaTime
// 2. Clear canvas
// 3. Update and draw all objects
// 4. Request next frame if running
throw new Error('Not implemented');
}
}
/**
* Bouncing ball animation
*/
class BouncingBall {
constructor(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.vx = Math.random() * 4 - 2;
this.vy = Math.random() * 4 - 2;
}
update(dt, canvasWidth, canvasHeight) {
// TODO:
// 1. Update position based on velocity and dt
// 2. Bounce off walls
throw new Error('Not implemented');
}
draw(ctx) {
// TODO: Draw circle at current position
throw new Error('Not implemented');
}
}
// Test
function testExercise5() {
const canvas = document.createElement('canvas');
canvas.width = 400;
canvas.height = 400;
const animator = new Animator(canvas);
const ball = new BouncingBall(200, 200, 20, 'red');
animator.add(ball);
// animator.start(); // Would need DOM
console.log('Exercise 5 - Animation system ready');
}
// =============================================================================
// Exercise 6: Drawing Pad
// =============================================================================
/**
* Create an interactive drawing pad
*/
class DrawingPad {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.drawing = false;
this.lastX = 0;
this.lastY = 0;
this.strokeColor = '#000000';
this.strokeWidth = 2;
this.tool = 'brush'; // 'brush', 'eraser', 'line', 'rectangle'
this.init();
}
/**
* Initialize event listeners
*/
init() {
// TODO:
// 1. Add mousedown listener - start drawing
// 2. Add mousemove listener - continue drawing
// 3. Add mouseup listener - stop drawing
// 4. Add mouseleave listener - stop drawing
throw new Error('Not implemented');
}
/**
* Get mouse position relative to canvas
*/
getPosition(e) {
// TODO: Return { x, y } relative to canvas
throw new Error('Not implemented');
}
/**
* Start drawing
*/
startDraw(e) {
// TODO: Set drawing = true, store position
throw new Error('Not implemented');
}
/**
* Continue drawing
*/
draw(e) {
// TODO: If drawing, draw line from last position to current
throw new Error('Not implemented');
}
/**
* Stop drawing
*/
stopDraw() {
// TODO: Set drawing = false
throw new Error('Not implemented');
}
/**
* Set stroke color
*/
setColor(color) {
this.strokeColor = color;
}
/**
* Set stroke width
*/
setWidth(width) {
this.strokeWidth = width;
}
/**
* Clear canvas
*/
clear() {
// TODO: Clear entire canvas
throw new Error('Not implemented');
}
/**
* Export as image
*/
toDataURL(type = 'image/png') {
// TODO: Return canvas.toDataURL()
throw new Error('Not implemented');
}
}
// Test
function testExercise6() {
const canvas = document.createElement('canvas');
canvas.width = 400;
canvas.height = 400;
const pad = new DrawingPad(canvas);
pad.setColor('#ff0000');
pad.setWidth(5);
console.log('Exercise 6 - Drawing pad ready');
}
// =============================================================================
// Exercise 7: Sprite Animation
// =============================================================================
/**
* Animate sprites from a sprite sheet
*/
class SpriteAnimator {
constructor(ctx, spriteSheet, frameWidth, frameHeight) {
this.ctx = ctx;
this.spriteSheet = spriteSheet;
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
this.currentFrame = 0;
this.frameCount = 0;
this.frameDelay = 100; // ms between frames
this.lastFrameTime = 0;
this.animations = {};
this.currentAnimation = null;
}
/**
* Define an animation
* @param {string} name - Animation name
* @param {Array} frames - Array of frame indices
* @param {number} delay - Delay between frames
*/
defineAnimation(name, frames, delay = 100) {
// TODO: Store animation definition
throw new Error('Not implemented');
}
/**
* Play an animation
*/
play(name) {
// TODO: Set current animation and reset frame
throw new Error('Not implemented');
}
/**
* Update animation frame based on time
*/
update(currentTime) {
// TODO:
// 1. Check if enough time has passed
// 2. Advance to next frame
// 3. Loop if at end
throw new Error('Not implemented');
}
/**
* Draw current frame
*/
draw(x, y, scale = 1) {
// TODO:
// 1. Calculate source position in sprite sheet
// 2. Draw frame at destination position
throw new Error('Not implemented');
}
/**
* Get frame position in sprite sheet
*/
getFramePosition(frameIndex) {
// TODO: Calculate row and column from frame index
throw new Error('Not implemented');
}
}
// Test
function testExercise7() {
const canvas = document.createElement('canvas');
canvas.width = 400;
canvas.height = 400;
const ctx = canvas.getContext('2d');
// Would need actual sprite sheet
// const sprite = new SpriteAnimator(ctx, spriteImage, 32, 32);
// sprite.defineAnimation('walk', [0, 1, 2, 3], 100);
// sprite.play('walk');
console.log('Exercise 7 - Sprite animator ready');
}
// =============================================================================
// Run All Tests
// =============================================================================
function runAllTests() {
console.log('Running Canvas/WebGL Exercises...\n');
try {
testExercise1();
testExercise2();
testExercise3();
testExercise4();
testExercise5();
testExercise6();
testExercise7();
console.log('\nā
All exercises ready for implementation!');
console.log('Note: Visual verification required for drawing tests');
} catch (error) {
console.error('\nā Error:', error.message);
}
}
// Export
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
BasicShapes,
GradientGenerator,
TextEffects,
ImageFilters,
Animator,
BouncingBall,
DrawingPad,
SpriteAnimator,
runAllTests,
};
}
// Uncomment to run
// runAllTests();