How can I get this function to react to the onclick method? - javascript

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, ...) {
}

Related

Javascript Canvas not Updating Correctly on Form Change

I am working on a character creator. You select different costumes from the form on the left and then the canvas on the right changes to reflect them.
I am having an issue with costumes changing on form change. Here is the demo website:
https://elegant-bonbon-6a82f0.netlify.app/
Currently, you can only make an image show up if the color is changed. Both onchange="" functions for select and color types run the same code (select form elements go through an if-then statement first and the call updatec). At first I thought it was an issue with the code adding the image to the canvas before the image loads, but that is not the case. I added a promise to wait for the image to load (My knowledge of promises is minimal, so I may have done something wrong).
Here's a demo what I am trying to accomplish:
https://jasmine-mogadam.github.io/
This uses a setInterval loop that updates the canvas every few milliseconds. This issue with this is the more elements in the character creator, the more laggy the site becomes (also the setInterval loop no longer works for the first/netlify demo. That's another issue).
In summary, I'm trying to figure out what is different between my color updatec() and select update(). Why does color update the canvas correctly, but not the select elements?
HTML Form Snippet:
<form>
<label for="Shape">Shape:</label>
<select id="Shape" name="Shape" onchange="update(0, 'Circle,Square,Triangle')">
<option value="Circle">Circle</option>
<option value="Square">Square</option>
<option value="Triangle">Triangle</option>
</select>
<label for="color0">Shape Color:</label>
<input type="color" value="#B3B3B3" id="color0" name="Shape Color" onchange="updatec(0)"></div>
</form>
Functions
function colorCanvasImage(canvas,id) {
canvas.width = 500;
canvas.height = 500;
var context = canvas.getContext('2d');
var image = document.getElementById('image' + id);
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
// Set the canvas the same width and height of the image
//updates color every 10 miliseconds
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);
changeToColor(imageData.data,id);
context.putImageData(imageData, 0, 0);
console.log("Painting " + image.src);
loadImage(image.src).then(addEffect(id,context,canvas,image));
addEffect(id,context,canvas,image)
//let loop = setInterval(addEffect(id,context,canvas,image), 100);
}
//function drawImage(image,canvas,id) {
function drawImage(canvas,id) {
var context = canvas.getContext('2d');
var image = document.getElementById('image' + id);
//loadImage(image)
console.log("Drawing " + image.src);
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);
}
function addEffect(id,context,canvas,image) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);
var image = document.getElementById('image' + id);
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
changeToColor(imageData.data,id);
context.putImageData(imageData, 0, 0);
}
//function credit - https://stackoverflow.com/questions/21646738/convert-hex-to-rgba
function hexToRGB(hex, alpha) {
var r = parseInt(hex.slice(1, 3), 16),
g = parseInt(hex.slice(3, 5), 16),
b = parseInt(hex.slice(5, 7), 16);
if (alpha) {
return {r,g,b,alpha};
} else {
return [r,g,b];
}
}
function changeToColor(data,id) {
const colors = hexToRGB(document.getElementById("color"+id).value,false);
for (var i = 0; i < data.length; i+=4 ) {
if(data[i]!=0||data[i+3]==0){
data[i] = colors[0];
data[i+1] = colors[1];
data[i+2] = colors[2];
}
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function loadImage(url) {
return new Promise(resolve => {
image = new Image();
image.addEventListener('load', () => {
resolve(image);
});
image.src = url;
});
}
function update(id,list){
let costumes = list.split(",");
var canvas = document.getElementById("canvas"+id);
var object = document.getElementById("form").elements[id*2].value;
var image;
console.log("Selected: " + object);
costumes.forEach(costume =>{
if(object === costume){
document.getElementById("image"+id).src = costume + ".PNG";
image = costume + ".PNG";
}
});
updatec(id);
}
function updatec(id){
var canvas = document.getElementById("canvas"+id);
colorCanvasImage(canvas,id)
}
Thanks for reading and I hope you have a great day :)
This is an issue with async await.
When using promises to make a function wait for an image to be loaded, the function needs to be declared as async, and the promise function needs to be prefaced with await
async function colorCanvasImage(canvas,id) {
// init stuff
await loadImage(image.src).then(addEffect(id,context,canvas,image));
}
I tried this out on the website and have updated it. It works like a charm :)
https://elegant-bonbon-6a82f0.netlify.app/

