html5 canvas switch draw functions - javascript

For a project I'm working on I need to draw a solar system on the canvas using the arc function and the lineTo function,and with one button press switch between them. I think I'm close I can switch once but I can't switch back to the original. Sorry for the massive block of code but I've been working on this for hours and I can no longer remember what is important.
if (window.addEventListener)
{
window.addEventListener( 'load', initialise, false);
window.addEventListener('keydown',onKeyDown, false);
}
var canvas;
var context;
var angle = 0;
var time = 20;
var paused = false;
var timer;
var drawStyle = drawArc();
function onKeyDown(event)
{
var keyCode = event.keyCode;
switch(keyCode)
{
case 80: //p
togglePause();
break;
}
switch(keyCode)
{
case 75: //k
toggleDrawStyle();
break;
}
switch(keyCode)
{
case 70: //f
speedUp();
break;
}
switch(keyCode)
{
case 82: //r
speedDown();
break;
}
}
function toggleDrawStyle()
{
if(drawStyle == drawArc())
{
drawStyle = drawLine();
}
else if(drawStyle == drawLine())
{
drawStyle = drawArc();
}
}
function speedUp()
{
time = time / 2;
}
function speedDown()
{
time = time * 2;
}
function togglePause()
{
if (!paused)
{
clearInterval(timer);
paused = true;
}
else
{
timer = setInterval(drawArc, time);
timer = setInterval(drawLine, time);
paused = false;
}
}
function initialise()
{
canvas = document.getElementById('canvas');
if (!canvas.getContext)
{
alert('Error: no canvas.getContext!');
return;
}
context = canvas.getContext('2d');
if (!context)
{
alert('Error: failed to getContext!');
return;
}
timer = setInterval(drawArc, time);
}
function drawArc()
{
clearCanvas();
context.fillStyle = "#000000";
context.fillRect(0, 0, canvas.width, canvas.height);
incrementAngle();
context.save();
//draw the sun
context.translate(canvas.width/2, canvas.height/2);
context.rotate(convertToRadians(angle));
circle(0, 0, 30, "#ffaa00");
//draw mercury
context.save();
context.rotate(convertToRadians(angle)*1.5);
moveCirlce(1/6, 1/6 , 30 , 30, "#555555");
context.restore();
//draw venus
context.save();
context.rotate(convertToRadians(angle)*1.25);
moveCirlce(1/5, 1/5, 40, 40, "#aa0011");
context.restore();
// draw earth
context.save();
context.rotate(convertToRadians(angle));
moveCirlce(0.3, 0.3, 55, 55, "#0077ff");
context.restore();
//draw the moon
context.save();
context.rotate(convertToRadians(angle));
context.translate(55, 55);
context.rotate(convertToRadians(angle) * 2);
moveCirlce(1/15, 1/15, 10, 10, "#555555");
context.restore();
//draw mars
context.save();
context.rotate(convertToRadians(angle) *0.9);
moveCirlce(1/3, 1/3, 80, 80, "#ff0000");
context.restore();
//draw jupiter
context.save();
context.rotate(convertToRadians(angle)* 0.75);
moveCirlce(2/3, 2/3, 110, 110, "#cc8811");
context.restore();
//draw sautrn
context.save();
context.rotate(convertToRadians(angle)*0.55);
moveCirlce(17/30, 17/30, 140, 140, "#dd7722");
context.restore();
//draw uranus
context.save();
context.rotate(convertToRadians(angle)*0.3);
moveCirlce(14/30, 14/30, 170, 170, "#4444ff");
context.restore();
//draw neptune
context.save();
context.rotate(convertToRadians(angle)*0.2);
moveCirlce(11/30, 11/30, 190, 190, "#9999ff");
context.restore();
context.restore();
}
function drawLine()
{
clearCanvas();
context.fillStyle = "#000000";
context.fillRect(0, 0, canvas.width, canvas.height);
incrementAngle();
context.save();
//draw sun
context.translate(canvas.width/2, canvas.height/2);
context.rotate(convertToRadians(angle));
lineCircle(0, 0, 30, "#ffaa00");
//draw mercury
context.save();
context.rotate(convertToRadians(angle)*1.5);
moveLineCirlce(1/6, 1/6 , 30 , 30, "#555555");
context.restore();
//draw venus
context.save();
context.rotate(convertToRadians(angle)*1.25);
moveLineCirlce(1/5, 1/5, 40, 40, "#aa0011");
context.restore();
// draw earth
context.save();
context.rotate(convertToRadians(angle));
moveLineCirlce(0.3, 0.3, 55, 55, "#0077ff");
context.restore();
//draw the moon
context.save();
context.rotate(convertToRadians(angle));
context.translate(55, 55);
context.rotate(convertToRadians(angle) * 2);
moveLineCirlce(1/15, 1/15, 10, 10, "#555555");
context.restore();
//draw mars
context.save();
context.rotate(convertToRadians(angle) *0.9);
moveLineCirlce(1/3, 1/3, 80, 80, "#ff0000");
context.restore();
//draw jupiter
context.save();
context.rotate(convertToRadians(angle)* 0.75);
moveLineCirlce(2/3, 2/3, 110, 110, "#cc8811");
context.restore();
//draw sautrn
context.save();
context.rotate(convertToRadians(angle)*0.55);
moveLineCirlce(17/30, 17/30, 140, 140, "#dd7722");
context.restore();
//draw uranus
context.save();
context.rotate(convertToRadians(angle)*0.3);
moveLineCirlce(14/30, 14/30, 170, 170, "#4444ff");
context.restore();
//draw neptune
context.save();
context.rotate(convertToRadians(angle)*0.2);
moveLineCirlce(11/30, 11/30, 190, 190, "#9999ff");
context.restore();
context.restore();
}
function convertToRadians(degree)
{
return degree*(Math.PI/180);
}
function incrementAngle()
{
angle++;
}
function clearCanvas()
{
canvas.width = canvas.width;
}
function circle(x, y, r , col)
{
context.beginPath();
context.arc(x, y, r, 0, 2*Math.PI);
context.fillStyle = col;
context.fill();
}
function moveCirlce(a, b, x, y, col)
{
context.save();
context.translate(x, y);
context.scale(a, b);
circle(0, 0, 30, col);
context.restore();
}
function lineCircle(a, b, r, col)
{
var theta = Math.PI * 2 / 9;
for (var i = 0; i <= 9; i = i + 1)
{
var angle = theta * i;
var x = a + r * Math.cos(angle);
var y = b + r * Math.sin(angle);
if (i == 0)
{
context.moveTo(x,y);
}
else
{
context.lineTo(x,y);
}
}
context.fillStyle = col;
context.fill();
}
function moveLineCirlce(a, b, x, y, col)
{
context.save();
context.translate(x, y);
context.scale(a, b);
lineCircle(0, 0, 30, col);
context.restore();
}
initialise();
drawArc();

