HTML5 - frame by frame animation not working - javascript

I have this frame animation where each frame is called at every 1/30s.
Canvas is simply not being cleared properly. Why?
Here is the code:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title></title>
<script src="http://code.createjs.com/easeljs-0.4.2.min.js" ></script>
<script src="http://code.createjs.com/tweenjs-0.2.0.min.js" ></script>
<script>
var canvas;
var stage;
var screen_width;
var screen_height;
var bmpAnimation;
// this is a list of keyframes for each image parameter to be animated
var beachX = new Array(102,130,140,200, 233, 211, 133, 455,222);
var beachY = new Array(52,120,240,400, 102,130,140,200, 233);
var beachRotation = new Array(102,30,140,200, 33, 211, 133, 355,222);
var beachOpacity = new Array(0, 0.5, 1, 0.3, 0.8, 0.3, 0.9, 0.3, 1);
var beachScaleX = new Array(0, 0.5, 0.7, 0.3, 0.8, 1, 0.9, 0.2, 1);
var beachScaleY = new Array(0.3, 0, 0.5, 0.7, 0.3, 0.8, 1, 0.9, 1);
var index = 0;
var beach;
var context;
var interval;
window.onload = init;
function make_beach()
{
beach = new Image();
beach.src = "beach.png"; // this can be any image that is large (at least 600 x 600 pixels)
beach.onload = function(){
context.drawImage(beach, 70,120);
}
}
function init() {
canvas = document.getElementById("myCanvas");
context = canvas.getContext('2d');
context.save();
make_beach();
interval = setInterval("tick()",33);
stage = new Stage(canvas);
}
function degreesToRadians(num) {
return num * 0.0174532925199432957;
}
function tick() {
var numberOfFrames = beachX.length;
if (index > (numberOfFrames -1)) {
clearInterval (interval); // cancel the timer
return;
}
context.restore();
context.globalAlpha = 1;
context.clearRect(0, 0, canvas.width, canvas.height);
var beachMiddleX = beach.width * 0.5;
var beachMiddleY = beach.height * 0.5;
context.translate(beachX[index] + beachMiddleX, beachY[index] + beachMiddleY);
context.scale(beachScaleX[index], beachScaleY[index]);
context.rotate(degreesToRadians(beachRotation[index]));
context.globalAlpha = beachOpacity[index];
context.translate(-beachMiddleX, -beachMiddleY);
context.drawImage(beach, beachX[index], beachY[index]);
context.restore();
index ++;
}
</script>
</head>
<body>
<div class="description">
</div>
<div class="canvasHolder">
<canvas id="myCanvas" width="1024" height="768" style="background-color:#FFFFFF">
Your browser doesn't support canvas. Please download a modern browser
</canvas>
</div>
</body>
</html>

Well the first thing that looks extremely weird is that you have a single call to context.save in your init function, with no matching call to restore. This is bad.
Then in your tick function you have two calls to restore!
So your program is doing save once, then restore a million times. Whatever you're trying to do that is certainly not what you want.
Here is your code with that fixed and the timer slowed down a lot. It seems to do something, though I don't know what your intent is here so its hard to say if its what you want or not:
http://jsfiddle.net/simonsarris/LJQpM/

Related

Get all 0's in return after use getImageData()

I'm drawing an image on my canvas and trying to use getImageData() from canvas to get the image data but I got all 0's in return. From my understanding, the image hasn't finished a load so I get the 0's. From this issue: getImageData always returning 0 seems like I need to wait by using onload. Is there another way to get the data by not use onload function? I want to declare an image tag in my html. Here is my code:
<html>
<head>
</head>
<body>
<script>
var bgMap = {
"bg1": "image1.JPG",
"bg2": "image2.JPG"
}
</script>
<center>
<select id="selectBg" onchange="document.getElementById('bgImage').src = bgMap[this.value]">
<option value="bg1">Bg1</option>
<option value="bg2">Bg2</option>
</select>
</center>
<img id='bgImage' src='image1.JPG' width=500 height=500 />
<canvas id='bgCanvas'></canvas>
<script>
var canvas = document.getElementById('bgCanvas')
var bgImage = document.getElementById('bgImage')
canvas.width = 500
canvas.height = 500
var ctx = canvas.getContext("2d")
ctx.drawImage(bgImage, 0, 0, 500, 500)
var imageData = ctx.getImageData(0, 0, 500, 500)
var rgba = imageData.data;
for (var px = 0, ct = 500 * 500 * 4; px < ct; px += 4) {
var r = rgba[px];
var g = rgba[px + 1];
var b = rgba[px + 2];
var a = rgba[px + 3];
console.log(r,g,b,a)
}
console.log(rgba)
</script>
</body>
</html>

