Is there any way to obtain the canvas that a context is used for?
Why I'm asking is because I'm creating a prototype function for CanvasRenderingContext2D in which I need the width/height of the canvas element.
E.g.:
var cv = document.getElementById('canvas');
var ctx = cv.getContext('2d');
// Using only 'ctx', how to get 'cv'?
ctx.canvas should return the canvas DOM node, from which you can get height and width.
I tried it with https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_usage
Firefox was able to return ctx.canvas, as well as ctx.canvas.width and ctx.canvas.height. Also confirmed in Chrome.
Try this to check for yourself:
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var isSameObject = canvas === context.canvas;
alert(isSameObject
? 'context.canvas gives expected result'
: 'unexpected result');
Here's the above in a jsFiddle.
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 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(); }
My application has very long running lifespan. So I want to garbage collect unused functions on the fly by declare variables locally.
function Video1() {
var video1 = document.getElementById('video1');
video1.play();
drawCanvas(video1);
}
function drawCanvas(elem) {
this.elem = elem;
console.log(elem);
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.drawImage(this.elem, 0, 0, canvas.width, canvas.height);
requestAnimationFrame(drawCanvas);
};
var button1 = document.getElementById('button1');
button1.addEventListener('click', Video1, false);
Putting the requestAnimationFrame in adding something like 1373086307101.346 as the elem value
I solve this with the setInterval method, but is there better way to do so as I mainly target mobile devices.
Your drawCanvas expects an argument elem. When not provided - it is set to undefined.
By calling requestAnimatinoFrame(drawCanvas) you will call drawCanvas without any arugments. What you probably need is the wrapper function:
function wrapperForDrawCanvas() {
// Obtain proper elem here
drawCanvas(elem);
}
And then call requestAnimationFrame(wrapperForDrawCanvas) instead of requestAnimationFrame(drawCanvas). That way your drawCanvas will always get elem argument.
I am trying to use InfoVis / JIT to render a force directed graph visualizing a network.
I am a newbie to both java script and JIT.
I have created my own custom node types using following code in my js file, which lets me display my image on the node.
$jit.ForceDirected.Plot.NodeTypes.implement({
'icon1': {
'render': function(node, canvas){
var ctx = canvas.getCtx();
var img = new Image();
img.src='magnify.png';
var pos = node.pos.getc(true);
img.onload = function() {
ctx.drawImage(img, pos.x, pos.y);
};
},
'contains': function(node,pos){
var npos = node.pos.getc(true);
dim = node.getData('dim');
return this.nodeHelper.circle.contains(npos, pos, dim);
//return this.nodeHelper.square.contains(npos, pos, dim);
}
}
I am assigning this custom node type to the node using "$type": "icon1" in the json data object. I do get image on the node, but the problem is that I am not able to hide it when required. I am able to hide the in-built node types like circle,square etc. using following code.
node.setData('alpha', 0);
node.eachAdjacency(function(adj) {
adj.setData('alpha', 0);
});
fd.fx.animate({
modes: ['node-property:alpha',
'edge-property:alpha'],
duration: 2000
});
But the same code does not work for custom nodes.
Hence I tried to temporarily change the type of node to the built-in "circle" type, hid it and then re-setted the type of node to its original i.e. my custom node, icon1.
function hideNode( ){
var typeOfNode = node.getData('type');
node.setData( 'type','circle');
node.setData('alpha', 0);
node.eachAdjacency(function(adj) {
adj.setData('alpha', 0);
});
fd.fx.animate({
modes: ['node-property:alpha',
'edge-property:alpha'],
duration: 2000
});
node.setData('type',typeOfNode );
}
I think this should work but the custom image comes back in a while on the canvas.
If I don't reset the type of node to its original i.e. in the above code and comment out the following statement and call hide function, then the node gets hidden.
node.setData('type',typeOfNode );
I am not able to figure out how by only setting a node's type to some custom type, the node is being rendered. Any help with this question will be appreciated.
I need to re-set the node's type to its original because I want the node to be restored when required by calling unhide function. If I don't reset node's type to the original then it would be rendered as a circle when restored.
I have gone through the API and the google group for JIT but couldn't find an answer.
Can anyone help?
Here's a look at a snippet from the Plot's plotNode function:
var alpha = node.getData('alpha'),
ctx = canvas.getCtx();
ctx.save();
ctx.globalAlpha = alpha;
// snip
this.nodeTypes[f].render.call(this, node, canvas, animating);
ctx.restore();
As you can see, the node's alpha value is applied to the canvas immediately before the node's render function is called. After rendering the node, the canvas is restored to the previous state.
The issue here is that your custom node's render function does not render the node synchronously, and the canvas state is getting restored prior to the call to drawImage. So, you can do one of two things:
1) Preload and cache your image (preferred approach, as this will also prevent image flickering and help with performance):
// preload image
var magnifyImg = new Image();
magnifyImg.src = 'magnify.png';
// 'icon1' node render function:
'render': function(node, canvas){
var ctx = canvas.getCtx();
var pos = node.pos.getc(true);
ctx.drawImage(magnifyImg, pos.x, pos.y);
}
or 2) save the canvas state, reapply the alpha, and then restore the canvas state after drawing the image in your onload handler:
// 'icon1' node render function:
'render': function(node, canvas){
var ctx = canvas.getCtx();
var img = new Image();
img.src='magnify.png';
var pos = node.pos.getc(true);
img.onload = function() {
ctx.save(); // save current canvas state
ctx.globalAlpha = node.getData('alpha'); // apply node alpha
ctx.drawImage(img, pos.x, pos.y); // draw image
ctx.restore(); // revert to previous canvas state
};
}
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.