I need some direction here as I'm not clear on what I'm doing wrong. All I'm trying to do is a load a bitmap in the center of the canvas but it's not showing up. My file path is correct and I don't see what I might have coded incorrectly, where am I going wrong?
var canvas, stage, centerX, centerY;
function init() {
'use strict';
canvas = document.getElementById("easel");
stage = new createjs.Stage(canvas);
centerX = canvas.width / 2;
centerY = canvas.height / 2;
var ship = new createjs.Bitmap("../images/millenium.png"),
shipCenterX = ship.width / 2,
shipCenterY = ship.height / 2;
ship.x = centerX;
ship.y = centerY;
ship.regX = shipCenterX;
ship.regY = shipCenterY;
stage.addChild(ship);
stage.update();
}
The way this library appears to handle drawing to the canvas is by calling stage.update() which they recommend attaching to their "tick" event (e.g. http://www.createjs.com/docs/easeljs/classes/Stage.html)
Basically, we need to keep continually redrawing the canvas, and createjs gives us a method to do that, like so:
createjs.Ticker.addEventListener("tick", handleTick);
function handleTick(event) {
stage.update();
}
However, since you haven't made your stage globally accessible, I tweaked your init function slightly so that we can access stage by returning it at the end of the function. Thus you can set stage in the global scope to the result of the function:
var stage = init();
And handleTick will use that stage by default. However if you're thinking of reusing your objects outside of your init() function, you may want to consider passing them to the init function or keeping their initial data structure outside of the init function to make them easier to access.
https://jsfiddle.net/jpgw1oka/
And make sure you are loading the CreateJS library: https://code.createjs.com/createjs-2015.11.26.min.js.
Remove the 'use strict'.
And make sure you are declaring your variables and function calls inside a window.onload event listener.
E.g.
var canvas, stage;
function init() {
canvas = document.getElementById("easel");
stage = new createjs.Stage(canvas);
var ship = new createjs.Bitmap("../images/millenium.png");
ship.x = Math.floor(stage.canvas.width * 0.5);
ship.y = Math.florr(stage.canvas.height * 0.5);
ship.regX = Math.floor(ship.image.width * 0.5);
ship.regY = Math.floor(ship.image.height * 0.5);
stage.addChild(ship);
stage.update();
}
Related
I am using createjs as my framework. I've placed a Bitmap on the canvas and I created a function to try and remove it but I keep getting an error message in the console that image is not defined. This is what my code looks like:
// onload=init() called in html doc
function init(){
var canvas = new createjs.Stage("canvas");
// Add image to canvas
image = new createjs.Bitmap("image.png");
image.x = 200;
image.y = 180;
image.scaleX = 0.35;
image.scaleY = 0.35;
canvas.addChild(image);
image.addEventListener('click', pop);
canvas.update();
}
//remove image from canvas
function pop() {
console.log('pop');
canvas.removeChild(image);
}
When I click on it, I get the console message "pop" followed by the error I mentioned above. I've tried moving the function inside init but I appear to get the same problem.
Make image as global variable, so that it can be accessed by all the functions in your case function pop.
var image;// defining 'image' variable as global
function init(){
var canvas = new createjs.Stage("canvas");
// Add image to canvas
image = new createjs.Bitmap("image.png");
image.x = 200;
image.y = 180;
image.scaleX = 0.35;
image.scaleY = 0.35;
canvas.addChild(image);
image.addEventListener('click', pop);
canvas.update();
}
//remove image from canvas
function pop() {
console.log('pop');
canvas.removeChild(image);
}
This is a scope issue. You defined image inside your init function, so it is not accessible on the pop method.
There are two easy fixes. Either move the var image outside of the init function, or use the click target instead.
var image;
function init() {
init = new createjs.Bitmap();
// etc
}
// OR
function pop(event) {
stage.removeChild(event.target);
}
Scope is really important to understand when building JavaScript applications, so I suggest getting to know it a little better :)
Cheers,
I am trying to draw a kitten in an HTML5 canvas through a class constructor using TypeScript but I am confused on how to achieve the task. I have commented the code to show what I have attempted to do based on the behavior that I expected vs what actually works. Thank you very much for your timer and advice.
module Game {
export class Test {
width: number;
height: number;
cellWidth: number;
cellHeight: number;
canvas: HTMLCanvasElement;
context: CanvasRenderingContext2D;
constructor() {
this.width = 28;
this.height = 31;
this.cellWidth = 20;
this.cellHeight = 20;
this.canvas = <HTMLCanvasElement> document.getElementById("game_canvas");
this.context = this.canvas.getContext("2d");
this.canvas.width = this.width * this.cellWidth;
this.canvas.height = this.height * this.cellHeight;
this.context.fillStyle = "blue";
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
let kitten = new Image();
kitten.src = 'img/kitten.png';
// When trying to draw a kitten in the canvas,
// this will work:
kitten.onload = () => {
this.context.drawImage(kitten, 0, 0);
};
// but this work won't:
//this.context.drawImage(kitten, 0, 0);
/*
I was assuming that by accessing the this.context property
I would have direct access to the canvas and I will be able to use
drawImage to draw the kitten on it; however, that approach
produces no kitten in the canvas.
Only by using the .onload method it works.
I am using the () => notation so that the this inside the block
is referring to the class.
I have seen many JavasScript files in which images are simple drawn
through:
context.drawImage(image, 0, 0);
They are not embedded in .onload
I have tried to Google information but I cannot pinpoint what is
happening.
*/
}
}
}
As per my comments here is my answer: quite simply because you are declaring a new Image() and setting the src, your drawImage call will no doubt be in advance of the src being loaded... if you were to use a previously loaded image (e.g. from the DOM) then the creation of a new image and load would not be required
setting the src triggers the load - doing it in another class still subjects you to the wait time for load and you cannot be sure - I would say using onload is bulletproof and essential if you are the loader of the images you are using - the only alternative methods are when the images are already loaded into the DOM (or preloaded elsewhere), you may find the canvas examples you have seen are designed to be initiated onload of the images concerned
From your code:
let kitten = new Image();
kitten.src = 'img/kitten.png';
// When trying to draw a kitten in the canvas,
// this will work:
kitten.onload = () => {
this.context.drawImage(kitten, 0, 0);
};
// but this work won't:
//this.context.drawImage(kitten, 0, 0);
Using onload is the defacto way you get an image that you can draw on a canvas. A microoptimization would be to keep a dictionary of loaded images so you get to reuse them if drawing multiple kittens.
First thing, excuse my absolute lack of knowledge in JavaScript. I'm looking for the best approach for this problem, but after 3 days I think it may be wrong.
I need to write some code to draw moving rectangles in different rows in a canvas. In the future I will need to detect when 2 rectangles are in the same X coordinate, so it's important to keep track of the X values. Coming from Java I thought the best would be to create some rectangle "objects" and with each instance a draw method.
What is causing me trouble is that I thought about calling the draw function with setInterval(), but it appears that every time the function draw is called, the values are not the same.
This is my definition of the Rectangle class:
function Rectangle(x,y,width,height) {
var x=x;
var y= y;
var width= width;
var height= height;
this.getX = function(){
return x;
}
this.setX = function (value) {
x = value;
}
this.getY = function(){
return y;
}
this.setY = function (value) {
y = value;
}
this.getWidth = function(){
return width;
}
this.setWidth = function (value) {
width = value;
}
this.getHeight = function(){
return height;
}
this.setHeight = function (value) {
height = value;
}
this.draw = function(){
if(this.getX() <=canvas.width){
clearContext(this.getX() - 30,this.getY(),this.getWidth(),this.getHeight());
var temp= this.getX()+1;
this.setX(temp);
ctx.fillRect(temp,this.getY(),this.getWidth(),this.getHeight());
}else{
clearInterval(this.draw(),speed);
}
}
}
Then I have a function formSubmit where I create the Rectangles instances when the button is pressed and call respectively the function draw with setInterval():
function formSubmit(){
number=parseInt(document.getElementById("nummerT").value);
rate=parseInt(document.getElementById("rate").value);
speed=parseInt(document.getElementById("speed").value);
confirm(speed);
myRectangle= new Rectangle(0,0,30,30);
myRectangle2 = new Rectangle(0,60,30,30);
setInterval(myRectangle.draw(),speed);
}
The problem is that setInterval(myRectangle.draw(),speed); doesn't do what you think it does. You are calling draw one time, and then the interval is calling the result of draw. You'll need something like:
interval = setInterval(function() {
myRectangle.draw();
}, speed);
You'll note, I set the return value of setInterval to a variable because that is how you'll clear the interval later. You just call
clearInterval(interval);
I don't know if that's going to solve all your problems, but it should at least you get to something that will give you some more information.
A Demo: http://jsfiddle.net/m1erickson/SdPPa/
Your instinct of creating rectangle objects to define what is drawn on the canvas is indeed the common standard.
Unlike Java, JavaScript does not have true classes, but you can create a pseudo-class as you have done in your question.
At it's simplest a Rectangle "class" needs these properties:
x, y
width, height
If you want to animate those rectangles on the canvas you might add:
velocityX, directionY
velocityY, direction
These new properties allow you to move the rectangles like this:
this.x += this.directionX * this.velocityX;
this.y += this.directionY * this.velocityY;
Hint: Html5 now has an excellent animation handler: requestAnimationFrame. You might want to use this instead of setInterval or setTimeout because it gives better performance by integrating itself with the refresh cycle of the browser.
Hint: JavaScript is a prototypal language so you can extend your "class" with methods. The best way to add methods to a "class" is to add them to the classes prototype. That way the methods are created once and shared by all instances of the class rather than having every method recreated on every instance.
So a method to allow a rectangle instance to draw itself to the canvas might look like this:
// draw this rect on the canvas
Rectangle.prototype.render=function(){
ctx.fillStyle=this.color;
ctx.fillRect(this.x,this.y,this.width,this.height);
return(this);
}
Hint: JavaScript "class" methods can be chained if you always return(this). A good use of chaining might be calling a move method on an instance and then chaining on the render method.
rectangle1.move().render();
There's lots to learn about javascript "classes".
Here's annotated code to start with:
Good luck with your project!
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// an array to hold all rectangle objects
var rectangles=[];
// a rectangle pseudo-class (javascript does not have actual classes)
function Rectangle(stdProperties) {
addProperties(this,stdProperties);
this.color=randomColor();
};
//
// Add methods that apply to all instance rectangles
// to Rectangle.prototype so those methods are
// created once for all instances instead of
// repeatedly for every instance.
//
// set x,y,width,height of this rectangle
Rectangle.prototype.init=function(x,y,width,height){
this.x=x;
this.y=y;
this.width=width;
this.height=height;
return(this);
};
// move this rectangle by its preset delta-x and delta-y
Rectangle.prototype.move=function(){
var maxRight=canvas.width-this.width;
var maxBottom=canvas.height-this.height;
this.x+=this.directionX*this.velocityX;
if(this.x<0){ this.x=0; this.directionX*=-1}
if(this.x>maxRight){ this.x=maxRight; this.directionX*=-1}
this.y+=this.directionY*this.velocityY;
if(this.y<0){ this.y=0; this.directionY*=-1}
if(this.y>maxBottom){ this.y=maxBottom; this.directionY*=-1}
return(this);
};
// draw this rect on the canvas
Rectangle.prototype.render=function(){
ctx.fillStyle=this.color;
ctx.fillRect(this.x,this.y,this.width,this.height);
return(this);
}
// create a new rectangle object from the Rectangle "class"
function newRect(x,y,width,height){
// define default properties for Rectangle
var DefaultRectangleProperties={
x:0,y:0,width:10,height:10,
velocityX:1,velocityY:1,directionX:1,directionY:1,
color:"black",
}
// new-up a Rectangle
var rect = new Rectangle(DefaultRectangleProperties);
// set the x,y,width,height & draw it on the canvas
rect.init(x,y,width,height).render();
// return(this) to allow chaining
return(rect);
}
// TESTING
// create 5 rectangles with some randomness
for(var i=0;i<5;i++){
var rect=newRect(Math.random()*200,Math.random()*200,40,50);
rect.velocityX=Math.random()*2;
rect.velocityY=Math.random()*3;
rectangles.push(rect);
}
// animate the rectangles using requestAnimationFrame
animate();
// the animation loop
function animate(t){
// request another animation frame
requestAnimationFrame(animate);
// clear the canvas
// move all the rectangles by their preset distance
// redraw all the rectangles
ctx.clearRect(0,0,canvas.width,canvas.height);
for(var i=0;i<rectangles.length;i++){
rectangles[i].move().render();
}
}
///////////////////////////////////
// Utilities
///////////////////////////////////
// create getters/setters on the specified object
// using the supplied properties object
//
function addProperties(object,properties){
for (var i in properties) {
(function(i) {
Object.defineProperty(object, i, {
get: function(){ return properties[i]; },
set: function(val){ properties[i] = val; }
})
})(i);
}
}
// generate a random color
function randomColor(){
return('#'+Math.floor(Math.random()*16777215).toString(16));
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I have not so much to add to the other answers (+1 to both) but just a note on this part:
function Rectangle(x,y,width,height) {
var x=x;
var y= y;
var width= width;
var height= height;
...
When you do Rectangle(x, y, ...) the compiler/parser will actually do this for you (or rather, the equivalent of):
var x = arguments[0]; // x declared internally, no need to manually declare it
var y = arguments[1]; // y declared too, etc.
...
so you do not need to declare the variables in the function signature as they are already declared - or just leave the signature without any parameters and do the assigning manually (a tad slower but fully legal).
function Rectangle() {
var x = arguments[0]; // legal but not recommended (in most cases)
var y = arguments[1];
...
So, in conclusion - the recommended approach in this case would be:
function Rectangle(x,y,width,height) {
// no x,y, width and height decl. here - they're declared by signature
this.getX = function(){
return x;
}
...
Second issue: setInterval will need a reference to a function. As it is now the function will invoked due to placing the to parenthesis at the end and the result of that function will be handed as a reference instead.
You can call it like:
setInterval(myRectangle.draw, speed); // only a reference, no parenthesis
But in order to enable cancelling of it you need to store the timer ID:
var timerID; // global scope
...
timerID = setInterval(myRectangle.draw, speed);
Then use that request to cancel it later:
clearInterval(timerID);
I would too recommend using requestAnimationFrame as this is optimized for animation and monitor sync.
Contrary to setInterval you'll need to call it per frame inside your animation loop. You can use a flag/condition to not call it again when you want to end the animation.
You have also a clearContext method in there - I assume you have that defined elsewhere in the code, if not, check out context.clearRect().
I have a problem with an object not drawing it's image. I've set the onload property of the image to the draw function..
//ctor
function Sprite(someargs,pContext)
this.setContext(pContext); //This could be the problem?
this.setX(px);
this.setY(py);
this.setTexture(pImagePath);
//I run this in the constructor
Sprite.prototype.setTexture = function(pImagePath){
this.texture = new Image();
this.texture.onload = this.draw();
this.texture.src = pImagePath;
};
Sprite.prototype.draw = function(){
this.getContext().drawImage(this.texture,this.getX(),this.getY(),100,100);
};
Sprite.prototype.setContext = function(pContext){
this.mContext = pContext;
};
No errors at runtime, image not drawn onto canvas though.
I'v put alerts in all the above methods, all of which are being executed.
Anyone got any ideas as to why it's not drawing?
Cheers
this.texture.onload = this.draw();
you are not setting onload to the draw function but to the result of draw function
this.texture.onload = this.draw;
also wouldn't be good because you will lose your context of this in here.
this inside of draw function will point to texture instead of Sprite
you need to bind the function draw to this (which is the Sprite at the moment) and pass it to onload
this.texture.onload = this.draw.bind(this);
or:
var that = this;
this.texture.onload = function() { that.draw(); }
I'd like to have an HTML canvas context that I can paint to and read off-screen (in this example, writing text and reading the shape that is created, but it's a general question). I may also want to use a canvas as an off-screen frame-buffer.
I suppose I could create a hidden DOM element but I'd rather create it from JavaScript (I may want to create and destroy a number of canvas at runtime).
Possible?
You can create a new canvas element with document.createElement:
var canvas = document.createElement('canvas');
and then get the context from it. Just make sure you set the width and height. You don't have to add the canvas to the tree in order to make it work:
DEMO
But you definitely have to create that node. You could create a function for that though:
function createContext(width, height) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas.getContext("2d");
}
But that is where my competency ends... whether you can somehow transfer a context to another context or canvas, I don't know...
Its old but what about saving one canvas with toDataURL and copying to the other with drawImage. you could also use save and restore to make a frame buffer
function createCanvas(width, height) {
var c = document.createElement('canvas');
c.setAttribute('width', width);
c.setAttribute('height', height);
return c;
}
function canvasImg(canvas) {
var ctx = canvas.getContext('2d');
ctx.fillRect(0,0,canvas.width, canvas.height);
var img = canvas.toDataURL('image/png');
return img;
}
function placeImage(canvas, img) {
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0,0);
}
window.onload = function(){
var canvas = createCanvas(400, 400);
var hiddenCanvas = createCanvas(400,400);
var i = canvasImg(hiddenCanvas);
var img = new Image();
img.src = i;
placeImage(canvas, img);
document.body.appendChild(canvas);
}
There is apparently a new thing called OffscreenCanvas that was deliberately designed for this use case. An additional bonus is that it also works in Web Workers.
You can read the specifications here: https://html.spec.whatwg.org/multipage/canvas.html#the-offscreencanvas-interface
And see examples here: https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
Currently it is only fully supported by Chrome and is available behind flags in Firefox and Opera, but you can always check for the latest information on supported browsers here: https://caniuse.com/#feat=offscreencanvas
ps.: Google also has a dedicated guide explaining it's use in Web Workers: https://developers.google.com/web/updates/2018/08/offscreen-canvas
Both the CanvasRenderingContext2D and WebGLRenderingContext classes have the canvas element associated with them as the property canvas; and, like normal, both context instances and their canvases will be garbage collected when your code no longer makes references to them at run time.
You can use this function to create a new context
function newContext({width, height}, contextType = '2d') {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas.getContext(contextType);
}
const ctx = newContext({width: 100, height: 100});
console.log(ctx.canvas.width == 100) // true
And by making use of dereferencing you can easily create a clone of a DOM canvas for frame buffering like this:
const domCanvas = document.getElementById('myCanvas');
const frameBuffer = newContext(domCanvas);
frameBuffer.drawImage(domCanvas, 0, 0);
Which will create a context with the same width and height as the canvas element passed in. You can extend the function as needed.