Create shapes by for loop in canvas

EDIT:i will post all my code the html and js,and excuse me for too many comments
I am trying to create rectangles in canvas by for loop (there is input user)
and I want to access them in another function to do some stuff,
the main problem is how to access the shapes's name after loop I have tried this but when i call them in another function it gives me,
undefined "object name"
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var c = document.getElementById("myCanvas");
//drawing the base off the towers
var base_twr1 = c.getContext("2d");
base_twr1.beginPath();
base_twr1.moveTo(550, 500);
base_twr1.lineTo(300, 500);
base_twr1.lineWidth = 10;
base_twr1.strokeStyle = '#ff0000';
base_twr1.closePath();
base_twr1.stroke();
var base_twr2 = c.getContext("2d");
base_twr2.beginPath();
base_twr2.moveTo(900, 500);
base_twr2.lineTo(650, 500);
base_twr2.closePath();
base_twr2.stroke();
var base_twr3 = c.getContext("2d");
base_twr3.beginPath();
base_twr3.moveTo(1250, 500);
base_twr3.lineTo(1000, 500);
base_twr3.closePath();
base_twr3.stroke();
//drawing the towers
var twr1 = c.getContext("2d");
twr1.beginPath();
twr1.moveTo(430, 300);
twr1.lineTo(430, 500);
twr1.closePath();
twr1.stroke();
var twr2 = c.getContext("2d");
twr2.beginPath();
twr2.moveTo(780, 300);
twr2.lineTo(780, 500);
twr2.closePath();
twr2.stroke();
var twr3 = c.getContext("2d");
twr3.beginPath();
twr3.moveTo(1130, 300);
twr3.lineTo(1130, 500);
twr3.closePath();
twr3.stroke();
//array to know each tower what contains
//to avoid collisions
var disks_in_twrs = [];
var twr1_holder = [];
var twr2_holder = [];
var twr3_holder = [];
//start function check the user input
//and call another function if everthing
//is fine
function btn_start() {
disks_number = document.getElementById("disk_input").value;
disks_number = parseInt(disks_number);
if (disks_number > 0) {
if (disks_number < 8)
put_disks(disks_number);
} else
alert('write number');
}
var width_disks_start = 305;
var height_disks_start = 490;
var disk_width = 220;
function put_disks(disks) {
for (i = 0; i < disks; i++) {
// var r = Math.floor((Math.random() * 256));
// var g = Math.floor((Math.random() * 256));
// var b = Math.floor((Math.random() * 256));
str1 = "disk";
width_disks_start = width_disks_start + 10;
height_disks_start = height_disks_start - 20;
disk_width = disk_width - 30;
// eval("disks_in_twrs.push(str1 + i)" );
// disks_in_twrs[i]=c.getContext("2d");
// disks_in_twrs[i].rect((Math.random)*100,(Math.random)*100,150,100);
// disks_in_twrs[i].stroke();
// alert(disks_in_twrs);
twr1_holder.push(str1 + i);
// ctx.fillStyle = 'rgb(' + r + ',' + g + ', ' + b + ')';
// alert(str1 + i);
//twr1_holder[i] = c.getContext("2d");
eval("var disk"+i+"= c.getContext('2d');");
// twr1_holder[i].rect(width_disks_start, height_disks_start, disk_width, 20);
eval("disk"+i+".rect(width_disks_start, height_disks_start, disk_width, 20);");
// twr1_holder[i].strokeStyle = "black";
eval("disk"+i+".strokeStyle = 'black';");
// twr1_holder[i].stroke();
eval("disk"+i+".stroke();");
// alert(disk1.toSource());
}
}
function hide_me(){
alert("byeeeeeeeeeeeeeeeee");
twr1.fillRect(430, 500, 250, 250);
// disk2.rect(515, 51, 6, 20);
// disk2.strokeStyle = 'red';
}
<!DOCTYPE html>
<html>
<head>
<title>tower of Hanoi</title>
<style type="text/css">
canvas{
border : 1px solid black;
}
</style>
</head>
<body>
<label>how many disk do you want ?</label>
<input type="text" id="disk_input">
<button id="start" onclick="btn_start()">start</button>
<label>note that maximum disk is 8 :P</label>
<button id="make_hidden" onclick="hide_me()" >make me hide</button>
<canvas id="myCanvas" >
</canvas>
<script src="tower.js">
</script>
</body>
</html>
There's a lot going on here! I recommend attacking each issue in your code separately and building up understanding gradually, because this is an application that requires a lot of different components (DOM manipulation/event handlers, JS canvas, objects/arrays/loops, design, etc). If you're uncomfortable with any of these concepts, pick one area (such as DOM manipulation) and spend time working on simple, understandable examples, then apply what you learned to the main application.
Firstly, almost always avoid eval entirely. Mozilla says never to use it! If you're using it, it probably means your design has gone haywire somewhere along the line, which I would contend is the case here.
As for event handlers and document manipulation, I recommend avoiding onclick. Adding event listeners in your script can take care of the job; you'll likely be listening for clicks on the canvas to enable interaction later on.
Next: using canvas. You generally only need to retrieve the context once per application, not before each drawing. Your drawing code looks good other than this, except that it's not very DRY, which is usually a signal to redesign.
The hardest part is designing your code to meet your goals, which I'm not entirely clear on. Are you making an interactive Towers of Hanoi app, or one that simply animates a solver algorithm and requires no user input? Either way, I opted to use object constructors to represent Towers and Disks. Using arrays to hold these objects means you identify towers and disks by their position in an array rather than evaling a string name. Whenever you want to perform an action on your towers, such as drawing them, all you need to do is loop through the towers and call draw on each one. Later, when it comes to handling user input or writing a solver algorithm, it should be fairly easy to manipulate these arrays to suit your needs (e.g., figuring out which disk was clicked on, moving disks between towers, etc).
Keep in mind the below example is just a quick sketch to get you going and may not follow best design principles or ones that meet your needs. For example, I've hard-coded most drawing coordinate values, so it's non-responsive, so many exercises are left for the reader to improve on.
const Disk = function(width, color) {
this.width = width;
this.color = color;
};
const Tower = function(x, disks) {
this.x = x;
this.disks = [];
this.width = 20;
};
Tower.prototype.draw = function(c, ctx) {
ctx.lineWidth = this.width;
ctx.strokeStyle = "#000";
ctx.beginPath();
ctx.moveTo(this.x, 0);
ctx.lineTo(this.x, c.height);
ctx.stroke();
this.disks.forEach((e, i) => {
ctx.fillStyle = e.color;
ctx.fillRect(
this.x - e.width / 2,
c.height - (i + 1) * this.width,
e.width, this.width
);
});
};
const draw = (c, ctx, towers) => {
ctx.clearRect(0, 0, c.width, c.height);
towers.forEach(t => t.draw(c, ctx));
};
const initialize = disks => {
const towers = [
new Tower(c.width / 5),
new Tower(c.width / 2),
new Tower(c.width - c.width / 5)
];
for (let i = disks; i > 0; i--) {
towers[0].disks.push(
new Disk(i * 30, `hsl(${Math.random() * 360}, 50%, 50%`)
);
}
return towers;
};
document.getElementById("initialize-form")
.addEventListener("submit", e => {
e.preventDefault();
towers = initialize(parseInt(e.target.elements[0].value), towers);
draw(c, ctx, towers);
});
document.getElementById("btn-hide").addEventListener("click",
e => document.getElementById("menu").style.display = "none"
);
const c = document.getElementById("hanoi");
c.width = 600;
c.height = 200;
const ctx = c.getContext("2d");
let towers;
body {
margin: 0;
}
#hanoi {
padding: 0.5em;
}
#initialize-form {
display: inline-block;
}
#menu {
padding: 0.5em;
display: inline-block;
}
<div id="menu">
<form id="initialize-form">
<label>Enter disks:</label>
<input type="number" min="1" max="8" value="6">
<button type="submit">start</button>
</form>
<button id="btn-hide">hide</button>
</div>
<canvas id="hanoi"></canvas>
For what you are trying to do you should consider using a canvas library, maybe Konva:
https://konvajs.github.io/
Here is an example:
<script src="https://cdn.rawgit.com/konvajs/konva/2.1.7/konva.min.js"></script>
<div id="container"></div>
<script>
function KonvaRect(x, y, fill, draggable) {
return new Konva.Rect({
x: x, y: y, width: 50, height: 50,
fill: fill, stroke: 'black',
strokeWidth: 4, draggable: draggable
});
}
var boxes = [];
boxes.push(KonvaRect(50, 10, '#00D2FF', true));
boxes.push(KonvaRect(200, 10, '#0000FF', true));
boxes.push(KonvaRect(125, 10, '#FF0000', false));
var layer = new Konva.Layer();
boxes.forEach(function(b) { layer.add(b) });
var stage = new Konva.Stage({
container: 'container', width: 600, height: 170
});
stage.add(layer);
function moveCenter() {
boxes.forEach(function(b) { b.move({ x:0, y: Math.random() * 10 }) });
layer.batchDraw();
}
boxes[0].on('mouseover', function() {
moveCenter();
});
</script>
On this example I put 3 boxes in an array and when we detect the mouse over the light blue box all boxes move randomly down, also both blue boxes you can click and drag around the canvas.
And for the record there are many many other libraries out there...