Your switch statements, as written, are nothing more than a bizarre way to write a bunch of if (condition) do_statements; so that's not the problem (although it does mean you aren't using a switch statement as intended; if this is homework for a grade, this might mean some deductions).
A clear indication that something is a muck is that your togglePause fires setInterval with drawArc and then overwrites the timer id by also firing setInterval with drawLine. This means whenever you attempt to clearInterval, you're only clearing the interval for drawLine.
You also have a drawStyle variable, but you aren't really using it for anything, as drawArc and drawLine don't return any values.
So, some hints...
Assign something proper to your drawStyle variable... whether or not you use some flag or the actual functions themselves, I don't care.
handle your setInterval logic better. Hint: with a few tweaks, you could use just use timer = setInterval(function(){drawStyle();}, time); everywhere.
your speed up/slow down functionality is broken. your solar system should work by varying time increments--that is, instead of just incrementing angle by 1, increment it by some variable amount that you can vary.
Finally, after you get the logic worked out, Fix your switch statement

Related

Creating a line animation on canvas

I'm a newbie in canvas drawing. I want to draw the PV string model and the direction of flow of electrons into <canvas> tag.
This is what I want to achieve, redrawing the lines from the following direction:
How do I initially set the animation location, and do I need to update it via setTimeout?
Here is what I try so far:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
// drawing code here
/* First Row */
ctx.fillStyle = "rgb(2,150,224, 1)";
ctx.fillRect(50, 50, 50, 50);
ctx.fillStyle = "rgb(2,150,224, 1)";
ctx.fillRect(110, 50, 50, 50);
ctx.fillStyle = "rgb(188,12,50, 1)";
ctx.fillRect(170, 50, 50, 50);
ctx.fillStyle = "rgb(2,150,224, 1)";
ctx.fillRect(230, 50, 50, 50);
ctx.fillStyle = "rgb(2,150,224, 1)";
ctx.fillRect(290, 50, 50, 50);
/* Second Row */
ctx.fillStyle = "rgb(0,106,160, 1)";
ctx.fillRect(50, 150, 50, 50);
ctx.fillStyle = "rgb(0,106,160, 1)";
ctx.fillRect(110, 150, 50, 50);
ctx.fillStyle = "rgb(0,106,160, 1)";
ctx.fillRect(170, 150, 50, 50);
ctx.fillStyle = "rgb(0,106,160, 1)";
ctx.fillRect(230, 150, 50, 50);
ctx.fillStyle = "rgb(0,106,160, 1)";
ctx.fillRect(290, 150, 50, 50);
/* Paths */
ctx.beginPath();
ctx.lineWidth = "3";
ctx.strokeStyle = "rgb(34,177,76, 1)";
ctx.moveTo(0, 75);
ctx.lineTo(400, 75);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = "10";
ctx.strokeStyle = "rgb(34,177,76, 1)";
ctx.moveTo(400, 75);
ctx.lineTo(400, 175);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = "3";
ctx.strokeStyle = "rgb(34,177,76, 1)";
ctx.moveTo(0, 175);
ctx.lineTo(400, 175);
ctx.stroke();
} else {
// canvas-unsupported code here
}
/* canvas {
border: 1px solid #d3d3d3;
} */
<canvas id="myCanvas" width="400" height="400">
Your browser does not support the HTML5 canvas tag.</canvas>
Any help would be appreciated!
There are many ways to animate this; here's my approach (excerpt; see
JSFiddle for full code):
var lerp = (a, b, t) => a + t * (b - a);
var speed = 0.01;
var time = 0;
var visited = [];
var points = [
{
from: { x: 0, y: 75 },
to: { x: 395, y: 75 }
},
{
from: { x: 395, y: 75 },
to: { x: 395, y: 175 }
},
{
from: { x: 395, y: 175 },
to: { x: 0, y: 175 }
}
];
/* Paths */
ctx.lineWidth = 3;
ctx.strokeStyle = "rgb(34, 177, 76, 1)";
(function update() {
if (points.length) {
visited.push({
x: lerp(points[0].from.x, points[0].to.x, time),
y: lerp(points[0].from.y, points[0].to.y, time)
});
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBoxes(ctx);
ctx.beginPath();
ctx.moveTo(visited[0].x, visited[0].y)
visited.forEach(e => ctx.lineTo(e.x, e.y));
ctx.stroke();
time += speed;
if (time >= 1) {
time = 0;
points.shift();
}
requestAnimationFrame(update);
}
})();
The idea is to keep a data structure of all the turning points, then lerp along the path, drawing a line along the way. Use an easing function instead of lerp if you prefer a more "modern"-looking animation; easing is usually easier to implement and may result in removal of some code (for example, no need to keep track of starting points and time).
Last minor note--your original code was cutting off the line at the right edge of the canvas, so I took the liberty of using 395 instead of 400 for the drawing width.

