fiveworlds Posted June 14, 2016 Posted June 14, 2016 Trying to update text in canvas saving the background. So far I haven't been able to not use clear rect. I can do it using a framework but I would rather be able to do it without one. <canvas id="myCanvas" width="578" height="200"></canvas> <script> var message, canvas = document.getElementById('myCanvas'), context = canvas.getContext('2d'), boundingrect = canvas.getBoundingClientRect(); function getMousePos(e) { return { x: e.clientX - boundingrect.left, y: e.clientY - boundingrect.top }; } canvas.addEventListener('mousemove', function(e) { mousePos = getMousePos(e); message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y; context.clearRect(0, 0, canvas.width, canvas.height); context.font = '18pt Calibri'; context.fillStyle = 'black'; context.fillText(message, 10, 25); }, false); </script> Here I can save the background data and then redraw it but that isn't a good solution. <canvas id="myCanvas" width="578" height="200"></canvas> <script> var message, canvas = document.getElementById('myCanvas'), context = canvas.getContext('2d'), boundingrect = canvas.getBoundingClientRect(); function getMousePos(e) { return { x: e.clientX - boundingrect.left, y: e.clientY - boundingrect.top }; } context.rect(10,10,150,100); context.stroke(); canvasdata = context.getImageData(0, 0, canvas.width, canvas.height); canvas.addEventListener('mousemove', function(e) { mousePos = getMousePos(e); message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y; context.clearRect(0, 0, canvas.width, canvas.height); context.font = '18pt Calibri'; context.fillStyle = 'black'; context.putImageData(canvasdata, 0, 0); context.fillText(message, 10, 25); }, false); </script> Is there a way to update already filled text??
Endy0816 Posted June 14, 2016 Posted June 14, 2016 Is there a way to update already filled text?? Unfortunately no, though you can use multiple canvas elements as layers.
fiveworlds Posted June 15, 2016 Author Posted June 15, 2016 Okay well that was actually very helpful. Still haven't got it perfect though. I can create an offscreen canvas layer and then merge the layers. However at the moment it isn't detecting transparency around the text perfectly it has a white border. <canvas id="myCanvas" width="578" height="200"></canvas> <script> var message, canvas = document.getElementById('myCanvas'), context = canvas.getContext('2d'), boundingrect = canvas.getBoundingClientRect(); function getMousePos(e) { return { x: e.clientX - boundingrect.left, y: e.clientY - boundingrect.top }; } canvas.offscreenCanvas = document.createElement("canvas"); canvas.offscreenCanvas.width = canvas.width; canvas.offscreenCanvas.height = canvas.height; canvas.offscreenContext = canvas.offscreenCanvas.getContext("2d"); canvas.offscreenCanva = document.createElement("canvas"); canvas.offscreenCanva.width = canvas.width; canvas.offscreenCanva.height = canvas.height; canvas.offscreenContex = canvas.offscreenCanva.getContext("2d"); canvas.offscreenContex.fillStyle = 'blue'; canvas.offscreenContex.fillRect(0,0,canvas.width,canvas.height); canvas.offscreenContex.stroke(); canvas.offscreenContex.canvasdata = canvas.offscreenContex.getImageData(0, 0, canvas.width, canvas.height); canvas.addEventListener('mousemove', function(e) { mousePos = getMousePos(e); message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y; canvas.offscreenContex.fillStyle = 'blue'; canvas.offscreenContex.fillRect(0,0,canvas.width,canvas.height); canvas.offscreenContex.stroke(); canvas.offscreenContex.canvasdata = canvas.offscreenContex.getImageData(0, 0, canvas.width, canvas.height); context.clearRect(0, 0, canvas.width, canvas.height); canvas.offscreenContext.globalAlpha = 0; canvas.offscreenContext.clearRect(0, 0, canvas.width, canvas.height); canvas.offscreenContext.font = '18pt Calibri';context.fillStyle = 'black'; canvas.offscreenContext.globalAlpha = 1; canvas.offscreenContext.fillText(message, 0, 18); canvas.offscreenContext.canvasdata = canvas.offscreenContext.getImageData(0, 0, canvas.width, canvas.height); imageDataArray = [canvas.offscreenContex.canvasdata,canvas.offscreenContext.canvasdata]; mergeLayerData (imageDataArray); }, false); function mergeLayerData( imageDataArray ) { var newImageData = imageDataArray[ 0 ]; for ( var j = 1, len = imageDataArray.length; j < len; j++ ) { for ( var i = 0, bytes = imageDataArray[ j ].data.length; i < bytes; i += 4 ) { var index = ( imageDataArray[ j ].data[ i + 3 ] === 0 ? 0 : j ); newImageData.data[ i ] = imageDataArray[ index ].data[ i ]; newImageData.data[ i + 1 ] = imageDataArray[ index ].data[ i + 1 ]; newImageData.data[ i + 2 ] = imageDataArray[ index ].data[ i + 2 ]; newImageData.data[ i + 3 ] = imageDataArray[ index ].data[ i + 3 ]; } } context.putImageData( newImageData, 0, 0 ); }; </script>
Sensei Posted June 15, 2016 Posted June 15, 2016 (edited) I am still not sure what for all this you need. Your examples from 1st post didn't blink at all here. Double buffering is useful to remove such blinking, while drawing, for a too long time, on on-screen bitmap. See answers here: http://stackoverflow.com/questions/2795269/does-html5-canvas-support-double-buffering Answer with 69 votes has code which is switching back and forth overlayed element. And draw to the one currently invisible. I realized that you could place one element over another element, same position, CSS or dynamically created. Then set background on deeper one, and draw text on "top" one, but in such way that text is on "top", the rest pass through whatever is below. Like while programming sprites with transparent regions. Edited June 15, 2016 by Sensei
fiveworlds Posted June 15, 2016 Author Posted June 15, 2016 (edited) Okay so I eventually figured out how to do it using offscreen canvas. The idea being that prior to drawing my text on the canvas I save the portion of the background that it covers to the offscreen canvas and so only redraw that section when the text is updated. <canvas id="myCanvas" width="578" height="200"></canvas> <script> var message, canvas = document.getElementById('myCanvas'), context = canvas.getContext('2d'), boundingrect = canvas.getBoundingClientRect(); function getMousePos(e) { return { x: e.clientX - boundingrect.left, y: e.clientY - boundingrect.top }; } context.fillStyle = 'green'; context.fillRect(0,0,200,100); offscreenCanvas = document.createElement("canvas"); offscreenCanvas.width = canvas.width; offscreenCanvas.height = 26; offscreenContext = offscreenCanvas.getContext("2d"); offscreenContext.canvasdata = context.getImageData(0, 0, canvas.width, canvas.height); offscreenContext.putImageData(offscreenContext.canvasdata,0,0); canvas.addEventListener('mousemove', function(e) { mousePos = getMousePos(e); message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y; context.clearRect(0, 0, canvas.width, 26); context.fillStyle = 'black'; context.putImageData (offscreenContext.getImageData(0, 0, canvas.width, canvas.height),0,0); context.font = '18pt Times New Roman'; context.fillStyle = 'black'; context.globalAlpha = 1; context.fillText(message, 0, 18); }, false); </script> Edited June 15, 2016 by fiveworlds
Endy0816 Posted June 15, 2016 Posted June 15, 2016 (edited) If it works I wouldn't worry about it, but meant something along these lines. Similar to what Sensei mentioned using CSS for absolute positioning. <!doctype html> <html lang="en"> <head> <style> body { background-color: blue; } canvas { position: absolute; left: 100px; top: 50px; } </style> </head> <body> <canvas id="gameCanvas" width="578" height="200"></canvas> <canvas id="textCanvas" width="578" height="200"></canvas> <script> var gameCanvas=document.getElementById("gameCanvas"); var ctx=gameCanvas.getContext("2d"); ctx.fillStyle="red"; ctx.fillRect(0,0, gameCanvas.width, gameCanvas.height); var message, canvas = document.getElementById('textCanvas'), context = canvas.getContext('2d'), boundingrect = canvas.getBoundingClientRect(); function getMousePos(e) { return { x: e.clientX - boundingrect.left, y: e.clientY - boundingrect.top }; } canvas.addEventListener('mousemove', function(e) { mousePos = getMousePos(e); message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y; context.clearRect(0, 0, canvas.width, canvas.height); context.font = '18pt Calibri'; context.fillStyle = 'black'; context.fillText(message, 10, 25); }, false); </script> </body> </html> Granted can see some limitations with this too, so depends on what you are looking for. Edited June 15, 2016 by Endy0816
fiveworlds Posted June 15, 2016 Author Posted June 15, 2016 CSS for absolute positioning. Why would you do that?? A game should be made with. body{margin:0px;} canvas{width:100%;height:100%;} This allows the game to run full-screen on all platforms. Then if you are including a game in a container it should be in an iframe. Granted can see some limitations with this too I did look at doing things that way too. However if you have multiple canvases all the event listeners have to be on the topmost canvas.
Sensei Posted June 15, 2016 Posted June 15, 2016 (edited) Why would you do that?? A game should be made with. body{margin:0px;} canvas{width:100%;height:100%;}This allows the game to run full-screen on all platforms. Then if you are including a game in a container it should be in an iframe. He used left,top, so these two elements overlap each other.. Without them elements would be one below another, not overlapping. Could be mixed with width,height. Or use JavaScript to set position and size at start up. Or even generate these elements dynamically. Edited June 15, 2016 by Sensei
fiveworlds Posted June 15, 2016 Author Posted June 15, 2016 I'd use canvas{position:fixed;top:0px;left:0px;width:100%;height:100%} to make them overlap. It is good practice to make all games suited to being put in an iframe because most sites with games force you to use iframes facebook etc.
Endy0816 Posted June 15, 2016 Posted June 15, 2016 https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events Somewhat ironically made for SVG originally.
fiveworlds Posted June 16, 2016 Author Posted June 16, 2016 While writing a click event listener with transparency I noticed the code was throwing a weird error in chrome. Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data. If I run the code on my server as localhost or my domain it isn't throwing an error. <!DOCTYPE HTML> <html> <head> <style> body { margin: 0px; padding: 0px; } </style> </head> <body> <canvas id="myCanvas" width="800" height="400"></canvas> <script> var canvas = document.getElementById('myCanvas'); var context = canvas.getContext('2d'); var boundingrect = canvas.getBoundingClientRect(); function getMousePos(e) { return { x: e.clientX - boundingrect.left, y: e.clientY - boundingrect.top }; } var imageObj = new Image(); imageObj.onload = function() { canvas_img = []; t=canvas_img.length; offscreenCanvas = document.createElement("canvas"); offscreenCanvas.width = imageObj.naturalWidth; offscreenCanvas.height = imageObj.naturalHeight; offscreenContext = offscreenCanvas.getContext("2d"); offscreenContext.drawImage(imageObj, 0, 0); canvasdata=offscreenContext.getImageData(0,0, offscreenCanvas.width, offscreenCanvas.height); for (j=10; j<(canvas.height-imageObj.naturalHeight); j=j+10+imageObj.naturalHeight){ for (i=10; i<(canvas.width-imageObj.naturalWidth); i=i+10+imageObj.naturalWidth){ canvas_img[t]={ x : i, y : j, data : imageObj, imagedata : canvasdata, src : imageObj.src, width : imageObj.naturalWidth, height : imageObj.naturalHeight }; context.drawImage(imageObj, i, j); t=t+1; } } }; imageObj.src = 'king_of_hearts.png'; canvas.addEventListener('click', function(e) { mousePos = getMousePos(e); for(i=0; i<canvas_img.length; i++){ if(mousePos.x > canvas_img[i].x && mousePos.y > canvas_img[i].y) { if(mousePos.x < (canvas_img[i].x+canvas_img[i].width) && mousePos.y < (canvas_img[i].y+canvas_img[i].height)) { if(canvas_img[i].imagedata.data[((mousePos.y-canvas_img[i].y)*canvas_img[i].width+(mousePos.x-canvas_img[i].x))*4+3]===0){ } else { alert(canvas_img[i].src); } } } } }); </script> </body> </html>
Sensei Posted June 16, 2016 Posted June 16, 2016 Dump width and height somewhere, so you see their value. http://stackoverflow.com/questions/22097747/getimagedata-error-the-canvas-has-been-tainted-by-cross-origin-data
fiveworlds Posted June 16, 2016 Author Posted June 16, 2016 console.log(offscreenCanvas.width offscreenCanvas.height); Returns 172 250. So both variables are set. There is no error if run on my server but then the path isn't the file:/// method it is http://. Why would the file method cause an error??
Sensei Posted June 16, 2016 Posted June 16, 2016 Did you read my links? http://stackoverflow.com/questions/22097747/getimagedata-error-the-canvas-has-been-tainted-by-cross-origin-data/27840082#27840082
fiveworlds Posted June 16, 2016 Author Posted June 16, 2016 (edited) Yeah only Kirby's method didn't refer to things I could do on my server (or settings on a server that was hosting me) and it didn't work the error was still thrown. I tried the mozilla method https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image var img = new Image, canvas = document.createElement("canvas"), ctx = canvas.getContext("2d"), src = "http://example.com/image"; // insert image url here img.crossOrigin = "Anonymous"; img.onload = function() { canvas.width = img.width; canvas.height = img.height; ctx.drawImage( img, 0, 0 ); localStorage.setItem( "savedImageData", canvas.toDataURL("image/png") ); } img.src = src; // make sure the load event fires for cached images too if ( img.complete || img.complete === undefined ) { img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; img.src = src; } but got the error Image from origin 'file://' has been blocked from loading by Cross-Origin Resource Sharing policy: Invalid response. Origin 'null' is therefore not allowed access. It would probably work on localhost but I am trying to make it work just running the file from my desktop. Edited June 16, 2016 by fiveworlds
Sensei Posted June 16, 2016 Posted June 16, 2016 (edited) Tried? img.crossOrigin = "Anonymous"; Together with Access-Control-Allow-Origin "*" Like they said in threads.. ? The whole thing is about origin of image. If it's dynamic/loaded from server outside of our control, it can change content to something else, so it could be used to in inject something to our server. Edited June 16, 2016 by Sensei
fiveworlds Posted June 16, 2016 Author Posted June 16, 2016 (edited) Tried? img.crossOrigin = "Anonymous"; Together with Access-Control-Allow-Origin "*" Like they said in threads.. ? That's Apache webserver as I was saying there seems to be no way of just running the file from my desktop without using a webserver. <IfModule mod_setenvif.c><IfModule mod_headers.c> <FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$"> SetEnvIf Origin ":" IS_CORS Header set Access-Control-Allow-Origin "*" env=IS_CORS </FilesMatch> </IfModule> </IfModule> You can't set the header using xmlhttprequests previous = undefined; function pagereloader() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (xhttp.readyState == 4 && xhttp.status == 200) { current = escape(xhttp.responseText.toString()); if(previous){ if(previous!==current){location.reload();} } previous=current; } }; xhttp.open("GET", window.location.href, true); xhttp.setRequestHeader("Access-Control-Allow-Origin","*"); xhttp.send(); } setTimeout(pagereloader, 1000); returned... XMLHttpRequest cannot load file:///C:/Users/David/Desktop/New%20folder%20(5)/test.html. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource. Edited June 16, 2016 by fiveworlds
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now