<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
#map{border: 2px solid black}
</style>
<script>
window.onload = function(){
var canvas = document.getElementById("map"),
c = canvas.getContext("2d");
c.fillStyle = "white";
c.fillRect(0, 0, canvas.width, canvas.height);
/*aisles*/
c.fillStyle = "#009900";
c.fillRect (20,90,20,250);
c.fillRect (70,90,20,250);
c.fillRect (120,90,20,250);
c.fillRect (170,90,20,250);
c.fillRect (220,90,20,250);
c.fillRect (270,90,20,250);
c.fillRect (470,90,20,250);
c.fillRect (520,90,20,250);
c.fillRect (570,90,20,250);
c.fillRect (620,90,20,250);
c.fillRect (670,90,20,250);
c.fillRect (720,90,20,250);
c.fillRect (770,90,20,250);
c.fillStyle = "#0066cc";
c.fillRect (320,90,20,250);
c.fillRect (370,90,20,250);
c.fillRect (420,90,20,250);
/*sections*/
c.fillStyle = "#009900";
c.fillRect (700, 400,200,50);
c.fillRect (850,0,50,300);
c.fillRect (850, 365, 50, 85);
c.fillRect (175,0,555,50);
c.fillRect (0,0,150,50 );
/*section names*/
c.fillStyle = "white";
c.font = "25px Arial";
c.fillText("Dairy" ,45,30);
c.fillText("-----Meat------", 375, 30);
c.fillText("Produce",750, 435);
c.fillText("B", 865, 90);
c.fillText("a", 865, 115);
c.fillText("k", 865, 140);
c.fillText("e", 865, 165);
c.fillText("r", 865, 190);
c.fillText("y", 865,215);
/*aisle numbers*/
c.fillStyle = "white";
c.font = "12px Arial";
c.fillText("16", 22, 210);
c.fillText("15", 72, 210);
c.fillText("14", 122, 210);
c.fillText("13", 172, 210);
c.fillText("12", 222, 210);
c.fillText("11", 272, 210);
c.fillText("10", 322, 210);
c.fillText("9", 376, 210);
c.fillText("8", 426, 210);
c.fillText("7", 476, 210);
c.fillText("6", 526, 210);
c.fillText("5", 576, 210);
c.fillText("4", 626, 210);
c.fillText("3", 676, 210);
c.fillText("2", 726, 210);
c.fillText("1", 776, 210);
c.beginPath();
c.fillStyle = "#009900";
c.arc(550,450,50,0,2,true);
c.fill();
c.beginPath();
c.fillStyle = "#009900";
c.arc(200,450,50,0,2,true);
c.fill();
/*animation sequence*/
var posX = 550;
var posY = 450;
setInterval(function(){
posX += 1;
if(posX >= 540){
posY += -1;
}
c.fillStyle = "red";
c.beginPath();
c.arc(posX,posY, 5, 0, Math.PI*2, false);
c.fill();
},30);
};
</script>
<title>Canvas Map</title>
</head>
<body>
<canvas id="map" width="900" height="450">
<img src="images/sad dinosaur.jpg" />
You will need an updated browser to view this page!
(Chrome,Firefox, etc...)
</canvas>
</body>
</html>
I am trying to make an animation where the red circle will go up and down the aisles(like a maze) without painting over them. I have been trying to use an if/else statement to enforce the directions of the animation. However, when I try and use a second if statement to alter the circles course it starts my circle off at that coordinate point.
This is one way:
Make an array of line coordinates that you wish to animate along.
Calculate waypoints along those lines where you want your circle to visit and save them in an array.
Create an animation loop.
Inside the loop, (1) clear the canvas, (2) draw the isles, (3) draw the circle at the next point in the array.
Here's example code and a Demo:
// canvas and context references
var canvas = document.getElementById("map");
var c = canvas.getContext("2d");
// set some context styles
c.fillStyle = "white";
c.fillRect(0, 0, canvas.width, canvas.height);
c.fillStyle = 'red';
var startTime;
var interval = 50;
// define lines that go up/down the isles
var lines = []
lines.push({
x: 553,
y: 454
});
lines.push({
x: 672,
y: 378
});
lines.push({
x: 815,
y: 368
});
lines.push({
x: 812,
y: 70
});
lines.push({
x: 752,
y: 71
});
lines.push({
x: 761,
y: 365
});
lines.push({
x: 708,
y: 364
});
lines.push({
x: 703,
y: 76
});
lines.push({
x: 204,
y: 72
});
lines.push({
x: 200,
y: 454
});
// calculate points at intervals along each line
// put all the calculated points in a points[] array
var points = [];
var pointIndex = 0;
for (var i = 1; i < lines.length; i++) {
var line0 = lines[i - 1];
var line1 = lines[i];
for (var j = 0; j < 100; j++) {
var dx = line1.x - line0.x;
var dy = line1.y - line0.y;
var x = line0.x + dx * j / 100;
var y = line0.y + dy * j / 100;
points.push({
x: x,
y: y
});
}
}
var img = new Image();
img.onload = start;
img.src = "https://dl.dropboxusercontent.com/u/139992952/multple/isles.png";
function start() {
requestAnimationFrame(animate);
}
function animate(time) {
// continue animating until we've reach the last point in points[]
if (pointIndex < points.length - 1) {
requestAnimationFrame(animate);
}
// get the current point
var p = points[pointIndex];
// clear the canvas
c.clearRect(0, 0, canvas.width, canvas.height);
// draw the isles
c.drawImage(img, 0, 0);
// draw the circle at the current waypoint
c.beginPath();
c.arc(p.x, p.y, 5, 0, Math.PI * 2);
c.closePath();
c.fill();
// increment the pointIndex for the next animation loop
pointIndex++;
}
<canvas id="map" width="900" height="450">
<img src="images/sad dinosaur.jpg" />
You will need an updated browser to view this page!
(Chrome,Firefox, etc...)
</canvas>
It looks to me like what you need is a state machine.
Currently, your shopper keeps track of two variables, which, between them, tell them where it is on the canvas. A state machine essentially introduces a set of tasks that the shopper has to perform, and then the shopper remembers which task it's performing at the moment. The simplest way to handle this would be with a switch statement in your animation callback. An abbreviated version might look like this:
/*animation sequence*/
var posX = 550;
var posY = 450;
var state = "enter";
setInterval(function () {
switch(state) {
case "enter":
if (posY > 390) {
posY -= 1;
} else {
state = "seekProduce";
}
break;
case "seekProduce":
if (posX < 840) {
posX += 1;
} else {
state = "seekBakery";
}
break;
/* ... */
}
}, 4);
I've set up a jsFiddle with something like what you're aiming for. In this case, the shopper has seven tasks, but is only ever doing one of them at a time.
Enter the store (just go up a little bit; when you're done, seek the produce section)
Seek the produce section (go right until you're close to the section; when done, seek the bakery)
Seek the bakery (go up quite a way, then seek the next aisle)
Seek the next aisle (go left a bit, then, if you're high up, go down the aisle, otherwise go up the aisle)
Go up an aisle (go up until you're out of the aisle, then seek the next aisle)
Go down an aisle (go down until you're out of the aisle; if there are no more aisles then seek the exit, otherwise seek the next aisle)
Seek the exit (go down and left until you're at the right spot, then we're done).
Except for leaving the store, each task is set up to go to another task when finished. I use a switch statement to decide how to move, given whatever task is current. My implementation is pretty crude -there's lots of room to improve the code- but it should demonstrate the general idea.
Related
I have a large array of objects to draw on a canvas.
The center of the objects needs to blend with standard alpha.
The border (stroke) of later objects needs to appear to remove any underlying borders while leaving the fill intact for blending.
An example as a code snippet with a couple of failed attempts - please note the 'desired' outcome was produced manually.
The solution needs to scale too as this is for a requestAnimationFrame and there will be thousands of objects to iterated over so performing individual beginPath()/stroke() combinations isn't likely to be viable.
var canvas = document.getElementById('canvas');
canvas.width = 600;
canvas.height = 600;
var ctx = canvas.getContext('2d');
//set up control objects
let objects = [{
x: 20,
y: 20,
w: 60,
h: 30,
rgba: "rgba(255, 0,0,.5)"
}, {
x: 40,
y: 30,
w: 60,
h: 30,
rgba: "rgba(0,255,0,.5)"
}]
//manually produce desired outcome
ctx.beginPath();
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y, myObject.w, myObject.h);
}
ctx.beginPath();
ctx.moveTo(40, 50);
ctx.lineTo(20, 50);
ctx.lineTo(20, 20);
ctx.lineTo(80, 20);
ctx.lineTo(80, 30);
ctx.rect(40, 30, 60, 30);
ctx.stroke();
ctx.font = "15px Arial"
ctx.fillStyle = "black";
ctx.fillText("Desired outcome - (done manually for example)", 120, 50);
//Attempt one: fill on iterate, stroke on end
ctx.beginPath();
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.rect(myObject.x, myObject.y + 70, myObject.w, myObject.h);
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y + 70, myObject.w, myObject.h);
}
ctx.stroke();
ctx.fillStyle = "black";
ctx.fillText("Attempt #1: inner corner of red box fully visible", 120, 120);
//Attempt two: fill and stroke on iterate
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.beginPath();
ctx.rect(myObject.x, myObject.y + 140, myObject.w, myObject.h);
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y + 140, myObject.w, myObject.h);
ctx.stroke();
}
ctx.fillStyle = "black";
ctx.fillText("Attempt #2: inner corner of red box partly visible", 120, 170);
ctx.fillText("(This also scales very badly into thousands of strokes)", 120, 190);
<canvas name="canvas" id="canvas" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
You can achieve this by drawing in two passes:
First you will composite your strokes, by iteratively
clearing each previous strokes where the filling would fall, using globalCompositeOperation = "destination-out"
draw this rect's stroke
When this is done, your canvas will only have the final strokes remaining.
Now you have to draw the fills, but since we want the strokes to be in front of the fills, we have to use an other compositing mode: "destination-over" and to iterate our rects in reversed order:
(async () => {
var canvas = document.getElementById('canvas');
canvas.width = 600;
canvas.height = 600;
var ctx = canvas.getContext('2d');
ctx.scale(2,2)
//set up control objects
let objects = [{
x: 20,
y: 20,
w: 60,
h: 30,
rgba: "rgba(255, 0,0,.5)"
}, {
x: 40,
y: 30,
w: 60,
h: 30,
rgba: "rgba(0,255,0,.5)"
},
{
x: 10,
y: 5,
w: 60,
h: 30,
rgba: "rgba(0,0,255,.5)"
}]
// first pass, composite the strokes
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.beginPath();
ctx.rect(myObject.x, myObject.y, myObject.w, myObject.h);
// erase the previous strokes where our fill will be
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#000"; // must be opaque
ctx.fill();
// draw our stroke
ctx.globalCompositeOperation = "source-over";
ctx.stroke();
}
await wait(1000);
// second pass, draw the colored fills
// we will draw from behind to keep the stroke at frontmost
// so we need to iterate our objects in reverse order
for (let i = objects.length- 1; i >= 0; i--) {
let myObject = objects[i];
// draw behind
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y, myObject.w, myObject.h);
}
})();
function wait(ms){
return new Promise(res => setTimeout(res, ms));
}
<canvas name="canvas" id="canvas" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
And of course, this can be used in an animation too:
var canvas = document.getElementById('canvas');
canvas.width = 100;
canvas.height = 100;
var ctx = canvas.getContext('2d');
//set up control objects
let objects = [{
x: 20,
y: 20,
w: 60,
h: 30,
rgba: "rgba(255, 0,0,.5)"
}, {
x: 40,
y: 30,
w: 60,
h: 30,
rgba: "rgba(0,255,0,.5)"
},
{
x: 10,
y: 5,
w: 60,
h: 30,
rgba: "rgba(0,0,255,.5)"
}]
objects.forEach( rect => {
rect.speedX = Math.random() * 2 - 1;
rect.speedY = Math.random() * 2 - 1;
});
requestAnimationFrame(anim);
onclick = anim
function anim() {
update();
draw();
requestAnimationFrame( anim );
}
function update() {
objects.forEach( rect => {
rect.x = rect.x + rect.speedX;
rect.y = rect.y + rect.speedY;
if(
rect.x + rect.w > canvas.width ||
rect.x < 0
) {
rect.speedX *= -1;
}
if(
rect.y + rect.h > canvas.height ||
rect.y < 0
) {
rect.speedY *= -1;
}
});
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// first pass, composite the strokes
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.beginPath();
ctx.rect(myObject.x, myObject.y, myObject.w, myObject.h);
// erase the previous strokes where our fill will be
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#000"; // must be opaque
ctx.fill();
// draw our stroke
ctx.globalCompositeOperation = "source-over";
ctx.stroke();
}
// second pass, draw the colored fills
// we will draw from behind to keep the stroke at frontmost
// so we need to iterate our objects in reverse order
for (let i = objects.length- 1; i >= 0; i--) {
let myObject = objects[i];
// draw behind
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y, myObject.w, myObject.h);
}
ctx.globalCompositeOperation = "source-over";
}
<canvas name="canvas" id="canvas" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
How do I emulate Gravity with canvas Objects having only a few variables to work with.
I created the base canvas, game, time, score, everything, even gameStates, but I'm stuck on the part where you "add the velocity and the gravity factor into the Player Y variables".
I tried multiplying the gravity factor by a specified value, and then adding it to the yVel and then adding that to the actual Y value, but I cant translate the positioning correctly.
I think if I figured out how to "create gravity" creating jumping, moving right, and moving left wouldnt be too difficult.
Heres the main code im using to look for the platforms:
map.plates represents an array full of arrays which each contain 4 values for a plate (platform plate)
e is the map.plates.Arrays. playY is basically the player's exactly Y Height,, all rendered into fillRect();
function detectGravity() {
map.plates.forEach(e => {
if (playY > e[1] && playX <= e[0] && playX >= e[0] + e[2]) {
} else {
playY += 0; // Gravity Calculations here
}
});
}
I dont really know if I should include anything else here, but if you want the whole project, see the snippet below.
If theres anything wrong with the question, please tell me, I havent been on here in nearly half a year.
Full code incase codepen dies (suggested in comments):
"esversion: 6";
const can = document.querySelector(".block"),
ctx = can.getContext("2d"),
mScore = 100,
map = {
plates: [
[25, 25, 25, 2],
[75, 25, 25, 2],
[125, 25, 25, 2],
[175, 25, 25, 2],
[225, 25, 25, 2],
[25, 75, 25, 2],
[75, 62, 25, 2],
[125, 50, 25, 2],
[175, 38, 25, 2],
[25, 87, 25, 2],
[75, 100, 25, 2]
],
moneys: [
[25, 25],
[125, 25],
[225, 25],
[75, 62],
[75, 100]
],
player: [25, 25, 2, 2],
badSpt: []
};
let score = 0,
time = 60,
gameOn = 0;
let playX,
playY,
velX,
velY,
grav = 1.05;
can.addEventListener("click", startGame);
function startGame() {
if (gameOn != 1) {
gameOn = 1;
init();
gameTime = setInterval(() => {
if (time != 0) {
time -= 1;
}
}, 1000);
}
}
function init() {
can.width = 300;
can.height = 300;
drawEnviron();
drawLevel();
drawPlayer();
drawGame();
drawPixels();
if (time == 0) {
clearInterval(gameTime);
time = 60;
gameOn = 2;
}
window.requestAnimationFrame(init);
}
function drawEnviron() {
with (ctx) {
fillStyle = "#000000";
fillRect(0, 0, can.width, can.height);
fillStyle = "rgba(255, 255, 255, 0.5)";
fillRect(0, 0, can.width, can.height);
fillStyle = "#000000";
fillRect(0, 0, can.width, can.height / 15);
fillStyle = "#ffffff";
font = can.height / 15 + "px Verdana";
fillText("Score: " + score + "/" + mScore, 1, can.height / 19);
fillText("Time: " + time, can.width / 1.5 + 6, can.height / 19);
}
}
function drawLevel() {
map.plates.forEach(e => {
ctx.fillStyle = "#ffffff";
ctx.fillRect(e[0], can.height / 15 + e[1], e[2], e[3]);
});
map.moneys.forEach(e => {
ctx.beginPath();
ctx.fillStyle = "#fcba03";
ctx.arc(e[0] + 12.5, e[1] + 12.5, 4, 0, 2 * Math.PI);
ctx.fill();
});
}
function detectGravity() {
map.plates.forEach(e => {
if (playY > e[1] && playX <= e[0] && playX >= e[0] + e[2]) {
} else {
playY += 0;
}
});
}
function drawPlayer() {
const a = map.player;
if (gameOn == 0 || gameOn == 2) {
playX = a[0];
playY = a[1];
velX = 0;
velY = 0;
}
ctx.beginPath();
ctx.fillStyle = "#ff0000";
ctx.arc(playX + 12.5, playY + 12.5, 4, 0, 2 * Math.PI);
ctx.fill();
}
function drawGame() {
if (gameOn == 0) {
can.style.animation = "none";
with (ctx) {
fillStyle = "rgba(0, 0, 0, 0.5)";
fillRect(0, 0, can.width, can.height);
strokeStyle = "#000000";
lineWidth = 5;
fillStyle = "#ffffff";
textAlign = "center";
strokeText("Click to Start", 150, can.height / 4);
fillText("Click to Start", 150, can.height / 4);
}
} else if (gameOn == 2) {
can.style.animation = "0.2s flash infinite";
with (ctx) {
fillStyle = "rgba(0, 0, 0, 0.5)";
fillRect(0, 0, can.width, can.height);
strokeStyle = "#000000";
lineWidth = 5;
fillStyle = "#ff0000";
textAlign = "center";
strokeText("-- Game Over --", 150, can.height / 4);
fillText("-- Game Over --", 150, can.height / 4);
}
} else {
can.style.animation = "none";
}
}
function drawPixels() {
var fw = (can.width / 2) | 0,
fh = (can.height / 2) | 0;
ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.msImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = false;
ctx.drawImage(can, 0, 0, fw, fh);
ctx.drawImage(can, 0, 0, fw, fh, 0, 0, can.width, can.height);
}
init();
* {
box-sizing: border-box;
overflow: hidden;
}
.block {
border: 2px solid black;
}
#keyframes flash {
0%, 100% {
border: 2px solid black;
}
50% {
border: 2px solid red;
}
}
<canvas class="block"></canvas>
Simple step based gravity.
Gravity
Gravity manifests as a change in speed over time (acceleration). It has a direction and magnitude (a vector)
We define the gravity vector going down the canvas
const gravity = {x: 0, y: 1};
Normally we apply gravity over time units of seconds. This is not a handy unit for animation. In this case we can define it as pixels per frame. A frame is 1/60th of a second. Thus the gravity defined above having a magnitude of 1 pixel per tick squared. So in one second an object would be traveling at 60 pixels per tick or 3600 pixels per second.
This is a little too fast for most animations so we can slow it down somewhat
const gravity = {x: 0, y: 0.1};
The object
An object has a position (a coordinate) and a velocity (a vector) having a direction and magnitude.
const object = {
pos: {x: 0, y: 0}, // position
vel: {x, 0, y: 0}, // velocity
}
To simulate gravity on this object we can add a behavior in the form of a function. In this case we can call it update. In the update function we accelerate the object, by adding the gravity vector to the velocity vector (object.vel). Then we update the position by adding the velocity vector object.vel to the position coordinate object.pos
const gravity = {x: 0, y: 0.1};
const object = {
pos: {x: 0, y: 0}, // position
vel: {x, 0, y: 0}, // velocity
update() {
this.vel.x += gravity.x;
this.vel.y += gravity.y;
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
}
}
The world
By its self this object will fall forever so we need to have it interact with the world. We can define a ground line. In its most basic a line at a y position on the canvas.
const ground = ctx.canvas.height; // ground at bottom of canvas.
To interact we need to add to the objects update function. In this we check the object position against the ground. If the position is below the ground, we move the position up away from the ground the same distance it has moved into the ground, and reverse the velocity (bounced).
We can define the springyness of the ground as a fraction of the velocity.
We also need to give the object a size.
Thus we get.
const gravity = {x: 0, y: 0.1};
const ground = ctx.canvas.height; // ground at bottom of canvas.
const bounce = 0.5;
const object = {
pos: {x: 0, y: 0}, // position
vel: {x, 0, y: 0}, // velocity
size: {w: 10, h: 10},
update() {
this.vel.x += gravity.x;
this.vel.y += gravity.y;
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
const g = ground - this.size.h; // adjust for size
if(this.pos.y >= g) {
this.pos.y = g - (this.pos.y - g); //
this.vel.y = -Math.abs(this.vel.y) * bounce; // change velocity to moving away.
}
}
}
Then all that is needed is to call update every frame and draw the object at the correct position.
Demo
Putting it into practice.
A simple box called object falls from the top of the canvas and hits the ground (bottom of the canvas) bounces a bit and stop. (Click to reset)
Update: I forgot to check if the object is at rest.
The math will have the box vibrate and never really stop moving if we don't add a little extra code to update.
The box will now appear to come to a complete stop when its bounce is less than gravity. See comment // check for rest.
const ctx = canvas.getContext("2d");
canvas.width = innerWidth-4;
canvas.height = innerHeight-4;
requestAnimationFrame(mainLoop); // starts the animation
const gravity = {x: 0, y: 0.1};
const ground = ctx.canvas.height; // ground at bottom of canvas.
const bounce = 0.9; // very bouncy
const object = {
pos: {x: ctx.canvas.width / 2, y: 0}, // position halfway on canvas
vel: {x: 0, y: 0}, // velocity
size: {w: 10, h: 10},
update() {
this.vel.x += gravity.x;
this.vel.y += gravity.y;
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
const g = ground - this.size.h; // adjust for size
if(this.pos.y >= g) {
this.pos.y = g - (this.pos.y - g); //
this.vel.y = -Math.abs(this.vel.y) * bounce;
if (this.vel.y >= -gravity.y) { // check for rest.
this.vel.y = 0;
this.pos.y = g - gravity.y;
}
}
},
draw() { ctx.fillRect(this.pos.x, this.pos.y, this.size.w, this.size.h) },
reset() { this.pos.y = this.vel.y = this.vel.x = 0 },
}
function mainLoop() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
object.update(); // move object
object.draw();
requestAnimationFrame(mainLoop);
}
canvas.addEventListener("click", object.reset.bind(object));
body {
margin: 0px;
padding: 0px;
}
canvas {
position: absolute;
top: 0px;
left: 0px;
border: 1px solid black;
}
<canvas id="canvas"></canvas>
The code below does not draw a circle and after one more stroke (when I press run from the HTML page), it paints the previous drawn function to red and still does not display the circle as intended.
I refered to this question and it still did not fix my problem.
var canvas = document.getElementById("mycanvas");
var c = canvas.getContext("2d");
var c1 = canvas.getContext("2d");
canvas.height = $canvasDiv.innerHeight();
canvas.width = $canvasDiv.innerWidth();
function crtanjeGrede(l)
{
var oslonac1 = parseInt(l) + 30;
c.beginPath();
c.fillRect(30, 30, l, 5);
c.beginPath();
c.moveTo(oslonac1, 35);
c.lineTo(oslonac1 + 25, 45);
c.lineTo(oslonac1 - 25, 45);
c.lineTo(oslonac1, 35);
c.moveTo(oslonac1 + 25, 50);
c.lineTo(oslonac1 - 25, 50);
c.stroke();
c.beginPath();
c.moveTo(30, 35);
c.stroke();
c.lineTo(55, 45);
c.stroke();
c.lineTo(5, 45);
c.stroke();
c.lineTo(30, 35);
c.stroke();
c.moveTo(30, 55);
c.lineTo(30, 400);
c.moveTo(oslonac1, 55);
c.lineTo(oslonac1, 400);
c.closePath();
c.stroke();
}
function crtanjeGerbera1(q){
var gerber1 = parseInt(q) + 30;
c1.beginPath();
c1.arc(q, 32, 44, 0, 2 * Math.PI); // length is inserted by user (variable q) and
// the starting point Y has to be directly on rectangle (has to be lined over the
// previous maded rectangle, that's why I wrote 32)
c1.fillStyle = 'red';
c1.fill();
c1.lineWidth = 1;
c1.strokeStyle = 'red';
c1.stroke();
c1.closePath();
}
crtanjeGrede(duzina);
crtanjeGerbera1();
So I am totally new to canvas and trying a project in which I need to make small balls move around with their background as images. Following code is what I am trying right now.
ctx.beginPath();
ctx.arc(
this.pos[0], this.pos[1], this.radius, 0, 2 * Math.PI, true
);
let tempCanvas = document.createElement("canvas"),
tCtx = tempCanvas.getContext("2d");
let ballbackground = new Image();
if (this.color === "green") {
ballbackground.src = "https://s26.postimg.cc/fl2vwj1mh/greenball.png";
}
else if (this.color === "yellow") {
ballbackground.src = "https://s26.postimg.cc/if61a18yh/yellowball.png";
}
else if (this.color === "blue") {
ballbackground.src = "https://s26.postimg.cc/xb4khn7ih/blueball.jpg";
}
tempCanvas.width = 50;
tempCanvas.height = 50;
tCtx.drawImage(ballbackground,0,0,ballbackground.width, ballbackground.height,0,0,50,50);
ctx.fillStyle = ctx.createPattern(tempCanvas, "repeat");
And for moving those balls I do as follows:
const velocityScale = timeDelta / NORMAL_FRAME_TIME_DELTA,
offsetX = this.vel[0] * velocityScale * this.speed,
offsetY = this.vel[1] * velocityScale * this.speed;
this.pos = [this.pos[0] + offsetX, this.pos[1] + offsetY];
However, the problem is when objects move they seem like sliding over background image like so:
If I try "no-repeat" with createPattern, the balls won't display at all.
What I want is those balls with background images moving on the canvas?
move the balls by using the canvas transform?
const ctx = document.querySelector("canvas").getContext("2d");
const pattern = createPattern(ctx);
function drawCircleByPosition(ctx, x, y) {
ctx.beginPath();
ctx.arc(x, y, 50, 0, Math.PI * 2, true);
ctx.fill();
}
function drawCircleByTransform(ctx, x, y) {
ctx.save();
ctx.translate(x, y);
ctx.beginPath();
ctx.arc(0, 0, 50, 0, Math.PI * 2, true);
ctx.fill();
ctx.restore();
}
function render(time) {
time *= 0.001;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.fillStyle = pattern;
drawCircleByPosition(ctx, 90, 75 + Math.sin(time) * 50);
drawCircleByTransform(ctx, 210, 75 + Math.sin(time) * 50);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function createPattern(ctx) {
const tCtx = document.createElement("canvas").getContext("2d");
tCtx.canvas.width = 50;
tCtx.canvas.height = 50;
tCtx.fillStyle = "yellow";
tCtx.fillRect(0, 0, 50, 50);
for (let x = 0; x < 50; x += 20) {
tCtx.fillStyle = "red";
tCtx.fillRect(x, 0, 10, 50);
tCtx.fillStyle = "blue";
tCtx.fillRect(0, x, 50, 10);
}
return ctx.createPattern(tCtx.canvas, "repeat");
}
canvas { border: 1px solid black; }
<canvas></canvas>
note that rather than call save and restore you can also just set the transform with setTransform which is probably faster since save and restore saves all state (fillStyle, strokeStyle, font, globalCompositeOperation, lineWidth, etc...).
You can either pass in your own matrix. Example
ctx.setTransform(1, 0, 0, 1, x, y); // for translation
and/or you can reset it to the default whenever and then use the standard transform manipulation functions
ctx.setTransform(1, 0, 0, 1, 0, 0); // the default
ctx.translate(x, y);
or whatever combination of things you want to do.
I have draw the Bezier Curve. But I Want Animate the Bezier Curve in onload for 1 time.
Please help me..
Here the below code:
window.onload = function() {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var x=10, y=25;
function curve() {
context.beginPath();
context.moveTo(214, 0);
context.bezierCurveTo(328, 80, 153, 82, 216, 162);
context.lineWidth = 10;
context.strokeStyle = 'gray';
context.stroke();
}
curve();
};
<body>
<canvas id="canvas" width="300" height="300" style="text-align: left; border: 1px solid black; position: absolute; left: 100px;"></canvas>
</body>
Curve draw on 0 to (328, 80, 153, 82, 216, 162)
You can cheat to get the effect, although the animation does not bend according to the line
(you have to stop the timer and this might not work if there are other drawn object near it)
http://jsfiddle.net/o9k83k0g/
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var x=10, y=25;
var show=0;
function curve() {
context.beginPath();
context.moveTo(214, 0);
context.bezierCurveTo(328, 80, 153, 82, 216, 162);
context.lineWidth = 10;
context.strokeStyle = 'gray';
context.stroke();
context.beginPath();
context.fillStyle = 'white';
context.fillRect(160,0+show,100,175-show);
context.stroke();
show=show+1;
}
setInterval(function(){curve();}, 30);
If you want to animate the curve, you can structurate your code like that :
var snake = {
points:[ {x:100,y:100},{x:100,y:150},{x:100,y:200},{x:100,y:250} ]
}
function loop(){
physic( snake )
display( snake )
}
setInterval( loop, 20 )
In the physic function, you update points in the snake structure according to what you want.
In the display function you do something like that :
function display( snake ){
var point = snake.points[0];
ctx.beginPath();
ctx.lineWidth = 10;
ctx.moveTo( point.x, point.y );
for( i=0; i< snake.points.length-1; i++ ){
point = snake.points[i];
var xc = (point.x + snake.points[i + 1].x) / 2;
var yc = (point.y + snake.points[i + 1].y) / 2;
ctx.quadraticCurveTo( point.x, point.y, xc, yc );
}
ctx.stroke();
}
The difficult part is the physic function.
function physic( snake ){
snake.points[2].x++ // An example
}