Background Image Canvas [duplicate]

I am drawing on the canvas each time a user presses a button, however sometimes the image is not getting drawn on the canvas. I think this could be that the image isn't loaded in time before the context.drawimage function runs, as some of the smaller files sometimes get drawn. I've used the console and checked resources and so this is the only problem I can think of.
How do I avoid this problem?
This is my Javascript code.
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var questionbg = new Image();
var answerbg = new Image();
//this code is inside a function that is called each time a user presses a button
if(questiontype == "text"){
questionbg.src = "./resources/textquestionbg.png";
context.drawImage(questionbg, 0, 0);
}
//if image question
else if(questiontype == "image"){
questionbg.src = "./resources/imageaudiovideoquestionbg.png";
context.drawImage(questionbg, 0, 0);
}
//if audio question
else if(questiontype == "audio"){
questionbg.src = "./resources/imageaudiovideoquestionbg.png";
context.drawImage(questionbg, 0, 0);
}
//else it is a video question
else{
questionbg.src = "./resources/imageaudiovideoquestionbg.png";
context.drawImage(questionbg, 0, 0);
}
You should check if the image is loaded. If not then listen to the load event.
questionbg.src = "./resources/imageaudiovideoquestionbg.png";
if (questionbg.complete) {
context.drawImage(questionbg, 0, 0);
} else {
questionbg.onload = function () {
context.drawImage(questionbg, 0, 0);
};
}
MDN (Mozilla Doc, great source btw) suggests:
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.onload = function(){
ctx.drawImage(img,0,0);
ctx.beginPath();
ctx.moveTo(30,96);
ctx.lineTo(70,66);
ctx.lineTo(103,76);
ctx.lineTo(170,15);
ctx.stroke();
};
img.src = '/files/4531/backdrop.png';
}
Obviously, you are not wanting to apply the stroke or fill. However, the idea is the same.

How do I set crossOrigin attribute when using canvas.toDataURL?