Reading pixel data by JavaScript thought Canvas

I have a problem t read the pixel's RGBA data from a image.But I am facing that all RGBA data( all 4 bytes for all pixel ) zero value.
i use this code for JavaScript :
By the way I use this code for html.
and tehn I run html by Chrome or Firefox but When I see the console log the all value of pixel Data is Zero.Why?
var canvas= document.getElementById('mycanvas');
var c=canvas.getContext("2d");
// c.beginPath();
// c.moveTo(0,0);
// c.lineTo(500,200);
// c.stroke();
var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
img.crossOrigin = "Anonymous";
// var img= document.getElementById('image')
img.onload=function () {
c.drawImage(img,0,0);
}
var myImageData = c.getImageData(0, 0, 500, 500);
//var myImageData = c.createImageData(600, 600);
var numBytes = myImageData.data.length;
var pixelData=myImageData.data;
console.log (numBytes);
console.log (pixelData);
// var x= function () {
// for(var i=0;i<pixelData.length;i+=40)
// {
// pixelData[i] = 255 - pixelData[i]; // red
// pixelData[i + 1] = 255 - pixelData[i + 1]; // green
// pixelData[i + 2] = 255 - pixelData[i + 2]; // blue
// }
// c.putImageData(myImageData, 0, 0);
// };
// //if (pixelData[i]&&pixelData[i+1]&&pixelData[i+2]===255) {
// //console.log (numBytes);
// //}
// //else {}
// //};
// //
// x();
//pixel = imageData.data[((row * (imageData.width * 4)) + (colume * 4)) + colorindex];
//var img = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); // here is the most important part because if you dont replace you will get a DOM 18 exception.
//window.location.href=image;
<!DOCTYPE html>
<html>
<head>
<title>Image processing</title>
<style type="text/css">
</style>
</head>
<body>
<canvas id="mycanvas" width="300" height="227">
</canvas>
<img src="https://mdn.mozillademos.org/files/5397/rhino.jpg" id="image" style="display:none;">
</style>
</style>="">
<script src="img.js">
</script>
</body>
</html>
This won't work as the image comes from another domain. The canvas accepts only images from the same origin. You cannot and should not be using getImageData() from external sources that don't support CORS.
But you can convert the image into dataURL and then paint the canvas.
Edit: Sorry for not expressing it properly. The only way would be to locally download the image to your server/domain and then draw it on the canvas.
I solved my problme.
firstly as Mohanesh Said we need use Localhost or http domain.So I installed the Python and used this command to creat localhost erver.
Python -m http.server
Second I used the creatpattern command instead drawimage.this code works for me:
var canvas = document.getElementById('mycanvas')
var c = canvas.getContext('2d');
// canvas.height = window.innerHeight;
// canvas.width = window.innerWidth;
var img = document.getElementById("image")
img.crossOrigin = "Anonymous";
var ptrn = c.createPattern(img, 'no-repeat');
c.fillStyle = ptrn;
c.fillRect(0, 0, canvas.width, canvas.height);
var myImageData = c.getImageData(0, 0, canvas.width, canvas.height);
var numBytes = myImageData.data.length;
var pixelData = myImageData.data;
console.log(numBytes);
console.log(pixelData);
<!DOCTYPE html>
<html>
<head>
<title>Image processing</title>
<style type="text/css">
</style>
</head>
<body>
<canvas id="mycanvas" width="100" height="100">
</canvas>
<img src="test1.png" alt="" id="image" style="display: none;" />
<script src="img22.js">
</script>
</body>
</html>

