how to create a canvas dynamically in javascript - javascript

I have a canvas that you can draw things with mouse.. When I click the button It has to capture the drawing and add it right under the canvas, and clear the previous one to draw something new..So first canvas has to be static and the other ones has to be created dynamically with the drawing that I draw .. What should I do can anybody help
here is jsfiddle
http://jsfiddle.net/dQppK/378/
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
painting = false,
lastX = 0,
lastY = 0;

You can create a new canvas the same way you’d create any element:
var newCanvas = document.createElement('canvas');
Then you can copy over your old canvas:
newCanvas.width = oldCanvas.width;
newCanvas.height = oldCanvas.height;
oldCanvas.parentNode.replaceChild(newCanvas, oldCanvas);
ctx = newCanvas.getContext('2d');
But if you’re just looking to clear your drawing surface, what’s wrong with clearRect?
ctx.clearRect(0, 0, canvas.width, canvas.height);
Or, in your case, another fillRect. Updated demo

here's the function i use for this, it is part of a library i made and use to ease a few things about canvas.
I just put it on github in case other function might be be of use, i'll have to make a readme later...
https://github.com/gamealchemist/CanvasLib
with namespaceing removed, the code is as follow to insert a canvas :
// insert a canvas on top of the current document.
// If width, height are not provided, use all document width / height
// width / height unit is Css pixel.
// returns the canvas.
insertMainCanvas = function insertMainCanvas (_w,_h) {
if (_w==undefined) { _w = document.documentElement.clientWidth & (~3) ; }
if (_h==undefined) { _h = document.documentElement.clientHeight & (~3) ; }
var mainCanvas = ga.CanvasLib.createCanvas(_w,_h);
if ( !document.body ) {
var aNewBodyElement = document.createElement("body");
document.body = aNewBodyElement;
};
document.body.appendChild(mainCanvas);
return mainCanvas;
}

Related

Use an existing canvas without losing contents in pIxi.js

I am quite new to Pixi.js so I'm sorry this is a stupid question.
I understand that if I would like to render pixi.js to an existing canvas I have to specify view.
const app = new PIXI.Application({
view: myExistingCanvas,
});
However, I realized that if I write like this, Pixi application actually overwrites my existing canvas and I end up losing all the contents inside "myExistingCanvas".
Could somebody advise me how I can create a pixi application on top of an existing canvas without overwriting?
Using the view property you can pass to the constrctor of a new PIXI.Application, we can tell it to use an existing Canvas. This canvas though doesn't necessarily have to be added to the DOM - it's enough if it exists 'virtually'.
So ultimately we need three Canvas instances - which all should have equal dimensions.
The first canvas would be the existing canvas you've mentioned in your question and act as an off-screen canvas
The second canvas is another empty off-screen canvas, which captures Pixi's output
The third canvas is actually the on-screen canvas which combines the output of the previous two canvases
Now you might wonder how to do this.
To do this we must intercept Pixi's update loop, which we can do by adding a ticker to PIXI.Ticker.shared.
Inside this update loop we need to do the following things:
Update Pixi's animations and call it's renderer
Clear the third (on-screen) canvas
Draw the contents of the first canvas to the third
Draw the contents of the second canvas to the third
Basically that's it - though it might sound a bit abstract.
Here's an example (Just click on 'Run code snippet'):
let offScreenCanvasA = document.createElement("canvas");
let offScreenCanvasB = document.createElement("canvas");
let onScreenCanvas = document.createElement("canvas");
let width = 400;
let height = 300;
offScreenCanvasA.width = width;
offScreenCanvasB.width = width;
onScreenCanvas.width = width;
offScreenCanvasA.height = height;
offScreenCanvasB.height = height;
onScreenCanvas.height = height;
document.body.appendChild(onScreenCanvas);
const app = new PIXI.Application({
view: offScreenCanvasB,
transparent: true,
width: 400,
height: 300
});
const container = new PIXI.Container();
const renderer = PIXI.autoDetectRenderer();
app.stage.addChild(container);
const texture = PIXI.Texture.from('https://picsum.photos/id/237/26/37');
for (let i = 0; i < 25; i++) {
const bunny = new PIXI.Sprite(texture);
bunny.anchor.set(0.5);
bunny.x = (i % 5) * 40;
bunny.y = Math.floor(i / 5) * 40;
container.addChild(bunny);
}
container.x = app.screen.width / 2;
container.y = app.screen.height / 2;
container.pivot.x = container.width / 2;
container.pivot.y = container.height / 2;
let ticker = PIXI.Ticker.shared
ticker.add(function(delta) {
container.rotation -= 0.01;
renderer.render(container);
onScreenCanvas.getContext("2d").clearRect(0, 0, width, height);
onScreenCanvas.getContext("2d").drawImage(offScreenCanvasA, 0, 0, width, height);
onScreenCanvas.getContext("2d").drawImage(offScreenCanvasB, 0, 0, width, height);
});
let image = new Image();
image.onload = function(e) {
offScreenCanvasA.getContext("2d").drawImage(e.target, 0, 0, width, height);
}
image.src = "https://picsum.photos/id/237/400/300";
<script src="https://d157l7jdn8e5sf.cloudfront.net/dev/pixi-legacy.js"></script>
The dog picture in the background is the from the first canvas, and the rotating grid of dogs Pixi's output to the second canvas.