Click on object to do certain function

I'm programming on JavaScript and basically what I'm supposed to do is to draw a face on a canvas and those faces have certain effects.
One of the effects I'm supposed to do is to click on the nose and it will change the face to an angry face. I have created objects for all the face parts (nose, eyes, etc.) and what I'm trying to do is that I want the function "angryFace" to be called when the nose is clicked. The nose is already an object.
Been stuck for a long time on this. Would appreciate it if someone could help! Uploaded the whole code down here. Thanks guys!
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>COM1008 Assignment 2</title>
<link rel="stylesheet" href="main1.css">
</head>
<main>
<h1>COM 1008 Assignment 2</h1>
<p>By Samuel Fung Chi Lun</p>
</main>
<body>
<canvas id="canvasFrame" width="600" height="600"></canvas>
<p>
<button name="sadbutton" id="sadFace">Sad</button>
<button name="angrybutton" id="angryFace">Angry</button>
<button name="surprisedbutton" id="surprisedFace">Surprised</button>
<button name="neutralbutton" id="neutralFace">Neutral</button>
</p>
<script>
var canvas = document.getElementById('canvasFrame');
var context = canvas.getContext('2d');
//Function to obtain x and y coordinates of mouse positions - Obtained from 20/11/2017 lecture slides
function getMouseXY(e) {
var boundingRect = canvas.getBoundingClientRect();
var offsetX = boundingRect.left;
var offsetY = boundingRect.top;
var w = (boundingRect.width - canvas.width);
var h = (boundingRect.height - canvas.height);
offsetX += w;
offsetY += h;
var mx = Math.round(e.clientX - offsetX);
var my = Math.round(e.clientY - offsetY);
return { x: mx, y: my };
}
const BROWS_UP = 190;
const BROWS_DOWN = 170;
//Creating an object for left eye brow
var leftEyeB = {
draw: function (x1,brows_direction1,x2,brows_direction2) {
context.beginPath();
context.lineWidth = 18;
context.moveTo(x1, brows_direction1);
context.lineTo(x2, brows_direction2)
context.stroke();
}
};
//Creating an object for right eye brow
var rightEyeB = {
draw: function (x1,brows_direction1,x2,brows_direction2) {
context.beginPath();
context.lineWidth = 18;
context.moveTo(x1, brows_direction1);
context.lineTo(x2, brows_direction2)
context.stroke();
}
};
//Creating an object for the head
var faceShape = {
draw: function() {
context.beginPath();
context.fillStyle = "rgb(255,255,0)"
context.lineWidth = 3;
context.arc(300, 300, 200, 0, Math.PI * 2, true);
context.fill();
context.stroke();
}
}
//Creating an object for the eyes
var eyes = {
draw: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.moveTo(255, 250); //LeftEye
context.arc(220, 250, 40, 0, Math.PI * 2, true);
context.moveTo(415, 250);//Right Eye
context.arc(380, 250, 40, 0, Math.PI * 2, true);
context.fill();
context.fill();
context.stroke();
context.beginPath();
context.fillStyle = "rgb(255,255,255)";
context.moveTo(240, 250); //LeftPupil
context.arc(220, 250, 15, 0, Math.PI * 2, true);
context.moveTo(400, 250); //Right Pupil
context.arc(380, 250, 15, 0, Math.PI * 2, true);
context.fill();
context.stroke();
}
}
//Creating an object for the nose
var nose = {
draw: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.moveTo(300, 275);
context.lineTo(275, 325);
context.lineTo(325, 325);
context.fill();
context.closePath();
context.stroke();
}
}
//Creating an object for the mouth
var mouth = {
frown: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.moveTo(305, 427);//Mouth
context.arc(305, 427, 80, 0, Math.PI, true);
context.fill();
context.closePath();
context.stroke();
},
circle: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.arc(300, 400, 50, 0, Math.PI * 2, true);
context.fill();
context.stroke();
},
straight: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.rect(225, 390, 150, 20);//Mouth
context.fill();
context.stroke();
}
}
//Drawing the sad face
function sadFace() {
faceShape.draw();
eyes.draw();
nose.draw();
mouth.frown();
leftEyeB.draw(175,BROWS_DOWN,265,BROWS_DOWN);
rightEyeB.draw(335,BROWS_DOWN,425,BROWS_DOWN);
}
//Drawing the angry face
function angryFace() {
faceShape.draw();
eyes.draw();
nose.draw();
mouth.frown();
leftEyeB.draw(175,BROWS_DOWN,265,BROWS_UP);
rightEyeB.draw(335,BROWS_UP,425,BROWS_DOWN);
}
//Drawing the surprised face
function surprisedFace() {
faceShape.draw();
eyes.draw();
nose.draw();
mouth.circle();
leftEyeB.draw(175,BROWS_UP,265,BROWS_DOWN);
rightEyeB.draw(335,BROWS_DOWN,425,BROWS_UP);
}
//Drawing the neutral face
function neutralFace() {
faceShape.draw();
eyes.draw();
nose.draw();
mouth.circle();
leftEyeB.draw(175,BROWS_DOWN,265,BROWS_DOWN);
rightEyeB.draw(335,BROWS_DOWN,425,BROWS_DOWN);
}
//Not sure how to properly do the bottom part. It does not work at all for the eyebrows one so please help!
function effects() {
//Click on the eyebrows to raise them
if (mousePosition.x > x1 && mousePosition.x < x2 && mousePosition.y < BROWS_UP && mousePosition.y > BROWS_DOWN) {
BROWS_UP += 20;
}
//Click on eye to show angry face
if (mousePosition = on coordinates of the eye) {
angryFace();
}
//Click on nose to show happy face
if (mousePosition = on coordinates of the nose) {
smileFace();
}
}
canvas.addEventListener('click', function(evt) {
effects(evt);
});
neutralFace();
//Linking the face functions to the buttons
var angryButton = document.getElementById("angryFace");
var sadButton = document.getElementById("sadFace");
var surprisedButton = document.getElementById("surprisedFace");
var neutralButton = document.getElementById("neutralFace");
sadButton.addEventListener("click", sadFace);
angryButton.addEventListener("click", angryFace);
surprisedButton.addEventListener("click", surprisedFace);
neutralButton.addEventListener("click", neutralFace);
</script>
</body>
</html>
When you make face, for you, there are different objects(say nose, eyes etc). For DOM, there is only 1 object i.e. Canvas. So when you click, you are clicking on canvas and not nose.
So to achieve this, you will have to play with coordinates. You have to assume the position of every container and on click of canvas, check the coordinates. If the coordinates are within range of some object, fire necessary function.
canvas.addEventListener('click', function(event){
var x = event.clientX, y = event.clientY;
if(x> 280 && x <335 && y > 280 && y <335) {
// nose is clicked.
angryFace();
}
else {
neutralFace();
}
})
Following is a partial sample:
var canvas = document.getElementById('canvasFrame');
var context = canvas.getContext('2d');
//Function to obtain x and y coordinates of mouse positions - Obtained from 20/11/2017 lecture slides
function getMouseXY(e) {
var boundingRect = canvas.getBoundingClientRect();
var offsetX = boundingRect.left;
var offsetY = boundingRect.top;
var w = (boundingRect.width - canvas.width);
var h = (boundingRect.height - canvas.height);
offsetX += w;
offsetY += h;
var mx = Math.round(e.clientX - offsetX);
var my = Math.round(e.clientY - offsetY);
return {
x: mx,
y: my
};
}
const BROWS_UP = 190;
const BROWS_DOWN = 170;
//Creating an object for left eye brow
var leftEyeB = {
draw: function(x1, brows_direction1, x2, brows_direction2) {
context.beginPath();
context.lineWidth = 18;
context.moveTo(x1, brows_direction1);
context.lineTo(x2, brows_direction2)
context.stroke();
}
};
//Creating an object for right eye brow
var rightEyeB = {
draw: function(x1, brows_direction1, x2, brows_direction2) {
context.beginPath();
context.lineWidth = 18;
context.moveTo(x1, brows_direction1);
context.lineTo(x2, brows_direction2)
context.stroke();
}
};
//Creating an object for the head
var faceShape = {
draw: function() {
context.beginPath();
context.fillStyle = "rgb(255,255,0)"
context.lineWidth = 3;
context.arc(300, 300, 200, 0, Math.PI * 2, true);
context.fill();
context.stroke();
}
}
//Creating an object for the eyes
var eyes = {
draw: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.moveTo(255, 250); //LeftEye
context.arc(220, 250, 40, 0, Math.PI * 2, true);
context.moveTo(415, 250); //Right Eye
context.arc(380, 250, 40, 0, Math.PI * 2, true);
context.fill();
context.fill();
context.stroke();
context.beginPath();
context.fillStyle = "rgb(255,255,255)";
context.moveTo(240, 250); //LeftPupil
context.arc(220, 250, 15, 0, Math.PI * 2, true);
context.moveTo(400, 250); //Right Pupil
context.arc(380, 250, 15, 0, Math.PI * 2, true);
context.fill();
context.stroke();
}
}
//Creating an object for the nose
var nose = {
draw: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.moveTo(300, 275);
context.lineTo(275, 325);
context.lineTo(325, 325);
context.fill();
context.closePath();
context.stroke();
}
}
//Creating an object for the mouth
var mouth = {
frown: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.moveTo(305, 427); //Mouth
context.arc(305, 427, 80, 0, Math.PI, true);
context.fill();
context.closePath();
context.stroke();
},
circle: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.arc(300, 400, 50, 0, Math.PI * 2, true);
context.fill();
context.stroke();
},
straight: function() {
context.beginPath();
context.fillStyle = "rgb(0,0,0)";
context.rect(225, 390, 150, 20); //Mouth
context.fill();
context.stroke();
}
}
//Drawing the sad face
function sadFace() {
faceShape.draw();
eyes.draw();
nose.draw();
mouth.frown();
leftEyeB.draw(175, BROWS_DOWN, 265, BROWS_DOWN);
rightEyeB.draw(335, BROWS_DOWN, 425, BROWS_DOWN);
}
//Drawing the angry face
function angryFace() {
faceShape.draw();
eyes.draw();
nose.draw();
mouth.frown();
leftEyeB.draw(175, BROWS_DOWN, 265, BROWS_UP);
rightEyeB.draw(335, BROWS_UP, 425, BROWS_DOWN);
}
//Drawing the surprised face
function surprisedFace() {
faceShape.draw();
eyes.draw();
nose.draw();
mouth.circle();
leftEyeB.draw(175, BROWS_UP, 265, BROWS_DOWN);
rightEyeB.draw(335, BROWS_DOWN, 425, BROWS_UP);
}
//Drawing the neutral face
function neutralFace() {
faceShape.draw();
eyes.draw();
nose.draw();
mouth.circle();
leftEyeB.draw(175, BROWS_DOWN, 265, BROWS_DOWN);
rightEyeB.draw(335, BROWS_DOWN, 425, BROWS_DOWN);
}
function effects() {
mousePosition = getMouseXY(e);
console.log(mousePosition);
}
neutralFace();
canvas.addEventListener('click', function(event){
var x = event.clientX, y = event.clientY;
if(x> 280 && x <335 && y > 280 && y <335) {
// nose is clicked.
angryFace();
}
else {
neutralFace();
}
})
//Linking the face functions to the buttons
var angryButton = document.getElementById("angryFace");
var sadButton = document.getElementById("sadFace");
var surprisedButton = document.getElementById("surprisedFace");
var neutralButton = document.getElementById("neutralFace");
sadButton.addEventListener("click", sadFace);
angryButton.addEventListener("click", angryFace);
surprisedButton.addEventListener("click", surprisedFace);
neutralButton.addEventListener("click", neutralFace);
<!-- This is not the original markup. This is a part of an edit to make the code executable. -->
<canvas id='canvasFrame' width=600 height=600></canvas>
<div>
<button id="angryFace">Angry Face</button>
<button id="sadFace">Sad Face</button>
<button id="surprisedFace">Surprised Face</button>
<button id="neutralFace">Neutral Face</button>
</div>

