CSS3 2D Transformations
CSS3 introduced the transform property, enabling developers to manipulate the coordinate space of elements. This allows for effects such as rotation, scaling, skewing, and translation without relying on JavaScript for the visual rendering engine, resulting in smoother performance.
Underlying specific transformation functions like rotate() or scale() is the matrix() function. The matrix allows for complex combined transformations using six parameters: matrix(a, b, c, d, tx, ty). These parameters map to scaling (a, d), skewing (b, c), and translation (tx, ty).
Dynamic Rotation with jQuery
While CSS transitions handle state changes, continuous animation often requires JavaScript. We can use jQuery to update the CSS transform property frame-by-frame. It is crucial to detect the correct vendor prefix for cross-browser compatibility.
(function($) {
var $target = $('#anim-object');
var angle = 0;
// Helper to find the supported transform property
var getTransformProp = function() {
var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms'];
for (var i = 0; i < prefixes.length; i++) {
var prop = prefixes[i] ? (prefixes[i] + 'Transform') : 'transform';
if (document.body.style[prop] !== undefined) {
return prop;
}
}
return 'transform'; // Default fallback
};
var transformProp = getTransformProp();
function rotateStep() {
angle = (angle + 2) % 360;
$target.css(transformProp, 'rotate(' + angle + 'deg)');
requestAnimationFrame(rotateStep);
}
if ($target.length) {
rotateStep();
}
})(jQuery);
Combining Transforms using Matrices
Applying multiple transforms (e.g., skewing and scaling) simultaneously on a single element is best achieved using the matrix function. For instance, to create a 3D perspective effect or a cover-flow style layout, one would calculate the matrix values dynamically. The logic involves manipulating the array of matrix values to apply scaling along the X-axis while applying a skew angle to the Y-axis, then updating the DOM element's style via JavaScript.
HTML5 Canvas Animation
The <canvas> element provides a bitmap surface for rendering graphics, shapes, and images via JavaScript. Unlike DOM manipulation, Canvas is immediate mode; once a shape is drawn, it becomes part of the pixel data and is not retained as an object in the scene graph. This requires the developer to implement a redraw loop to animate content.
The Rendering Context
To draw on a canvas, you must retrieve the CanvasRenderingContext2D. This object exposes methods such as fillRect, beginPath, arc, and stroke. It also manages global properties like fillStyle, strokeStyle, and transformation matrices specific to the canvas state.
Creating a Particle System
A practical demonstration of canvas capabilities is a particle system. This involves managing an array of objects, each representing a particle with coordinates and velocity. The animation loop clears the canvas, updates particle positions, and redraws them.
var canvas = document.getElementById('particleCanvas');
var ctx = canvas.getContext('2d');
var particles = [];
var particleCount = 50;
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
function createParticles() {
for (var i = 0; i < particleCount; i++) {
particles.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 4,
vy: (Math.random() - 0.5) * 4,
size: Math.random() * 3 + 1,
color: 'rgba(255, 255, 255, 0.8)'
});
}
}
function updateAndDraw() {
// Clear the screen with a slight fade effect
ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
particles.forEach(function(p) {
p.x += p.vx;
p.y += p.vy;
// Boundary check (bounce)
if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fillStyle = p.color;
ctx.fill();
});
requestAnimationFrame(updateAndDraw);
}
createParticles();
updateAndDraw();
Interactive Game Loop
Building a game on Canvas requires a structured loop handling logic updates and rendering. Below is a simplified framework for a game involving a player entity and collision detection against enemies.
var gameCanvas = document.getElementById('gameCanvas');
var gCtx = gameCanvas.getContext('2d');
var gameState = {
running: true,
player: { x: 50, y: 200, width: 30, height: 30, speed: 5 },
enemies: [],
score: 0
};
// Initialize Enemies
for(var i=0; i<5; i++) {
gameState.enemies.push({
x: 400 + (i * 60),
y: 50 + (i * 40),
width: 20,
height: 20,
speed: 2
});
}
function update() {
if (!gameState.running) return;
// Move Player (Simple AI or Input logic would go here)
gameState.player.y += Math.sin(Date.now() / 200) * 2;
// Move Enemies and Check Collision
gameState.enemies.forEach(function(enemy) {
enemy.x -= enemy.speed;
if (enemy.x < 0) enemy.x = gameCanvas.width; // Reset position
// AABB Collision Detection
if (gameState.player.x < enemy.x + enemy.width &&
gameState.player.x + gameState.player.width > enemy.x &&
gameState.player.y < enemy.y + enemy.height &&
gameState.player.height + gameState.player.y > enemy.y) {
gameState.running = false;
alert('Game Over! Score: ' + gameState.score);
}
});
}
function draw() {
gCtx.clearRect(0, 0, gameCanvas.width, gameCanvas.height);
// Draw Player
gCtx.fillStyle = '#00FF00';
gCtx.fillRect(gameState.player.x, gameState.player.y, gameState.player.width, gameState.player.height);
// Draw Enemies
gCtx.fillStyle = '#FF0000';
gameState.enemies.forEach(function(enemy) {
gCtx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
});
}
function gameLoop() {
update();
draw();
if (gameState.running) {
requestAnimationFrame(gameLoop);
}
}
gameLoop();