Keep canvas image after resizing [duplicate]

I have created a basic shape in HTML canvas element which works fine.
The problem occurs when I resize the canvas, all the drawing in the canvas disappears. Is this the normal behavior? or is there a function that can be used to stop this?
One way to fix this could be to call drawing function again on canvas resize however this may not be very efficient if there is huge content to be drawn.
What's the best way?
Here is the link to sample code https://gist.github.com/2983915
You need to redraw the scene when you resize.
setting the width or height of a canvas, even if you are setting it to the same value as before, not only clears the canvas but resets the entire canvas context. Any set properties (fillStyle, lineWidth, the clipping region, etc) will also be reset.
If you do not have the ability to redraw the scene from whatever data structures you might have representing the canvas, you can always save the entire canvas itself by drawing it to an in-memory canvas, setting the original width, and drawing the in-memory canvas back to the original canvas.
Here's a really quick example of saving the canvas bitmap and putting it back after a resize:
http://jsfiddle.net/simonsarris/weMbr/
Everytime you resize the canvas it will reset itself to transparant black, as defined in the spec.
You will either have to:
redraw when you resize the canvas, or,
don't resize the canvas
One another way is to use the debounce if you are concerned with the performance.
It doesnt resize or redraw every position you are dragging. But it will resize only when the it is resized.
// Assume canvas is in scope
addEventListener.("resize", debouncedResize );
// debounce timeout handle
var debounceTimeoutHandle;
// The debounce time in ms (1/1000th second)
const DEBOUNCE_TIME = 100;
// Resize function
function debouncedResize () {
clearTimeout(debounceTimeoutHandle); // Clears any pending debounce events
// Schedule a canvas resize
debounceTimeoutHandle = setTimeout(resizeCanvas, DEBOUNCE_TIME);
}
// canvas resize function
function resizeCanvas () { ... resize and redraw ... }
I had the same problem. Try following code
var wrapper = document.getElementById("signature-pad");
var canvas = wrapper.querySelector("canvas");
var ratio = Math.max(window.devicePixelRatio || 1, 1);
canvas.width = canvas.offsetWidth * ratio;
canvas.height = canvas.offsetHeight * ratio;
It keeps the drawing as it is
One thing that worked for me was to use requestAnimationFrame().
let height = window.innerHeight;
let width = window.innerWidth;
function handleWindowResize() {
height = window.innerHeight;
width = window.innerWidth;
}
function render() {
// Draw your fun shapes here
// ...
// Keep this on the bottom
requestAnimationFrame(render);
}
// Canvas being defined at the top of the file.
function init() {
ctx = canvas.getContext("2d");
render();
}
I had the same problem when I had to resize the canvas to adjust it to the screen.
But I solved it with this code:
var c = document.getElementById('canvas');
ctx = c.getContext('2d');
ctx.fillRect(0,0,20,20);
// Save canvas settings
ctx.save();
// Save canvas context
var dataURL = c.toDataURL('image/jpeg');
// Resize canvas
c.width = 50;
c.height = 50;
// Restore canvas context
var img = document.createElement('img');
img.src = dataURL;
img.onload=function(){
ctx.drawImage(img,20,20);
}
// Restote canvas settings
ctx.restore();
<canvas id=canvas width=40 height=40></canvas>
I also met this problem.but after a experiment, I found that Resizing the canvas element will automatically clear all drawings off the canvas!
just try the code below
<canvas id = 'canvas'></canvas>
<script>
var canvas1 = document.getElementById('canvas')
console.log('canvas size',canvas1.width, canvas1.height)
var ctx = canvas1.getContext('2d')
ctx.font = 'Bold 48px Arial'
var f = ctx.font
canvas1.width = 480
var f1 = ctx.font
alert(f === f1) //false
</script>

Canvas state lost after changing size

I want to resize a canvas, but when I do, the context gets reset, losing the current fillStyle, transformation matrix, etc. The ctx.save() and ctx.restore() functions didn’t work as I initially expected:
function resizeCanvas(newWidth, newHeight) {
ctx.save();
canvas.width = newWidth; // Resets context, including the save state
canvas.height = newHeight;
ctx.restore(); // context save stack is empty, this does nothing
}
After researching for a while, I can’t seem to find a good way to save and restore the canvas context after resizing. The only way I can think of is manually saving each property, which seems both messy and slow. ctx.save() doesn’t return the saved state either, and I don’t know of a way to access the context’s stack.
Is there a better way, or am I doomed to use something like this:
function resizeCanvas(newWidth, newHeight) {
let fillStyle = ctx.fillStyle;
let strokeStyle = ctx.strokeStyle;
let globalAlpha= ctx.globalAlpha;
let lineWidth = ctx.lineWidth;
// ...
canvas.width = newWidth;
canvas.height = newHeight;
ctx.fillStyle = fillStyle;
ctx.strokeStyle = strokeStyle;
ctx.globalAlpha= globalAlpha;
ctx.lineWidth = lineWidth;
// ...
}
I also found this somewhat annoying and I'm very interested to see if there are other solutions to this. I'd also like to see a reason as to why the state has to be reset and the state stack cleared when the canvas is resized. Really it's unintuitive behavior that you wouldn't expect and even MDN doesn't mention it, so it's probably pretty easy to make this mistake.
The absolute best way is to restructure your code in such a manner that all draw operations can be redone after the canvas is resized since you're gonna have to redraw everything anyway, you might as well be setting all the ctx states after you resize anyway. (i.e. make and utilize a 'draw' function that can be called after you resize the canvas)
If you really want to keep the state then I'd recommend making your own save/restore state stack and perhaps a resize function while you're at it. I'm not gonna judge you and say it's a bad idea...
Let's say I wanted to resize the canvas to the exact width and height of some text before drawing the text.
Normally I would have to set the font for the text first, then measure the text, then resize the canvas, but since resizing the canvas resets the state, I'd then have to set the font again like so:
ctx.font = "48px serif"
width = ctx.measureText('Hello World').width
canvas.width = width
ctx.font = "48px serif"
As an (admittedly over-complicated) workaround, I'd save and restore the state using my custom save-restore functions before and after resizing, respectively.
And yes, I do see the irony in replacing one extra line of code with around 30 or so extra lines of code in this particular example.
let canvas = document.querySelector('canvas')
, ctx = canvas.getContext('2d')
, stack = []
function save(ctx){
let state = {}
for(let property in ctx){
if(property == 'canvas')
continue
if(typeof ctx[property] == 'function')
continue
state[property] = ctx[property]
}
stack.push(state)
}
function restore(ctx){
let state = stack.pop() || {}
for(let property in state){
ctx[property] = state[property]
}
}
function resize(ctx, width, height){
save(ctx)
ctx.canvas.width = width || canvas.width;
ctx.canvas.height = height || canvas.height;
restore(ctx)
}
////////////// EXAMPLE ////////////////
let index = 0
, words = ["Our", "Words", "Are", "Dynamic"];
(function change(){
let font_size = ~~(Math.random() * 150 + 16)
let word = words[index]
index = (index + 1) % words.length
ctx.font = font_size+"px serif"
ctx.textBaseline = "hanging"
ctx.fillStyle = "white"
resize(ctx, ctx.measureText(word).width, font_size)
ctx.fillText(word, 0, 0)
setTimeout(change, 750)
})()
canvas{
background-color : orange
}
<canvas></canvas>
The accepted answer no longer works because some properties are deprecated and hence give runtime error when trying to set deprecated properties.
Here's a fix for Khauri MacClain's answer.
function save(ctx){
let props = ['strokeStyle', 'fillStyle', 'globalAlpha', 'lineWidth',
'lineCap', 'lineJoin', 'miterLimit', 'lineDashOffset', 'shadowOffsetX',
'shadowOffsetY', 'shadowBlur', 'shadowColor', 'globalCompositeOperation',
'font', 'textAlign', 'textBaseline', 'direction', 'imageSmoothingEnabled'];
let state = {}
for(let prop of props){
state[prop] = ctx[prop];
}
return state;
}
function restore(ctx, state){
for(let prop in state){
ctx[prop] = state[prop];
}
}
function resize(ctx, width, height){
let state = save(ctx);
ctx.canvas.width = width || canvas.width;
ctx.canvas.height = height || canvas.height;
restore(ctx, state);
}
I was trying to take a partially-drawn canvas -- generated by a library, and so not really possible to resize before all the draw commands were issued -- and expand it to add some text next to it. There's a deleted answer on this question by #markE that helped me out. See, you can make a new canvas element of arbitrary size, and "draw" the old (smaller) canvas onto it:
const libCanvas = myLib.getCanvas();
const newCanvas = document.createElement("canvas");
const ctx = newCanvas.getContext("2d");
ctx.font = MY_FONT;
newCanvas.width = libCanvas.width + ctx.measureText(myStr).width;
ctx.drawImage(libImage, 0, 0);
ctx.font = MY_FONT; // Changing width resets the context font
ctx.fillText(myStr, libCanvas.width, libCanvas.height / 2);
Now newCanvas is as wide as the old canvas, plus the width of the text I wanted to draw in, and has the contents of the library-generated canvas on the left side, and my label text on the right side.