Center a bezierCurve html5 canvas drawing?

So I have made basic drawings with html5 canvas and the basic shapes you can create have parameters to position the whole shape, below I center a circle centerX and centerY by taking the window size and dividing by 2.
context.beginPath();
context.globalCompositeOperation = 'destination-out';
context.arc(centerX, centerY, radius, Math.PI*2, false);
context.fill();
context.closePath();
The above drawing is nice and centered but now that I am playing with the bezier curve I can't find anything on the web that suggests how to center it.
// some arbitrary example
context.beginPath();
context.moveTo(170, 80);
context.bezierCurveTo(130, 100, 130, 150, 230, 150);
context.bezierCurveTo(250, 180, 320, 180, 340, 150);
context.bezierCurveTo(420, 150, 420, 120, 390, 100);
context.fill();
context.globalCompositeOperation = 'destination-out';
context.closePath();
I wrote up a fiddle so there is something to work with JSFIDDLE. Below is the code pasted directly from my fiddle.
var canvas = document.getElementById("c");
var context = canvas.getContext("2d");
canvas.width = $(window).width();
canvas.height = $(window).height();
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.fillStyle = '#333';
context.fillRect(0, 0, canvas.width, canvas.height);
context.closePath();
// custom shape (weird shape lol)
context.beginPath();
context.globalCompositeOperation = 'destination-out';
context.moveTo(170, 80);
context.bezierCurveTo(130, 100, 130, 150, 230, 150);
context.bezierCurveTo(250, 180, 320, 180, 340, 150);
context.bezierCurveTo(420, 150, 420, 120, 390, 100);
context.fill();
context.closePath();
context.globalCompositeOperation = 'source-over';
}
draw();
Here's one method to accurately center your group of cubic Bezier curves
A Demo: http://jsfiddle.net/m1erickson/6GZmp/
Step#1. Use De Casteljau's algorithm to plot points along each curve in your group of curves.
// De Casteljau's algorithm which calculates points along a cubic Bezier curve
// plot a point at interval T along a bezier curve
// T==0.00 at beginning of curve. T==1.00 at ending of curve
// Calculating 100 T's between 0-1 will usually define the curve sufficiently
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
Step#2. Determine the bounding box of the curve-group by getting the minX,maxX,minY,maxY of the points you plotted in #1. And use max-min to determine the width and height of the curves group.
var curvesWidth = maxX - minX;
var curvesHeight = maxY - minY;
Step#3. Calculate the offset needed in order to center your curves-group.
var offsetX=(canvas.width/2-curvesWidth/2)-curvesLeft;
var offsetY=(canvas.height/2-curvesHeight/2)-curvesTop;
Step#4. Knowing the offsets, you can use context.translate to draw your centered curves.
context.save();
context.translate(offsetX,offsetY);
context.beginPath();
context.moveTo(170, 80);
context.bezierCurveTo(130, 100, 130, 150, 230, 150);
context.bezierCurveTo(250, 180, 320, 180, 340, 150);
context.bezierCurveTo(420, 150, 420, 120, 390, 100);
context.fill();
context.restore();
I don't know if there's a quick way of doing it. My attempt works like this:
you check each point on the x axis and compare it to the other points, if it is the most left or the most right store their position in a variable, otherwise do nothing. Once you have those points you know the width of the whole path and you can calculate an offset value to place it inside the center (because you know the canvas width). Then just add that offset value to the points coordinates and you're good:
http://jsfiddle.net/jonigiuro/8jsw9/4/
var canvas = document.getElementById("c"); var context = canvas.getContext("2d");
canvas.width = $(window).width(); canvas.height = $(window).height();
var centerX = canvas.width / 2; var centerY = canvas.height / 2;
var bezierSteps = [
[130, 100, 130, 150, 230, 150],
[250, 180, 320, 180, 340, 150],
[420, 150, 420, 120, 390, 100]
];
var mostLeft = 2000; var mostRight = 0;
findCenter();
function findCenter() {
for (var i = 0; i < bezierSteps.length; i++) {
for (var p = 0; p < bezierSteps.length; p+=2) {
mostLeft = bezierSteps[i][p] < mostLeft ? bezierSteps[i][p] : mostLeft;
mostRight = bezierSteps[i][p] > mostRight ? bezierSteps[i][p] : mostRight;
}
}
console.log(mostLeft, mostRight) } var offset = (canvas.width - mostLeft - mostRight) / 2;
console.log(offset)
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.fillStyle = '#333';
context.fillRect(0, 0, canvas.width, canvas.height);
context.closePath();
// custom shape (weird shape lol)
context.beginPath();
context.globalCompositeOperation = 'destination-out';
context.moveTo(170 + offset, 80);
for (var i = 0, l = bezierSteps.length ; i < l ; i++) {
context.bezierCurveTo(bezierSteps[i][0] + offset,bezierSteps[i][1],bezierSteps[i][2] + offset,bezierSteps[i][3],bezierSteps[i][4] + offset,bezierSteps[i][5])
}
//context.bezierCurveTo(130, 100, 130, 150, 230, 150);
//context.bezierCurveTo(250, 180, 320, 180, 340, 150);
//context.bezierCurveTo(420, 150, 420, 120, 390, 100);
context.fill();
context.closePath();
context.globalCompositeOperation = 'source-over';
}
draw();
sorry for the dirty code..