javascript not linking correctly with HTML. how to fix?

here's my HTML
<html>
<head>
<meta charset="UTF-8" />
<script src="script.js"></script>
</head>
....
and here's the javascript. everything was fine when i had the script inline, but when i move it outside of the html file it breaks. just a simple html canvas drawing but not sure the issue. ideas?
// Canvas 1
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
photo = document.getElementById("red");
function drawImage() {
context.drawImage(photo, 0, 0);
}
window.addEventListener("load", drawImage, false);
// Canvas 2
var canvas2 = document.getElementById("canvas2");
var context2 = canvas2.getContext("2d");
context2.fillStyle = "darkRed";
context2.fillRect(0, 2, 800, 500);
context2.moveTo(0, 0);
context2.lineTo(400, 300);
// Canvas 3
var canvas3 = document.getElementById("canvas3");
var context3 = canvas3.getContext("2d");
photo3 = document.getElementById("red2");
function drawImage() {
for (var x = 0; x < 6; x++) {
for (var y =0; y < 6; y++ ) {
context3.drawImage(photo3, x * 100, y * 75, 100, 75);
}
}
}
window.addEventListener("load", drawImage, false);
Since you're loading the script in the <head>, everything is running before the DOM is loaded, so all your getElementBuId() calls are failing. You either need to put the <script> tag at the end of the <body>, or put all the code into a window.onload function, e.g.
window.onload = function() {
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
photo = document.getElementById("red");
function drawImage() {
context.drawImage(photo, 0, 0);
}
window.addEventListener("load", drawImage, false);
...
};
This has the added benefit of not polluting the global namespace.
I'll second what Barmar said. In general, I load my JavaScript at the end of the html for better performance, and so I'm sure I won't have this issue.

