Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>ES6 Improved Canvas Animation Demo</title>
- <style id="jsbin-css">
- body{
- margin:16px;
- font:14pt Roboto, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-weight:300;
- }
- h1{
- display:block;
- margin:-16px -16px 16px;
- padding:32px;
- background:#616161;
- box-sizing:border-box;
- color:#fff;
- font-size:32pt;
- font-weight:300;
- }
- canvas{
- box-shadow:0 0 8px #666;
- }
- </style>
- </head>
- <body>
- <h1>ES6 Improved Canvas Animation Demo</h1>
- <canvas id="canvas" width="600" height="400">Please update your browser.</canvas>
- <button id="addRect">Add Rectangle</button>
- <script id="jsbin-javascript">
- console.clear();
- class Drawable{
- // Lots of constructor parameters with sensible defaults = more useful
- constructor(context, x = 0, y = 0, fillStyle = "#000", strokeStyle = "transparent", lineWidth = 0){
- this.context = context;
- if(!(this.context instanceof CanvasRenderingContext2D)){
- throw new Error("You must provide a valid canvas context to Drawables.");
- }
- this.x = x;
- this.y = y;
- this.fillStyle = fillStyle;
- this.strokeStyle = strokeStyle;
- this.lineWidth = lineWidth;
- // NEW: Deltas Map
- // Note I have decided not to allow this to be passed as a parameter, but you could add this if you prefer.
- // Remember to add it to all of the classes which inherit from Drawable too if you like.
- this.deltas = new Map();
- }
- // The method to call when drawing
- draw(){
- // SAVE context
- this.context.save();
- // TRANSLATE to where we want to start drawing
- this.context.translate(this.x, this.y);
- // CONFIGURE the context with colours and widths
- this.context.fillStyle = this.fillStyle;
- this.context.strokeStyle = this.strokeStyle;
- this.context.lineWidth = this.lineWidth;
- // This is where the drawing would happen.
- // And will in subclasses!
- }
- // This method should be called when a shape has finished drawing
- // It will restore the context back to where it was before drawing started
- afterDraw(){
- // RESTORE the context (undo translate, colours, width changes)
- this.context.restore();
- }
- // NEW: Method to tell a Drawable to apply any applicable animations, based on how much time has passed
- applyAnimation(secondsElapsed){
- // Loop over all deltas
- for(const [propertyName, valueChangePerSecond] of this.deltas){
- // Calculate how much to change the amount by (change amount = change per second * seconds passed)
- const changeAmount = secondsElapsed * valueChangePerSecond;
- // Apply the change
- // e.g. this["x"] += 0.5;
- this[propertyName] += changeAmount;
- // Enable for a verbose rundown of changes being made
- // Warning, this will spam your console!
- //console.log(`Changing the ${propertyName} property by ${changeAmount} because ${secondsElapsed} seconds have passed.`)
- }
- }
- }
- class Rectangle extends Drawable{
- // Compared to drawable, also accepts width and height
- constructor(context, x = 0, y = 0, fillStyle = "#000", strokeStyle = "transparent", lineWidth = 0, deltas = new Map(), width = 100, height = 100){
- // Call through to super - with the parameters it expects
- super(context, x, y, fillStyle, strokeStyle, lineWidth);
- // Save the additional properties
- this.width = width;
- this.height = height;
- }
- // Override draw to provide actual rectangle drawing
- draw(){
- // Call super's draw method to do all of the set-up
- super.draw();
- // Now do the rectangle drawing
- // We can draw *around* point 0,0 because we have already translated to the desired x,y coordinate
- this.context.fillRect( this.width / -2, this.height / -2, this.width, this.height);
- this.context.strokeRect( this.width / -2, this.height / -2, this.width, this.height);
- // Remember, subclasses must call afterDraw to do cleanup (restore the translates done)
- super.afterDraw();
- }
- }
- const canvas = document.getElementById("canvas");
- const context = canvas.getContext("2d");
- // NEW: A set to hold all canvas objects
- const canvasObjects = new Set();
- // Create some rectangles
- const rect1 = new Rectangle(context, 100, 100, "orange");
- const rect2 = new Rectangle(context, 100, 300, "rgba(200, 100, 100, 0.8)");
- const rect3 = new Rectangle(context, 400, 150, "#f00");
- const rect4 = new Rectangle(context, 500, 300, "hsla(200, 50%, 50%, 1)");
- // NEW: Set some delta on the rects
- rect1.deltas.set("width", 5); // Change width property by 5 per second
- rect2.deltas.set("x", 20); // Change x property by 250 per second
- rect2.deltas.set("height", -10); // Change height property by -10 per second
- rect3.deltas.set("height", 5); // Change height property by -10 per second
- rect3.deltas.set("width", 5); // Change height property by -10 per second
- rect4.deltas.set("y", -15); // Change height property by -10 per second
- let lastTime = Date.now();
- // NEW: Add objects to the canvasObjects set
- canvasObjects.add(rect1);
- canvasObjects.add(rect2);
- canvasObjects.add(rect3);
- canvasObjects.add(rect4);
- // Called very rapidly to carry out animations
- function animationLoop(){
- // 0. Schedule the next run of this function
- requestAnimationFrame(animationLoop);
- // 1. Clear the Canvas
- context.clearRect(0, 0, canvas.width, canvas.height);
- // 2. Calculate Changes
- // Calculate the difference, in seconds, between when the
- // loop last ran and now.
- const diffSeconds = (Date.now() - lastTime) / 1000;
- // Store the current time as lastTime, so that next time the loop
- // is called we have a reference point to calculate diffSeconds from
- lastTime = Date.now();
- // Only if some time has passsed...
- if(diffSeconds > 0){
- // NEW: 3. Loop to apply animations and redraw
- for(const canvasObject of canvasObjects){
- canvasObject.applyAnimation(diffSeconds);
- canvasObject.draw();
- }
- }
- }
- // Call animate for the first time
- animationLoop();
- // NEW: Handle clicks on the button
- document.getElementById("addRect").addEventListener("click", (e) => {
- e.preventDefault();
- // Create the rectangle, using some random properties
- const rect = new Rectangle(context, // context
- randomBetween(0, canvas.width), // x
- randomBetween(0, canvas.height), // y
- randomNiceColourString(), // fillStyle
- randomNiceColourString(), // strokeStyle
- randomBetween(0, 5), // lineWidth
- randomBetween(20, 120), // width
- randomBetween(20, 120)); // height
- // Add the rectangle to the canvasObjects array
- // It will get drawn (and optionally begin animation)
- canvasObjects.add(rect);
- });
- // NEW: Helper function for a random whole number between two values (inclusive)
- function randomBetween(minimum, maximum){
- var range = maximum - minimum;
- var random = Math.random();
- random = random * (range + 1);
- random = Math.floor(random);
- random = random + minimum;
- return random;
- }
- // NEW: Helper function for a random hsl colour string - high saturation and mid lightness for vivid colours
- function randomNiceColourString(){
- var h = randomBetween(0, 360),
- s = randomBetween(90, 100),
- l = randomBetween(45, 55);
- // e.g. hsl(320,95%,50%)
- var colour = "hsl(" + h + "," + s + "%," + l + "%)";
- return colour;
- }
- </script>
- <script id="jsbin-source-css" type="text/css">body{
- margin:16px;
- font:14pt Roboto, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-weight:300;
- }
- h1{
- display:block;
- margin:-16px -16px 16px;
- padding:32px;
- background:#616161;
- box-sizing:border-box;
- color:#fff;
- font-size:32pt;
- font-weight:300;
- }
- canvas{
- box-shadow:0 0 8px #666;
- }</script>
- <script id="jsbin-source-javascript" type="text/javascript">console.clear();
- class Drawable{
- // Lots of constructor parameters with sensible defaults = more useful
- constructor(context, x = 0, y = 0, fillStyle = "#000", strokeStyle = "transparent", lineWidth = 0){
- this.context = context;
- if(!(this.context instanceof CanvasRenderingContext2D)){
- throw new Error("You must provide a valid canvas context to Drawables.");
- }
- this.x = x;
- this.y = y;
- this.fillStyle = fillStyle;
- this.strokeStyle = strokeStyle;
- this.lineWidth = lineWidth;
- // NEW: Deltas Map
- // Note I have decided not to allow this to be passed as a parameter, but you could add this if you prefer.
- // Remember to add it to all of the classes which inherit from Drawable too if you like.
- this.deltas = new Map();
- }
- // The method to call when drawing
- draw(){
- // SAVE context
- this.context.save();
- // TRANSLATE to where we want to start drawing
- this.context.translate(this.x, this.y);
- // CONFIGURE the context with colours and widths
- this.context.fillStyle = this.fillStyle;
- this.context.strokeStyle = this.strokeStyle;
- this.context.lineWidth = this.lineWidth;
- // This is where the drawing would happen.
- // And will in subclasses!
- }
- // This method should be called when a shape has finished drawing
- // It will restore the context back to where it was before drawing started
- afterDraw(){
- // RESTORE the context (undo translate, colours, width changes)
- this.context.restore();
- }
- // NEW: Method to tell a Drawable to apply any applicable animations, based on how much time has passed
- applyAnimation(secondsElapsed){
- // Loop over all deltas
- for(const [propertyName, valueChangePerSecond] of this.deltas){
- // Calculate how much to change the amount by (change amount = change per second * seconds passed)
- const changeAmount = secondsElapsed * valueChangePerSecond;
- // Apply the change
- // e.g. this["x"] += 0.5;
- this[propertyName] += changeAmount;
- // Enable for a verbose rundown of changes being made
- // Warning, this will spam your console!
- //console.log(`Changing the ${propertyName} property by ${changeAmount} because ${secondsElapsed} seconds have passed.`)
- }
- }
- }
- class Rectangle extends Drawable{
- // Compared to drawable, also accepts width and height
- constructor(context, x = 0, y = 0, fillStyle = "#000", strokeStyle = "transparent", lineWidth = 0, deltas = new Map(), width = 100, height = 100){
- // Call through to super - with the parameters it expects
- super(context, x, y, fillStyle, strokeStyle, lineWidth);
- // Save the additional properties
- this.width = width;
- this.height = height;
- }
- // Override draw to provide actual rectangle drawing
- draw(){
- // Call super's draw method to do all of the set-up
- super.draw();
- // Now do the rectangle drawing
- // We can draw *around* point 0,0 because we have already translated to the desired x,y coordinate
- this.context.fillRect( this.width / -2, this.height / -2, this.width, this.height);
- this.context.strokeRect( this.width / -2, this.height / -2, this.width, this.height);
- // Remember, subclasses must call afterDraw to do cleanup (restore the translates done)
- super.afterDraw();
- }
- }
- const canvas = document.getElementById("canvas");
- const context = canvas.getContext("2d");
- // NEW: A set to hold all canvas objects
- const canvasObjects = new Set();
- // Create some rectangles
- const rect1 = new Rectangle(context, 100, 100, "orange");
- const rect2 = new Rectangle(context, 100, 300, "rgba(200, 100, 100, 0.8)");
- const rect3 = new Rectangle(context, 400, 150, "#f00");
- const rect4 = new Rectangle(context, 500, 300, "hsla(200, 50%, 50%, 1)");
- // NEW: Set some delta on the rects
- rect1.deltas.set("width", 5); // Change width property by 5 per second
- rect2.deltas.set("x", 20); // Change x property by 250 per second
- rect2.deltas.set("height", -10); // Change height property by -10 per second
- rect3.deltas.set("height", 5); // Change height property by -10 per second
- rect3.deltas.set("width", 5); // Change height property by -10 per second
- rect4.deltas.set("y", -15); // Change height property by -10 per second
- let lastTime = Date.now();
- // NEW: Add objects to the canvasObjects set
- canvasObjects.add(rect1);
- canvasObjects.add(rect2);
- canvasObjects.add(rect3);
- canvasObjects.add(rect4);
- // Called very rapidly to carry out animations
- function animationLoop(){
- // 0. Schedule the next run of this function
- requestAnimationFrame(animationLoop);
- // 1. Clear the Canvas
- context.clearRect(0, 0, canvas.width, canvas.height);
- // 2. Calculate Changes
- // Calculate the difference, in seconds, between when the
- // loop last ran and now.
- const diffSeconds = (Date.now() - lastTime) / 1000;
- // Store the current time as lastTime, so that next time the loop
- // is called we have a reference point to calculate diffSeconds from
- lastTime = Date.now();
- // Only if some time has passsed...
- if(diffSeconds > 0){
- // NEW: 3. Loop to apply animations and redraw
- for(const canvasObject of canvasObjects){
- canvasObject.applyAnimation(diffSeconds);
- canvasObject.draw();
- }
- }
- }
- // Call animate for the first time
- animationLoop();
- // NEW: Handle clicks on the button
- document.getElementById("addRect").addEventListener("click", (e) => {
- e.preventDefault();
- // Create the rectangle, using some random properties
- const rect = new Rectangle(context, // context
- randomBetween(0, canvas.width), // x
- randomBetween(0, canvas.height), // y
- randomNiceColourString(), // fillStyle
- randomNiceColourString(), // strokeStyle
- randomBetween(0, 5), // lineWidth
- randomBetween(20, 120), // width
- randomBetween(20, 120)); // height
- // Add the rectangle to the canvasObjects array
- // It will get drawn (and optionally begin animation)
- canvasObjects.add(rect);
- });
- // NEW: Helper function for a random whole number between two values (inclusive)
- function randomBetween(minimum, maximum){
- var range = maximum - minimum;
- var random = Math.random();
- random = random * (range + 1);
- random = Math.floor(random);
- random = random + minimum;
- return random;
- }
- // NEW: Helper function for a random hsl colour string - high saturation and mid lightness for vivid colours
- function randomNiceColourString(){
- var h = randomBetween(0, 360),
- s = randomBetween(90, 100),
- l = randomBetween(45, 55);
- // e.g. hsl(320,95%,50%)
- var colour = "hsl(" + h + "," + s + "%," + l + "%)";
- return colour;
- }</script></body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement