I am creating a file called functions.js with diferent functions like:
bgImage()
drawImage()
setText()
My issue is that my text keeps staying behind.
What i want to do is, when i call setText() i can put text where i want. And the text will be put on the top ofc the convas. I know i need to call the image draw load functions first to get them to not overwrite my text. But i did so in my JS.
So its very important that i can call the function setText() as many times as i want, after all images are drawn/set, and the text will be visible.
I want the text on the top.
Here is my code:
functions.js
var canvas = "";
var context = "";
function canvasInit() {
canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d');
}
function bgImage() {
var imageObj = new Image();
imageObj.onload = function() {
var pattern = context.createPattern(imageObj, 'repeat');
context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle = pattern;
context.fill();
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/wood-pattern.png';
}
function drawImage() {
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 10, 50);
context.drawImage(imageObj, x, y, width, height);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
}
function (text) {
context.font = 'italic 40pt Calibri';
context.fillText(text, 150, 100);
}
index.html
<!DOCTYPE html>
<html>
<head>
<script src="functions.js"></script>
<script>
document.addEventListener('DOMContentLoaded',ready);
function ready() {
canvasInit();
bgImage();
drawImage();
setText("Yo");
setText("heyyyy");
}
</script>
</head>
<body>
<canvas id="myCanvas" width="500" height="400"></canvas>
</body>
</html>
Updated test that do not work:
index.html
<!DOCTYPE html>
<html>
<head>
<script src="functions.js"></script>
<script>
document.addEventListener('DOMContentLoaded',ready);
function ready() {
canvasInit();
bgImage(function() {
setText();
});
}
</script>
</head>
<body>
<canvas id="myCanvas" width="500" height="400"></canvas>
</body>
</html>
functions.js
var canvas = "";
var context = "";
function canvasInit() {
canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d');
}
function bgImage(callback) { // add parameter for function
var imageObj = new Image();
imageObj.onload = function() {
var pattern = context.createPattern(imageObj, 'repeat');
context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle = pattern;
context.fill();
if (typeof callback === 'function') {
callback(); // invoke callback function
}
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/wood-pattern.png';
}
function drawImage() {
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 10, 50);
context.drawImage(imageObj, x, y, width, height);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
}
function setText() {
context.font = 'italic 40pt Calibri';
context.fillText("Yoo adfa ds asd a sd", 150, 100);
}
It happens because your image loading is asynchronous: before the image has finished loading you draw your text as the function exits after setting the source. Then when the image has finished loaded the onload function is called and the image drawn on top of whatever is drawn previously (in this case the text).
You need to implement a callback handler for your functions for this to work - for example:
function bgImage(callback) { /// add parameter for function
var imageObj = new Image();
imageObj.onload = function() {
var pattern = context.createPattern(imageObj, 'repeat');
context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle = pattern;
context.fill();
if (typeof callback === 'function')
callback(); /// invoke callback function
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/wood-pattern.png';
}
Then you use it:
bgImage(function() {
setText();
});
You will of course need to do this with the other image loading functions as well. Tip: If you end up with a long chain it's probably better to assign non-anonymous functions instead of inline them as in the last example.
Update:
Just for clarity: it's important that the function provided is provided as a reference and not a result from calling, for example: use the callback this way:
bgImage(setText); /// correct
not this way:
bgImage(setText()); /// wrong
With the parenthesis the setText is simply invoked and its result is passed as a callback. This means the text will be drawn first and then bgImage is called.
I never got my code to work. However i found some framework called "http://kineticjs.com/" and i solved my issues using that.
Thanks for the comments Ken - Abdias Software. I did also take a look at what you linked to in your profile description. Looking really neat ;)
However i feel its the best to accept the answer that solved my problem, and i solved it myself.
Thanks again :)
Related
In general we can convert the HTML elements to string and then we can insert it into DOM later when needed. Similarly, I want to convert the "CANVAS" element to string along with its context properties.
In the following example, I am getting the string value of span tag with outerHTML property. Likewise I want to get the "CANVAS"element along with context properties.
Is there any method or property for this support?
Example code snippets:
var sp=document.createElement("span");
sp.innerHTML = "E2"
var e2 = sp.outerHTML;
$("#test1").append(e2);
var c=document.createElement("CANVAS");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50);
ctx.lineTo(150,120);
ctx.stroke();
var cn = c.outerHTML;
$("#test2").append(cn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test1">
<span>E1</span>
</div>
<div id="test2">
</div>
Seems like you already know how to get dom properties of the canvas object.
Now you only need "context" infos (image data as I understand it)
You can get the image data as a base64 string like this:
function CreateDrawing(canvasId) {
let canvas = document.getElementById(canvasId);
let ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50);
ctx.lineTo(150,120);
ctx.stroke();
}
function GetDrawingAsString(canvasId) {
let canvas = document.getElementById(canvasId);
let pngUrl = canvas.toDataURL(); // PNG is the default
// or as jpeg for eg
// var jpegUrl = canvas.toDataURL("image/jpeg");
return pngUrl;
}
function ReuseCanvasString(canvasId, url) {
let img = new Image();
img.onload = () => {
// Note: here img.naturalHeight & img.naturalWidth will be your original canvas size
let canvas = document.getElementById(canvasId);
let ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
}
img.src = url;
}
// Create something
CreateDrawing("mycanvas");
// save the image data somewhere
var url = GetDrawingAsString("mycanvas");
// re use it later
ReuseCanvasString("replicate", url);
<canvas id="mycanvas"></canvas>
<canvas id="replicate"></canvas>
In short no!
You should realize the difference between a standard DOM-element and a canvas-element:
A created DOM-element is part of the mark-up language that can be viewed and changed.
In the canvas a vector image is drawn based upon the rules created in script. These rules are not stored in the element as text but as the image and can't be subtracted from the canvas element.
However there are other possibilities. We can get the variables from the ctx-object. But no info about coordinates:
var c=document.createElement("CANVAS");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50);
ctx.lineTo(150,120);
ctx.stroke();
var ctxInfo = [];
for (ctxKey in ctx)
{
if (Object.prototype.toString.call(ctx[ctxKey]) !== "[object Function]" )
{
ctxInfo.push(ctxKey + " : " + ctx[ctxKey]);
}
}
console.log(ctxInfo);
To transfer from one canvas to the other I would keep a list (array or object) of instructions and write a generic function that applies them.
canvasObject = [["beginPath"], ["moveTo", 20, 20], ["lineTo", 100, 20], ["arcTo", 150, 20, 150, 70, 50], ["lineTo", 150, 120], ["stroke"]];
function createCanvas(cnvsObj)
{
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
cnvsObj.forEach(function(element){
//loop through instructions
ctx[element[0]].apply(ctx, element.slice(1));
});
return c;
}
var a = createCanvas(canvasObject);
document.body.appendChild(a);
I'm learning to draw an image with canvas and getting a problem inside this example:
let img = new Image();
img.src = 'https://image.freepik.com/free-photo/hrc-siberian-tiger-2-jpg_21253111.jpg';
let a = function () {
let c1 = document.getElementById('c1'),
c2 = document.getElementById('c2');
c1.width = c2.width = 150;
c1.height = c2.width = 150;
c1.getContext('2d').drawImage(img, 0, 0, 150, 150);
c2.getContext('2d').drawImage(img, 0, 0, 150, 150);
};
let b = function () {
let c2 = document.getElementById('c2');
c2.width = 100;
c2.height = 100;
c2.getContext('2d').drawImage(c2, 0, 0, 100, 100);
};
let c = function () {
let c1 = document.getElementById('c1'),
c3 = document.getElementById('c3');
c3.width = 100;
c3.height = 100;
c3.getContext('2d').drawImage(c1, 0, 0, 100, 100);
};
a();
b();
c();
<div>
<canvas id="c1"></canvas>
</div>
<div>
<canvas id="c2"></canvas>
</div>
<div>
<canvas id="c3"></canvas>
</div>
Inside b function. I want to re-draw (resize) its own image with another size (changing width and height from 150 to 100). But it looks like it couldn't.
Then, I've tried to make another function (c). In this function, I've used the image of canvas c1 to re-draw image of canvas c3. That's ok.
So, my question is: Cannot canvas use its own image to draw an image for itself? (or maybe I've done something wrong)
Edit: At first I thought that using an HTMLCanvasElement in the drawImage() call was an incorrect argument type. That was wrong, it's a valid argument. The actual issue was that the code was not waiting for the image to load.
You need to take a look at how you are getting your initial image data for your first canvas. I would not expect it to work because you could be drawing the image data from img before it is actually loaded. You need to attach callbacks to img to wait for it to finish loading the image and then draw that image on a canvas.
Consider my example below that includes an asynchronous way to load an image, how to draw one canvas in another, and how to draw text (just to show differences between canvases).
function loadImage(path) {
return new Promise((resolve, reject) => {
let img = new Image();
img.addEventListener("load", () => {
resolve(img);
});
img.addEventListener("error", (err) => {
reject(err);
});
img.src = path;
});
}
loadImage("https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg")
.then((img) => {
let c1 = document.getElementById("c1"),
c2 = document.getElementById("c2"),
c1Ctx = c1.getContext("2d"),
c2Ctx = c2.getContext("2d");
c1Ctx.drawImage(img, 0, 0, 150, 150);
c1Ctx.strokeText("I'm drawing on canvas 1", 25, 25);
c2Ctx.drawImage(c1, 25, 25, 100, 100);
c2Ctx.strokeText("I'm drawing on canvas 2", 25, 25);
})
.catch(console.error);
<canvas id="c1" width="150" height="150"></canvas>
<canvas id="c2" width="150" height="150"></canvas>
Another thing that you will likely run into since you want to be able to pull data out of your canvas is CORS issues. I point this out explicitly because the image that you are trying to draw onto your canvas is from a different domain (image.freepik.com in your example). Whenever you draw image data from another domain onto a canvas, that canvas becomes tainted an you can no longer use canvas' toBlob(), toDataURL(), or getImageData() methods. See: MDN: CORS enabled image.
I want to draw an image in javascript and i dont know why its not drawing. I've look on the interweb for an answer but I cant find one. Here is the code:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = 640;
canvas.height = 480;
var playerimg = new Image();
function setup(){
render();
}
function render()
{
playerimg.onload = drawcharacter();
}
function drawcharacter(){
ctx.drawImage(playerimg, 340, 240, 50, 50);
}
playerimg.src = "playersrc.png";
window.onload = render();
render();
Your mistake is in this line:
playerimg.onload = drawcharacter();
Your intention seems to be to make drawcharacter the onload handler for playerimg. But what this line does actually do, is immediately execute drawcharacter() and then assign the return value as onload handler of playerimg. The function doesn't return anything, so your onload handler gets set to undefined.
To properly assign a function as an onload handler, omit the parenthesis:
playerimg.onload = drawcharacter;
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.
This is the button as is, it currently will get the canvas image and display that image on another canvas.
var formElement2 = document.getElementById("recImage");
formElement2.addEventListener('click', recImagePressed, false);
function recImagePressed(e){
var outputCanvas = document.getElementById("outputCanvas");
var recr = canvas2.toDataURL();
outputCtx = outputCanvas.getContext('2d');
outputCtx.drawImage(canvas2, 0, 0);
//context2.clearRect(0, 0, canvas2.width, canvas2.height);<----***
}
//***I need the image to clear when the user clicks, the above is wrong
The function that I need to react upon onclick is: (this function has been tested and works if I manually place the png into the function)
function init () { <---------this will be done away with and replaced w/
onClick ?? <-----------------****
canvas = document.getElementById('canVas');
ctx = canvas.getContext('2d');
draw ();
}
function draw() { <------This is the function that I need to react to mouse event
img = new Image();
img.src = myPng.png ***---->This is where I need the canvas image<---------------***
fr1 = makeFrame(ctx, makeVect(400,0), makeVect(400, 0), makeVect(0, 400));
img.onload = function(){
ctx.save();
newPainter = cornerSplit(imagePainter,5);
newPainter(fr1);
ctx.restore();
ctx.save();
newPainter(flipHorizLeft(fr1));
ctx.restore();
ctx.save();
newPainter(flipVertDown(fr1));
ctx.restore();
ctx.save();
newPainter(flipVertDown(flipHorizLeft(fr1)));
}
}
formElement2.onclick = function(args, ...) {
}