How to scale a canvas image larger and smaller in a continuous loop?

I created a cloud shape in canvas, and I'm wondering how I can scale the shape back and forth between larger and smaller. Like I want the cloud to get bigger, than smaller, then bigger then smaller, etc.
I was able to be able to move a separate canvas image up and down using a when-then method, but I don't think that method will work by increasing the canvas size because the actual image stays to scale.
Here is my canvas code:
<script>
var canvas = document.getElementById('hardware-cloud');
var context = canvas.getContext('2d');
// begin cloud shape-Hardware
context.beginPath();
context.moveTo(180, 80);
context.bezierCurveTo(150, 132, 143, 165, 203, 154);
context.bezierCurveTo(203, 154, 180, 200, 260, 175);
context.bezierCurveTo(297, 231, 352, 198, 344, 185);
context.bezierCurveTo(344, 185, 372, 215, 374, 175);
context.bezierCurveTo(473, 165, 462, 132, 429, 110);
context.bezierCurveTo(473, 44, 407, 33, 374, 55);
context.bezierCurveTo(352, 10, 275, 22, 275, 55);
context.bezierCurveTo(210, 20, 165, 22, 180, 80);
// complete cloud shape-Hardware
context.closePath();
context.lineWidth = 5;
context.strokeStyle = 'navy';
context.stroke();
context.fillStyle = 'white';
context.fill();
//font inside hardware cloud
context.beginPath();
context.font = 'bold 15pt Calibri';
context.textAlign = 'center';
context.fillStyle ="navy"; // <-- Text colour here
context.fillText('Why not the electronic store?', 300, 120);
context.lineWidth = 2;
context.strokeStyle = 'grey';
context.stroke();
context.closePath();
//top hardware circle
context.beginPath();
context.arc(380, 220, 13, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = 'navy';
context.stroke();
context.fillStyle = 'white';
context.fill();
context.closePath();
//middle hardware circle
context.beginPath();
context.arc(398, 253, 10, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = 'navy';
context.stroke();
context.fillStyle = 'white';
context.fill();
context.closePath();
//bottom hardware circle
context.beginPath();
context.arc(425, 273, 7, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = 'navy';
context.stroke();
context.fillStyle = 'white';
context.fill();
context.closePath();
</script>
And here is my attempt at the jQuery. The first part of it is to get the div region to slide into view. The second part is an attempt to scale up and down.:
$(document).ready(function(){
$('#hardware').hide();
$('#hardware').show("slide", {direction: "left"}, 400 );
ani();
function ani(){
$.when(
$('#hardware-cloud').effect({
scale: "120"},700),
$('#hardware-cloud').effect({
scale: "100"},700)
.then(ani));}
});
You can use scale and translate to change the size of the shape.
All transforms works for the next drawn shape and doesn't affect already drawn shapes.
So for it to work you'll need to clear the canvas, transform and then redraw the shape.
For example, re-factor the code so that you can call shape() to draw the cloud, then:
Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height);
Apply scale ctx.scale(scaleX, scaleY);
Optionally translate the shape using ctx.translate(deltaX, deltaY);
Redraw shape shape();
Repeat using for example requestAnimationFrame() in a loop.
Scale value is 1 = 1:1, 0.5 = half etc. Just remember these transforms are accumulative (you can use setTransform() to set absolute transforms each time).
Update
Here is one way you can do this:
var maxScale = 1, // for demo, this represents "max"
current = 0, // angle (in radians) used to scale smoother
step = 0.02, // speed
pi2 = Math.PI; // cached value
// main loop clears, transforms and redraws shape
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
transform();
drawShape();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
// set scale based on rotation
function transform() {
current += step;
current %= pi2;
// just play around with different combinations here
var s = (maxScale * Math.abs(Math.sin(current))) / maxScale + 0.5;
// set absolute scale
context.setTransform(s, 0, 0, s, 0, 0);
}
// wrap shape calls in a function so it can be reused
function drawShape() {
// begin cloud shape-Hardware
context.beginPath();
... rest goes here...
Online demo
In addition you can use translate() to re-position the shape linked to the rotation value.
Hope this helps!

Rotation of canvas not happening as per my expectation

I want my rectangle to rotate in place. But currently it is rotating as though there is one object at center and my object is revolving around it i.e much like what we see in solar system. I want my object to rotate in place not revolve around something. How can I do that?
This is my code so far:
<script type="text/javascript">
var context;
var radian = 0.01;
var w, h;
$(document).ready(function () {
w = document.width;
h = document.height;
var canvas = $('#canvas');
context = canvas[0].getContext('2d');
canvas[0].width = w;
canvas[0].height = h;
setInterval(startAnim, 10);
});
function startAnim() {
context.save();
context.strokeStyle = 'rgb(0,0,0)';
context.fillStyle = 'rgb(0,0,0)';
context.fillRect(0, 0, w, h);
context.strokeStyle = 'rgb(0,0,0)';
context.fillStyle = 'rgb(255,255,0)';
context.strokeRect(0, 0, 200, 200);
context.fillRect(0, 0, 200, 200);
context.restore();
context.fillStyle = 'rgb(0,255,255)';
context.fillRect(500, 400, 200, 200);
radian += 0.01;
}
</script>
UPDATE: Sorry I was experimenting before I posted and I forgot to add rotate function while posting here. This is the actual code:
function startAnim() {
context.save();
context.strokeStyle = 'rgb(0,0,0)';
context.fillStyle = 'rgb(0,0,0)';
context.fillRect(0, 0, w, h);
context.translate(350, 300);
context.rotate(radian);
context.translate(-350, -300);
context.strokeStyle = 'rgb(0,0,0)';
context.fillStyle = 'rgb(255,255,0)';
context.strokeRect(0, 0, 200, 200);
context.fillRect(0, 0, 200, 200);
context.restore();
context.fillStyle = 'rgb(0,255,255)';
context.fillRect(500, 400, 200, 200);
radian += 0.01;
}
Use translate(x, y), rotate(r), and then draw your rectangle.
function startAnim() {
context.save();
context.strokeStyle = 'rgb(0,0,0)';
context.fillStyle = 'rgb(0,0,0)';
context.fillRect(0, 0, w, h);
//^Shouldn't clearRect() be better here?
//Draw translated, rotated rectangle at (250, 250)
var x = 250;
var y = 250;
context.save();
context.translate(x, y);
context.rotate(radian);
context.fillStyle = 'rgb(255,255,0)';
context.fillRect(0, 0, 200, 200);
context.restore();
//Restore context (to reverse translation and rotation)
context.strokeStyle = 'rgb(0,0,0)';
context.fillStyle = 'rgb(255,255,0)';
context.strokeRect(0, 0, 200, 200);
context.fillRect(0, 0, 200, 200);
context.restore();
context.fillStyle = 'rgb(0,255,255)';
context.fillRect(500, 400, 200, 200);
radian += 0.01;
}
jsFiddle demo and Simple jsFiddle demo with one rotating rectangle
You have to translate your context to the center of the rectangle before you rotate it:
context.translate(RECT_CENTER_X, RECT_CENTER_Y);
context.rotate(radian);
context.fillRect(-RECT_CENTER_X, -RECT_CENTER_Y, w, h);

Categories