Change Color of Shape Mid Tween - javascript

I'm trying to make an event that changes my shapes stroke color for 5 seconds when a button is clicked, and then the shape returns to original color after the duration.
I am able to do this with clearing the entire stage and redrawing new shapes (which resets their position), but I can't figure it out with the current shapes.
Q. What's the best way to approach making a change to a shapes color, during a Tween?
I was also curious if there's a better way to handling tweening the shapes width? Currently I am relying on ScaleX and ScaleY - but this also changes the stroke's size - which is not desired.
JS Fiddle
HTML
<button id="change">Click to Change Color</button>
<canvas id="demoCanvas" width="500" height="500"></canvas>
JS
var stage,
circle;
function init() {
stage = new createjs.Stage("demoCanvas");
createjs.Ticker.setFPS(60);
createjs.Ticker.addEventListener("tick", stage);
}
function createCircle(){
circle = new createjs.Shape().set({name:"circle"});
circle.graphics.setStrokeStyle(1).beginStroke("#000").beginFill( "#FFF" ).drawCircle(0, 0, 20);
circle.x = 100;
circle.y = 100;
stage.addChild(circle);
createjs.Tween.get(circle, {loop: true})
.to({x: 225, y: 225}, 1000, createjs.Ease.getPowInOut(1))
.to({x: 100, y: 100}, 1000, createjs.Ease.getPowInOut(1));
circle2 = new createjs.Shape().set({name:"circle"});
circle2.graphics.setStrokeStyle(1).beginStroke("#000").beginFill( "#FFF" ).drawCircle(0, 0, 20);
circle2.x = 400;
circle2.y = 400;
stage.addChild(circle2);
createjs.Tween.get(circle2, {loop: true})
.to({scaleX: 2, scaleY: 2, x: 425, y: 125}, 1000, createjs.Ease.getPowInOut(1))
.to({scaleX: 1, scaleY: 1, x: 400, y: 400}, 1000, createjs.Ease.getPowInOut(1));
stage.update();
}
$( "#change" ).click(function() {
// change color
});
$(document).ready(function() {
init();
createCircle();
});

There are a few questions in this post, so I will try to answer them all:
First, a solution to most of your issues is Graphic commands. Commands provide a simple way to store graphic instructions, and change them later. Here is a simple example:
var shape = new createjs.Shape();
var colorCmd = shape.graphics.beginFill("red").command;
var rectCmd = shape.graphics.drawRect(0,0,100,100).command;
// Later
colorCmd.style = "blue";
rectCmd.w = 200;
stage.update(); // Remember to update the stage after changing properties
You can read more about commands on the createjs blog. All commands and their properties are documented in the EaselJS docs.
Change a color: I outlined this in the example above, but the short answer is to adjust the style property of a fill command. If you want to change it instantly, you can just set up a Tween.call:
Example:
createjs.Tween.get(circle, {loop: true})
.to({x: 225, y: 225}, 1000, createjs.Ease.getPowInOut(1))
.call(function(tween) {
colorCmd.style = "rgba(0, 0, 255, 0.5)"; // Change to 50% blue
})
.to({x: 100, y: 100}, 1000, createjs.Ease.getPowInOut(1));
If you want to tween the color, then you could check out the ColorPlugin, which is currently in a "Plugins" branch of TweenJS: https://github.com/CreateJS/TweenJS/tree/Plugins/extras/plugins
// Tween the color from its current value to blue.
// Note that only hex, short hex, HSL, and RGB formats are supported.
createjs.Tween.get(colorCmd).to({style:"#0000ff"});
Change the size: The example above also shows how to modify the values of a drawRect call. You can do the same with any other draw command (including moveTo, lineTo, polyStar, etc).
Scaling also works, and if you want to not scale the stroke, just set the ignoreScale parameter on the stroke style.
shape.graphics.setStrokeStyle(1, null, null, null, true);

Related