I cannot resize the Canvas in JavaScript

Ok, so, I'm working on a project in HTML5 and JavaScript. I'm trying to resize a Canvas, but it won't work. I don't think it's my browser, though, because I am using the latest version of FireFox. I've also researched this issue for a while, and I am confident I'm doing this correctly. So, I don't know why it won't work.
Here's my Code:
var level = 1;
var levelImg = undefined;
var width = 0;
var height = 0;
var cnvs = document.getElementById("cnvs").getContext("2d");
width = window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
height = window.innerHeight
|| document.documentElement.cleintHeight
|| document.body.cleintHeight;
cnvs.width = width;
cnvs.height = height;
window.onload = function Init(){
levelImg = document.getElementById("level" + level);
setInterval("Draw()", 3);
}
function Draw(){
//Clear the Screen
cnvs.clearRect(0, 0, width, height);
//Draw stuff
DrawLevel();
}
function DrawLevel(){
cnvs.fillRect(0, 0, 10, 10);
}
Any help is appreciated. Thanks.
First correct all the typos in you code.
Use the browser console to detect errors.
Difference between canvas and the context
var cnvs = document.getElementById("cnvs").getContext("2d");
cnvs variable is not a canvas but the context for the canvas.
Canvas is the element and context is the object used to write in the canvas.
To access the canvas you need to do this:
var canvas = document.getElementById("cnvs");
var cnvs = canvas.getContext('2d'); //context
Now when you are trying to change the canvas with, you use canvas, not cnvs.
canvas.width = width;
canvas.height = height;
SetInterval expects a function and a number value that represents milliseconds.
"Draw()" is a string, not a function, and 3 is a really small number between each time the browser draws on canvas. It works, but it's very inefficient.
Other point about setInterval. Avoid it by using requestAnimationFrame() instead.
Take a look here: setTimeout or setInterval or requestAnimationFrame
Defining var levelImg = undefined has no utility. It can be replaced by var levelImg;