So I'm trying to create a print map function for an OpenLayers 3 application I'm building. I'm aware of their example but whenever I attempt to use it I run into the dreaded tainted canvas issue. I've read the whole internet and come across folks saying first to set CORS correctly (done and done) but also to do:
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;
The above is described here.
My question is, I've never really used toDataURL() before and I'm not really sure how I make sure the image being created has the crossOrigin attribute correctly set before it slams into the:
Error: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
Any thoughts?
I have seen this. My question is how they incorporate that into a function that works. Something like:
var printMap = function(){
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;
img.onload = function() {
var canvas = document.getElementsByTagName('canvas');
var dataURL = canvas.toDataURL("image/png");
console.log(dataURL);
};
};
If the crossOrigin property/attribute is supported by the browser (it is now in FF, Chrome, latest Safari and Edge ), but the server doesn't answer with the proper headers (Access-Control-Allow-Origin: *), then the img's onerror event fires.
So we can just handle this event and remove the attribute if we want to draw the image anyway.
For browsers that don't handle this attribute, the only way o test if the canvas is tainted is to call the toDataURL into a try catch block.
Here is an example :
var urls =
["http://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
"http://lorempixel.com/200/200"];
var tainted = false;
var img = new Image();
img.crossOrigin = 'anonymous';
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
var load_handler = function() {
canvas.width = 200;
canvas.height = 200;
ctx.fillStyle = 'white';
ctx.font = '15px sans-serif';
ctx.drawImage(this, 0, 0, 200, 200*(this.height/this.width));
// for browsers supporting the crossOrigin attribute
if (tainted) {
ctx.strokeText('canvas tainted', 20, 100);
ctx.fillText('canvas tainted', 20, 100);
} else {
// for others
try {
canvas.toDataURL();
} catch (e) {
tainted = true;
ctx.strokeText('canvas tainted after try catch', 20, 100);
ctx.fillText('canvas tainted after try catch', 20, 100);
}
}
};
var error_handler = function() {
// remove this onerror listener to avoid an infinite loop
this.onerror = function() {
return false
};
// certainly that the canvas was tainted
tainted = true;
// we need to removeAttribute() since chrome doesn't like the property=undefined way...
this.removeAttribute('crossorigin');
this.src = this.src;
};
img.onload = load_handler;
img.onerror = error_handler;
img.src = urls[0];
btn.onclick = function() {
// reset the flag
tainted = false;
// we need to create a new canvas, or it will keep its marked as tainted flag
// try to comment the 3 next lines and switch multiple times the src to see what I mean
ctx = canvas.cloneNode(true).getContext('2d');
canvas.parentNode.replaceChild(ctx.canvas, canvas);
canvas = ctx.canvas;
// reset the attributes and error handler
img.crossOrigin = 'anonymous';
img.onerror = error_handler;
img.src = urls[+!urls.indexOf(img.src)];
};
<button id="btn"> change image src </button><br>
But since toDataURL can be a really heavy call for just a check and that code in try catch is deoptimized, a better alternative for older browsers is to create a 1px*1px tester canvas, draw the images on it first and call its toDataURL in the try-catch block :
var urls = ["http://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", "http://lorempixel.com/200/200"];
var img = new Image();
img.crossOrigin = 'anonymous';
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
//create a canvas only for testing if our images will taint our canvas or not;
var taintTester = document.createElement('canvas').getContext('2d');
taintTester.width = 1;
taintTester.height = 1;
var load_handler = function() {
// our image flag
var willTaint = false;
// first draw on the tester
taintTester.drawImage(this, 0, 0);
// since it's only one pixel wide, toDataURL is way faster
try {
taintTester.canvas.toDataURL();
} catch (e) {
// update our flag
willTaint = true;
}
// it will taint the canvas
if (willTaint) {
// reset our tester
taintTester = taintTester.canvas.cloneNode(1).getContext('2d');
// do something
ctx.fillStyle = 'rgba(0,0,0,.7)';
ctx.fillRect(0, 75, ctx.measureText('we won\'t diplay ' + this.src).width + 40, 60);
ctx.fillStyle = 'white';
ctx.font = '15px sans-serif';
ctx.fillText('we won\'t diplay ' + this.src, 20, 100);
ctx.fillText('canvas would have been tainted', 20, 120);
} else {
// all clear
canvas.width = this.width;
canvas.height = this.height;
ctx.fillStyle = 'white';
ctx.font = '15px sans-serif';
ctx.drawImage(this, 0, 0);
}
};
var error_handler = function() {
// remove this onerror listener to avoid an infinite loop
this.onerror = function() {
return false
};
// we need to removeAttribute() since chrome doesn't like the property=undefined way...
this.removeAttribute('crossorigin');
this.src = this.src;
};
img.onload = load_handler;
img.onerror = error_handler;
img.src = urls[0];
btn.onclick = function() {
// reset the attributes and error handler
img.crossOrigin = 'anonymous';
img.onerror = error_handler;
img.src = urls[+!urls.indexOf(img.src)];
};
<button id="btn">change image src</button>
Note
Cross-origin requests are not the only way to taint a canvas :
In IE < Edge, drawing an svg on the canvas will taint the canvas for security issues, in the same way, latest Safari does taint the canvas if a <foreignObject> is present in an svg drawn onto the canvas and last, any UA will taint the canvas if an other tainted canvas is painted to it.
So the only solution in those cases to check if the canvas is tainted is to try-catch, and the best is to do so on a 1px by 1px test canvas.
So Pointy and Kaiido both had valid ways of making this work but they both missed that this was an OpenLayers issue (and in the case of Pointy, not a duplicate question).
The answer was to do this:
source = new ol.source.TileWMS({
crossOrigin: 'anonymous'
});
Basically you had to tell the map AND the layers that you wanted crossOrigin: anonymous. Otherwise your canvas would still be tainted. The more you know!

drawimage() not working in js

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;

HTML5 draw text that stays on the top

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 :)

Categories