p5.js: How to punch a hole into a canvas shape, without changing the background or using an external canvas?

I'm trying to figure out a way to punch holes into a thing, but without the hole also going through whatever is in the background already.
the hole is made of a few arbitrary shapes, and is not a simple path I can use to clip.
The hole is only punched through the foreground shapes, and not all the way into the background (the background should stay as-is).
I figured a way to do this with an external context, and then bringing it in.
My questions: is there a way to do it on my default canvas, and avoid the complications that might arise from the external context (extra memory, color differences etc)?
Here's a working (p5.js) example, which is using a new context:
function setup() {
createCanvas(600,600);
background(255, 0, 0);
noStroke();
}
function draw() {
//blue: stuff in the background that should not change
fill ("blue");
rect (20,20,500,500);
//draw on external canvas
pg = createGraphics(600,600);
//yellow+green foreground shapes
pg.fill("green");
pg.rect(100, 100, 200, 200);
pg.fill("yellow");
pg.rect(80, 80, 100, 300);
//punch a hole in the shapes
pg.fill(0, 0, 255);
pg.blendMode(REMOVE);
pg.circle(140, 140, 150);
pg.circle(180, 180, 150);
//bring in the external canvas with punched shapes
image(pg, 0, 0);
noLoop();
}
html,
body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
There is no easy or built in way to do this without the technique you've already discovered. The only alternative would be to implement boolean geometry operations like subtraction and intersection on arbitrary shapes and splines. That would allow you to make arbitrary bezier splines that represent the composites of multiple complex shapes and then draw those directly. This approach would have different behavior with regards to stroke than the removal approach.
Just FYI, there are also a pair of methods in p5js erase() and noErase() which have a similar behavior to the blendMode(REMOVE) approach. I don't think there's any technical benefit, but it might be more idiomatic to use them rather than blend mode.
I agree, as Paul(+1) mentions as well, using multiple p5.Graphics instances (external contexts as you call them) is the most straight forward/readable method.
You could explicitly uses p5.Image and mask(), however there are few more operations involved and the could would be a little less readable. Here's an example:
function setup() {
createCanvas(600,600);
background(255, 0, 0);
noStroke();
}
function draw() {
//blue: stuff in the background that should not change
fill ("blue");
rect (20,20,500,500);
//draw on external canvas
pg = createGraphics(600,600);
//yellow+green foreground shapes
pg.fill("green");
pg.rect(100, 100, 200, 200);
pg.fill("yellow");
pg.rect(80, 80, 100, 300);
//punch a hole in the shapes
let msk = createGraphics(600, 600);
msk.background(0);
msk.erase();
msk.noStroke();
msk.circle(140, 140, 150);
msk.circle(180, 180, 150);
let mskImage = msk.get();
pgImage = pg.get();
pgImage.mask(mskImage);
image(pgImage, 0, 0);
noLoop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
A (very) hacky workaround would be to do the same thing with one canvas.
This would leave the areas inside the circles completely transparent so make them appear blue, simply make the background element behind the blue:
function setup() {
createCanvas(600,600);
background(255, 0, 0);
noStroke();
}
function draw() {
//blue: stuff in the background that should not change
fill ("blue");
rect (20,20,500,500);
//draw on external canvas
// pg = createGraphics(600,600);
//yellow+green foreground shapes
fill("green");
rect(100, 100, 200, 200);
fill("yellow");
rect(80, 80, 100, 300);
//punch a hole in the shapes
fill(0, 0, 255);
blendMode(REMOVE);
circle(140, 140, 150);
circle(180, 180, 150);
//bring in the external canvas with punched shapes
// image(pg, 0, 0);
noLoop();
}
body{
/* make the HTML background match the canvas blue */
background-color: #00F;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
This might not be flexible enough though.
Now, assuming your foreground is made of the yellow and green shapes and the background is blue, another option would be manually accessing the pixels[] array and updating pixel values. In your example the masks are circular so you could check if:
the distance between the current pixel and the circle's centre is smaller than the circle's radius: this means the pixel is inside the circle
also, if the colour inside the circle is a foreground colour (e.g. green or yellow in your case)
If both conditions match then you could replace this pixel with a background colour (blue in your case)
Here's an example of that:
function setup() {
createCanvas(600,600);
pixelDensity(1);
background(255, 0, 0);
noStroke();
}
function draw() {
//blue: stuff in the background that should not change
fill ("blue");
rect (20,20,500,500);
//draw on external canvas
//yellow+green foreground shapes
fill("green");
rect(100, 100, 200, 200);
fill("yellow");
rect(80, 80, 100, 300);
//punch a hole in the shapes
fill(0, 0, 255);
// make pixels available for reading
loadPixels();
// apply each circle "mask" / bg color replacement
// yellow , green , bg blue to replace fg with
circleMask(140, 140, 150, [255, 255, 0], [0, 0x80, 0], [0, 0, 255]);
circleMask(180, 180, 150, [255, 255, 0], [0, 0x80, 0], [0, 0, 255]);
// once all "masks" are applied,
updatePixels();
noLoop();
}
function circleMask(x, y, radius, fg1, fg2, bg){
// total number of pixels
let np = width * height;
let np4 = np*4;
//for each pixel (i = canvas pixel index (taking r,g,b,a order into account)
// id4 is a quarter of "i"
for(let i = 0, id4 =0 ; i < np4; i+=4, id4++){
// compute x from pixel index
let px = id4 % width;
// compute y from pixel index
let py = id4 / width;
// if we're within the circle
if(dist(px, py, x, y) < radius / 2){
// if we've found foreground colours to make transparent
// ([0][1][2] = r, g, b)
if((pixels[i] == fg1[0] || pixels[i] == fg2[0]) &&
(pixels[i+1] == fg1[1] || pixels[i+1] == fg2[1]) &&
(pixels[i+2] == fg1[2] || pixels[i+2] == fg2[2])){
// "mask" => replace fg colour matching pixel with bg pixel
pixels[i] = bg[0];
pixels[i+1] = bg[1];
pixels[i+2] = bg[2];
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
There are a few things to notice here.
pixels[] is faster than set(x, y, clr) but it means you need to remember a few details:
call loadPixels() before accessing pixels[] to read/populate the array
make all the pixels changes required (in this case the circle "masks" / pixels inside circle colour replacement)
call updatePixels() after pixels[] have been updated
Also notices it takes a bit of time to execute.
There could be a few speed improvements such as only iterating over the pixels inside the bounding box of the circle before checking distance and checking squared distance instead of dist(), however this would also make the code less readable.

Rotate a group of shapes containing text whilst keeping text centered and horizontal

This is probably just maths.
I am using Konva to dynamically generate shapes, which I'm storing as a label. So there's a label which contains a textElement and a rectangle. I want to make sure text in that rectangle is always a) Centered horizontally and vertically and b) facing the right way up.
So a rectangle could have any rotation, but I always want the text centered and facing the right way up.
The code for creation; width, height, rotation, x and y all have values pulled from a database.
var table = new Konva.Label({
x: pos_x,
y: pos_y,
width: tableWidth,
height: tableHeight,
draggable:true
});
table.add(new Konva.Rect({
width: tableWidth,
height: tableHeight,
rotation: rotation,
fill: fillColor,
stroke: strokeColor,
strokeWidth: 4
}));
table.add(new Konva.Text({
width: tableWidth,
height: tableHeight,
x: pos_x, //Defaults to zero
y: pos_y, //Default to zero
text: tableNumber,
verticalAlign: 'middle',
align: 'center',
fontSize: 30,
fontFamily: 'Calibri',
fill: 'black'
}))
tableLayer.add(table);
The problem is, if rotation is in place, text is off center, as in this image:
I do manually correct in some circumstances - for example if rotation = 45 degrees:
pos_x = -tableWidth/2;
pos_y = tableHeight/5;
but that is not a permanent solution. I want the x and y co-ordinates of the text to be at the centerpoint of the shape itself.
I've tried a few approaches (such as applying rotation to the Label itself and then negative rotation value to the text)
This code snippet illustrates a solution. It is copied & modified from my other self-answer when I was looking for a robust approach to rotation around an arbitrary point - note that I consider this a slightly different question than my original so I have not suggested this is a dup. The difference is the need to work with a more complex grouped shape and to keep some element within that group unrotated.
Not in the OP's question, but I set a background rectangle into the text by making the text a group. The purpose of this was to show that the text rectangle will extend outside the label rectangle in some points of rotation. This is not a critical issue but it is useful to see it happen.
The fundamental challenge for the coder is to understand how the shapes move when rotated since we usually want to spin them around their centre but the fundamental 2D canvas pattern that Konva (and all HTML5 canvas wrappers) follow is to rotate from the top-left corner, al least for rectangles as per shapes in the question. It 'is' possible to move the rotation point (known as the offset) but again that is a conceptual challenge for the dev and a nice trap for anyone trying to support the code later.
There's a lot of code in this answer that is here to set up something dynamic that you can use to visualise what is going on. However, the crux is in this:
// This is the important call ! Cross is the rotation point as illustrated by crosshairs.
rotateAroundPoint(shape, rotateBy, {x: cross.x(), y: cross.y()});
// The label is a special case because we need to keep the text unrotated.
if (shape.name() === 'label'){
let text = shape.find('.text')[0];
rotateAroundPoint(text, -1 * rotateBy, {x: text.getClientRect().width/2, y: text.getClientRect().height/2});
}
The rotateAroundPoint() function takes as parameters the Konva shape to rotate, the clockwise rotation angle (not radians, good ole degrees), and the x & y position of the rotation point on the canvas / parent.
I constructed a group of shapes as my label, composing it from a rectangle and a text shape. I named this 'label'. Actually I switched the text shape to be another group of rect + text to that I could show the rectangle the text sits within. You could leave out the extra group. I named this 'text'.
The first call to rotateAroundPoint() rotates the group named 'label'. So the group rotates on the canvas. Since the 'text' is a child of the 'label' group, that would leave the 'text' rotated, so the next line checks if we are working with the 'label' group, and if so we need to get hold of the 'text' shape which is what this line does:
let text = shape.find('.text')[0];
In Konva the result of a find() is a list so we take the first in the list. Now all that remains for me to do is rotate the text on the 'label' group back again by applying the negative rotation degrees to its center point. The line below achieves this.
rotateAroundPoint(text, -1 * rotateBy, {x: text.getClientRect().width/2, y: text.getClientRect().height/2});
One note worthy of mention - I used a group for my 'text' shape. A Konva group does not naturally have a width or height - it is more of a means to collect shapes together but without a 'physical' container. So to get its width and height for the centre point calculations I use the group.getClientRect() method which gives the size of the minimum bounding box that would contain all shapes in the group, and yields an object formed as {width: , height: }.
Second note - the first use of rotateAroundPoint() affects the 'label' group which has as its parent the canvas. The second use of that function affects the 'text' group which has the 'label' group as its parent. Its subtle but worth knowing.
Here is the snippet. I urge you to run it fullscreen and spin a few shapes around a few different points.
// Code to illustrate rotation of a shape around any given point. The important functions here is rotateAroundPoint() which does the rotation and movement math !
let
angle = 0, // display value of angle
startPos = {x: 80, y: 45},
shapes = [], // array of shape ghosts / tails
rotateBy = 20, // per-step angle of rotation
shapeName = $('#shapeName').val(), // what shape are we drawing
shape = null,
ghostLimit = 10,
// Set up a stage
stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
}),
// add a layer to draw on
layer = new Konva.Layer(),
// create the rotation target point cross-hair marker
lineV = new Konva.Line({points: [0, -20, 0, 20], stroke: 'lime', strokeWidth: 1}),
lineH = new Konva.Line({points: [-20, 0, 20, 0], stroke: 'lime', strokeWidth: 1}),
circle = new Konva.Circle({x: 0, y: 0, radius: 10, fill: 'transparent', stroke: 'lime', strokeWidth: 1}),
cross = new Konva.Group({draggable: true, x: startPos.x, y: startPos.y}),
labelRect, labelText;
// Add the elements to the cross-hair group
cross.add(lineV, lineH, circle);
layer.add(cross);
// Add the layer to the stage
stage.add(layer);
$('#shapeName').on('change', function(){
shapeName = $('#shapeName').val();
shape.destroy();
shape = null;
reset();
})
// Draw whatever shape the user selected
function drawShape(){
// Add a shape to rotate
if (shape !== null){
shape.destroy();
}
switch (shapeName){
case "rectangle":
shape = new Konva.Rect({x: startPos.x, y: startPos.y, width: 120, height: 80, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
case "hexagon":
shape = new Konva.RegularPolygon({x: startPos.x, y: startPos.y, sides: 6, radius: 40, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
case "ellipse":
shape = new Konva.Ellipse({x: startPos.x, y: startPos.y, radiusX: 40, radiusY: 20, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
case "circle":
shape = new Konva.Ellipse({x: startPos.x, y: startPos.y, radiusX: 40, radiusY: 40, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
case "star":
shape = new Konva.Star({x: startPos.x, y: startPos.y, numPoints: 5, innerRadius: 20, outerRadius: 40, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
case "label":
shape = new Konva.Group({name: 'label'});
labelRect = new Konva.Rect({x: 0, y: 0, width: 120, height: 80, fill: 'magenta', stroke: 'black', strokeWidth: 4, name: 'rect'})
shape.add(labelRect);
labelText = new Konva.Group({name: 'text'});
labelText.add(new Konva.Rect({x: 0, y: 0, width: 100, height: 40, fill: 'cyan', stroke: 'black', strokeWidth: 2}))
labelText.add(new Konva.Text({x: 0, y: 0, width: 100, height: 40, text: 'Wombat',fontSize: 20, fontFamily: 'Calibri', align: 'center', padding: 10}))
shape.add(labelText)
labelText.position({x: (labelRect.width() - labelText.getClientRect().width) /2, y: (labelRect.height() - labelText.getClientRect().height) /2})
break;
};
layer.add(shape);
cross.moveToTop();
}
// Reset the shape position etc.
function reset(){
drawShape(); // draw the current shape
// Set to starting position, etc.
shape.position(startPos)
cross.position(startPos);
angle = 0;
$('#angle').html(angle);
$('#position').html('(' + shape.x() + ', ' + shape.y() + ')');
clearTails(); // clear the tail shapes
stage.draw(); // refresh / draw the stage.
}
// Click the stage to move the rotation point
stage.on('click', function (e) {
cross.position(stage.getPointerPosition());
stage.draw();
});
// Rotate a shape around any point.
// shape is a Konva shape
// angleRadians is the angle to rotate by, in radians
// point is an object {x: posX, y: posY}
function rotateAroundPoint(shape, angleDegrees, point) {
let angleRadians = angleDegrees * Math.PI / 180; // sin + cos require radians
const x =
point.x +
(shape.x() - point.x) * Math.cos(angleRadians) -
(shape.y() - point.y) * Math.sin(angleRadians);
const y =
point.y +
(shape.x() - point.x) * Math.sin(angleRadians) +
(shape.y() - point.y) * Math.cos(angleRadians);
shape.rotation(shape.rotation() + angleDegrees); // rotate the shape in place
shape.x(x); // move the rotated shape in relation to the rotation point.
shape.y(y);
shape.moveToTop(); //
}
$('#rotate').on('click', function(){
let newShape = shape.clone();
shapes.push(newShape);
layer.add(newShape);
// This ghost / tails stuff is just for fun.
if (shapes.length >= ghostLimit){
shapes[0].destroy();
shapes = shapes.slice(1);
}
for (var i = shapes.length - 1; i >= 0; i--){
shapes[i].opacity((i + 1) * (1/(shapes.length + 2)))
};
// This is the important call ! Cross is the rotation point as illustrated by crosshairs.
rotateAroundPoint(shape, rotateBy, {x: cross.x(), y: cross.y()});
// The label is a special case because we need to keep the text unrotated.
if (shape.name() === 'label'){
let text = shape.find('.text')[0];
rotateAroundPoint(text, -1 * rotateBy, {x: text.getClientRect().width/2, y: text.getClientRect().height/2});
}
cross.moveToTop();
stage.draw();
angle = angle + 10;
$('#angle').html(angle);
$('#position').html('(' + Math.round(shape.x() * 10) / 10 + ', ' + Math.round(shape.y() * 10) / 10 + ')');
})
// Function to clear the ghost / tail shapes
function clearTails(){
for (var i = shapes.length - 1; i >= 0; i--){
shapes[i].destroy();
};
shapes = [];
}
// User cicks the reset button.
$('#reset').on('click', function(){
reset();
})
// Force first draw!
reset();
body {
margin: 10;
padding: 10;
overflow: hidden;
background-color: #f0f0f0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/konva#^3/konva.min.js"></script>
<p>1. Click the rotate button to see what happens when rotating around shape origin.</p>
<p>2. Reset then click stage to move rotation point and click rotate button again - rinse & repeat</p>
<p>
<button id = 'rotate'>Rotate</button>
<button id = 'reset'>Reset</button>
<select id='shapeName'>
<option value='label' selected='selected'>Label</option>
<option value='rectangle'>Rectangle</option>
<option value='hexagon'>Polygon</option>
<option value='ellipse' >Ellipse</option>
<option value='circle' >Circle</option>
<option value='star'>Star</option>
</select>
Angle : <span id='angle'>0</span>
Position : <span id='position'></span>
</p>
<div id="container"></div>

How can I animate a moving circle to a bigger circle using Raphael?

I have a circle with x=50, y=30 and radius = 20.
This circle should move in 1000 ms to a new position x=150, y=170 and radius = 30.
These are my objects:
paper.circle(50, 30, 20);
paper.newCircle(150, 170, 30);
Now I want to animate a movement, so it looks like the first circle moves to the second one but with a bigger radius.
Much like the first example here (click on the arrow) but it should have a bigger radius after the animation.
This should be pretty simple but I can not figure out how to do this.
You can pass in an object to the animate method, and pass it a duration, like follows.
var c = paper.circle(50, 30, 20);
c.animate({ r: 30, cx: 150, cy: 170 }, 1000);

How to add gravity to multiple bodies 'automatically'?

I know how to add gravity to any given object/element. Just add acceleration Y downwards. But "what if want my hero to fly?" or "what if I want to turn gravity off for one particular object? I'll have to set gravity = 0 which will turn off for everyone obviously. I also thought giving every shape their own 'gravity' variable, but I figured that would be too much and it's probably not how it's done...
How would I go from creating shapes
(Using EaseJS)
function spawnShape(x, y, w, h) {
var shape = new createjs.Shape();
shape.graphics.beginFill("black").drawRect(x, y, w, h);
stage.addChild(shape);
}
spawnShape(20, 250, 600, 30);
spawnShape(200, 150, 5, 5);
stage.update();
to adding gravity "automatically"? (every shape inheriting downwards acceleration) I know there's 2D physics engines made but I want to do/understand this myself, and I did try to use PhysicsJS but failed to do so.. I'll probably be using an engine but for now I want to know how to do this :P
You can create an object:
function Shape(x, y, w, h, gravity){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.gravity = gravity;
this.shape = new createjs.Shape();
stage.addChild(shape);
this.draw = function(){
shape.graphics.beginFill("black").drawRect(x, y, w, h);
}
}
Thus, you can call it as:
> x = new Shape(200, 200, 10, 10, 0.5)
Shape {x: 200, y: 200, w: 10, h: 10, gravity: 0.5}
> y = new Shape(400, 100, 50, 100, 0.75)
Shape {x: 400, y: 100, w: 50, h: 100, gravity: 0.75}
> x.gravity = 0
0
> y.gravity
0.75
I haven't worked with EaseJS so the specifics may be inaccurate but the overarching logic will be as demonstrated above.
I think you understand how to add gravity or not add gravity to an object. As you say, it is just adding acceleration Y to the object.
It sounds like you just need to think out your design a little. Let's say you have a module 'gravity.js' that is responsible for applying gravity to an object.
/* gravity.js */
const DEFAULT_GRAVITY_ACCELERATION = 1.0;
function applyGravity(shape) {
const gravity = shape.gravityAcceleration !== undefined ?
shape.gravityAcceleration : DEFAULT_GRAVITY_ACCELERATION;
//Do whatever you normally do to update Y acceleration. Code below
//is just an example.
shape.addYAcceleration(gravity);
}
If you create a shape someplace and want it to be free of gravity, just set the .gravityAcceleration member of that object. BTW, there is nothing special about that "gravityAcceleration" name--it could be whatever you want.
//Assuming spawnShape returns an object.
var superman = spawnShape(20, 250, 600, 30);
superman.gravityAcceleration = 0; //Override the default gravity.
You only need to set the .gravityAcceleration member for shape objects that will defy gravity.

Drawing shapes and lines with HTML5 Canvas and jQuery

I have an upcoming project that I would like to use the HTML5 canvas element to accomplish what would have had to be done in the past with either images and absolutely paced div's or Flash. Here is a basic example of the concept
Here are my concerns:
I am ok with using div's with corner radius to create the circles as they will be styled, and I'm not sure if I can do that with a mix of svg and the canvas element.
My main concern is the stroke that joins the outer circles to the inner, I would like to do this with canvas but am not sure A) How to get multiple canvas elements on one page in one containing element (a wrapper div) and B) how to figure out the starting points, I would assume the ending point would just be the center of the wrapper div (IE if its 600x600 = x=300, y=300)
Can anyone shed some light on this and offer any suggestions? Is there an advantage to using any of the jQuery canvas plugiins over vanilla JS?
thank you!
The canvas API consists of some functions which seem to do the job just fine:
.moveTo/.lineTo for a line path
.arc for a circle path
.stroke to stroke a path (line)
.fill to fill a path (circle)
Here's a very trivial proof of concept: http://jsfiddle.net/eGjak/275/.
I've used (x, y) for both the lines and the circles, which means the lines go from and to the midpoint of two circles. r is the radius of a circle.
var ctx = $('#cv').get(0).getContext('2d');
var circles = [ // smaller circles
{ x: 50, y: 50, r: 25 },
{ x: 250, y: 50, r: 25 },
{ x: 250, y: 250, r: 25 },
{ x: 50, y: 250, r: 25 },
];
var mainCircle = { x: 150, y: 150, r: 50 }; // big circle
function drawCircle(data) {
ctx.beginPath();
ctx.arc(data.x, data.y, data.r, 0, Math.PI * 2); // 0 - 2pi is a full circle
ctx.fill();
}
function drawLine(from, to) {
ctx.beginPath();
ctx.moveTo(from.x, from.y);
ctx.lineTo(to.x, to.y);
ctx.stroke();
}
drawCircle(mainCircle); // draw big circle
$.each(circles, function() { // draw small circles and lines to them
drawCircle(this);
drawLine(mainCircle, this);
});​
You could just do all of these circles in CSS. Get some divs, style them as you like in CSS, and then apply border-radius: 100; to the object, and done. I hope this helped.

Categories

Resources