assign canvas to div, adapt canvas dimensions to div dimenson - how to fix?

I have this example of a canvas as a div background (I forked it from another example, don't remember where I found it):
http://jsfiddle.net/sevku/6jace59t/18/
var canvas = document.getElementsByTagName('canvas')[0];
var ctx = canvas.getContext('2d');
var divHeight = document.getElementById('canvas').clientHeight;
var divWidth = document.getElementById('canvas').clientWidth;
function assignToDiv(){ // this kind of function you are looking for
dataUrl = canvas.toDataURL();
document.getElementsByTagName('div')[0].style.background='url('+dataUrl+')'
}
function draw() { // replace with your logic
ctx.fillStyle = "rgb(100, 250, 100)";
ctx.fillRect (10, 10, divWidth-20, divHeight-20);
}
draw()
assignToDiv()
My problem; If I put the dimensions of the div 300 x 150, the canvas does what it is supposed to do. But if I change the dimensions, the canvas is supposed to adapt to the div dimensions. What did I do wrong that this doesn't happen?
PS: I'm a beginner, so please forgive me stupid questions.
It's because if you don't give canvas width and height, it's default to 300x150, so after you get the width and height from div, you should use them to set your canvas' dimensions as well.
Another point worth notice is that you use div.style.background property to set the background image, however, as there's many background related properties (e.g: background-repeat in your jsfiddle, background-position, background-size...), the background can set all of them at once.
When you use div.style.background='url('+dataUrl+')';. It overrides all other background-related properties to initial.
If you want to preserve those properties, you may either reset them after you set style.background, or you can use div.style.backgroundImage to change the background image without affect other background related properties.
jsfiddle
var canvas = document.getElementsByTagName('canvas')[0];
var ctx = canvas.getContext('2d');
var divHeight = document.getElementById('canvas').clientHeight;
var divWidth = document.getElementById('canvas').clientWidth;
// VVVV After you get WxH, set the canvas's dimension too.
canvas.width = divWidth;
canvas.height = divWidth;
var div1 = document.getElementById('canvas');
var div2 = document.getElementById('canvas2');
function assignToDiv(div){ // this kind of function you are looking for
var dataUrl = canvas.toDataURL();
div.style.background='url('+dataUrl+')'; // This line will overwrite your background settings.
div.style.backgroundRepeat = 'repeat-x'; // Use this to set background related properties after above.
}
function assignToDivAlt(div){ // this kind of function you are looking for
var dataUrl = canvas.toDataURL();
div.style.backgroundImage = 'url('+dataUrl+')'; // Only set the background-image would have same effect.
}
function draw() { // replace with your logic
ctx.fillStyle = "rgb(100, 250, 100)";
// If you don't set WH, then the canvas would be 300x150, and those
// you drawed but out of boundary are clipped.
ctx.fillRect (10, 10, divWidth-20, divHeight-20);
}
draw()
assignToDiv(div1);
assignToDiv(div2);
canvas {display:none;}
div {
width:600px;
height:550px;
border:1px solid grey;
background-repeat:repeat-x;
}
<canvas></canvas>
<div id="canvas"></div>
<div id="canvas2"></div>

Categories