Createjs - Tweenjs doesn't work with Bitmap

I have an example, i want to create animation with easel.js Bitmap but it seems not working. In this project, i use preload.js to load image; crop card in cards picture; create Bitmap object and try to animate this bitmap by using tween.js Anyone can help me. Thank you!
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="Scripts/CanvasLib/easeljs-0.6.1.min.js"></script>
<script src="Scripts/CanvasLib/preloadjs-0.3.1.min.js"></script>
<script src="Scripts/CanvasLib/soundjs-0.4.1.min.js"></script>
<script src="Scripts/CanvasLib/tweenjs-0.4.1.min.js"></script>
</head>
<body>
<canvas id="CanvasDemo" width ="1024" height="768" style="border:1px solid #000000;"> </canvas>
<script>
var queue = new createjs.LoadQueue(),
stage = new createjs.Stage("CanvasDemo"),
text = new createjs.Text("Welcome to canvas demo!", "40px Bold Aria"),
image = {},
card = {};
stage.addChild(text);
//stage.autoClear = false;
queue.addEventListener("complete", handleComplete);
queue.loadManifest([
{ id: "myImage", src: "Images/card.png" }
]);
function handleComplete() {
image = queue.getResult("myImage");
card = new createjs.Bitmap(image);
card.sourceRect = new createjs.Rectangle(56, 74, 56, 74);
stage.addChild(card);
createjs.Tween.get(card).to({ x: 600, y: 1000 }, createjs.Ease.linear);
createjs.Ticker.addListener(this);
}
function tick() {
text.x += 5;
if (text.x >= 1024) {
text.x = 0;
}
text.y = 50 + Math.cos(text.x * 0.1) * 10;
text.color = createjs.Graphics.getHSL(360 * Math.random(), 50, 50);
stage.update();
}
</script>
</body>
</html>
This works just fine - except you skipped the "duration" parameter on the Tween.to call (and instead specified the ease, which is the 3rd parameter). This makes it a 0-duration tween, which ends up off-stage (so you never see it).
Try this instead:
createjs.Tween.get(card).to({ x: 600, y: 1000 }, 1000, createjs.Ease.linear);

Categories