Object to canvas javascript - javascript

I'm testing javascript on a smart TV,
I try to get an object video to canvas. With html5 video tag it works in my browser, but not my smart TV.
But when I try with an object player, I have this error message :
Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The
provided value is not of type '(HTMLImageElement or HTMLVideoElement
or HTMLCanvasElement or ImageBitmap)'
after multiple test with (id, object id,object src..) the result is same, i don't know how i can get an Object video to a canvas.
here's a simple html test:
<canvas id="test" width="300" height="300"></canvas>
<div id="test" style="left: 0%; top: 0%; width: 25%; height: 25%; position: fixed;">
<object type="application/avplayer" style="width: 480px; height: 270px;"></object>
</div>
and the js:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.getElementsByTagName('object');
//const video = document.getElementsById('idVideo');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
Here's an example of my goal but i can't use video tag : http://jsfiddle.net/on1kh4o0/
Any idea or hack to get the same result with an object?

You can't draw an HTMLObjectElement (<object>) directly on a canvas, it is not defined as a CanvasImageSource.
For info, currently the only objects that are defined as being of this type are
HTMLImageElement
SVGImageElement
HTMLVideoElement
HTMLCanvasElement
ImageBitmap
OffscreenCanvas
And even though it's still only part of a draft specs, it is expected that CSSImageValue also gets added to this list.
But HTMLObjectElement is not part of this list, and certainly will never be.
Indeed, even though you can load a video or an image in an <object>, just like with an <iframe>, you can also load a text or an HTML document or many other document types which can't be drawn on a canvas.
Now to your issue, as has been pointed out in the comments, you are definitely facing an XY problem.
Using a video element is currently the only way to draw a video on a canvas (maybe in the future we'll be able to use the Web-Codecs API too, but that's for the future).
So try to find out why your browser doesn't want to draw this video on the canvas.
Try different videos, from different sources, back in the days some Android browsers were blocking drawing any cross-origin mp4 video on a canvas, maybe you are facing a similar issue, if you can, try to input the video from an <input type="file">.
And if your browser has debugging tools, use them. (For instance if it's based on chromium, you should be able to navigate to chrome://media-internals/ or chrome://inspect which might be bale to lead you to some logs.
But anyway, using an <object> as source here won't help you.

Related

Image on HTML5 canvas only appearing for one frame

Long time lurker but never made an account. Just wanted to preface that I'm by no means a dev and just tinkering and experimenting for fun, so I apologise in advance if I seem really dumb.
I'm working on a dynamic overlay for Twitch streaming and was previously using AS3 but I've switched over to HTML5 now. I'm trying to load an image onto the canvas (which will eventually be a profile picture fetched using Twitch API... but one step at a time). I'm using Adobe Animate and I have the following so far applied in Actions on the first frame of the layer:
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
show_image();
function show_image() {
source_image = new Image();
source_image.src = 'https://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png';
source_image.onload = function () {
context.drawImage(source_image, 100, 100);
}
}
When I hit Ctrl+Enter and see it in Chrome, the image appears for the first frame then disappears. I'm not sure how I'm supposed to get it to stay indefinitely. I need to be able to animate it later, and it'll change depending on the latest follow/donation/sub, etc.
I tried extending the frame itself in the timeline, however, this just changed long how it took to loop and didn't make the image itself stay longer. I'm probably missing something really simple!
Any help would be appreciated. Thanks!
Your code is okay if your approach is using a canvas with HTML and JS, without any libraries involved. However, this is not the case, as you are using Animate, and the way to draw graphics with it is different than using default canvas methods like drawImage().
Animate includes the CreateJS suite, which includes the EaselJS library ,and this allows you to use another tools to draw to your canvas. Two or them are the Stage object, the visual container of your animate project, and the Bitmap object, who represents an image, canvas or video. For effects of this question, only both objects are required.
Note that the code below is only for the first frame:
/* It is not necessary to declare the canvas or stage element,
as both are already declared. At this point the stage is ready to be drawn */
show_image();
function show_image() {
var source_image = new Image();
source_image.src = 'https://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png';
source_image.onload = function(event) {
/* A new Bitmap object is created using your image element */
var bmp = new createjs.Bitmap(event.currentTarget);
/* The Bitmap is added to the stage */
stage.addChild(bmp);
}
}

Test a Chart.js canvas with Protractor

I'm fairly new to protractor so sorry if this is a stupid question. I'm looking into testing an application and need test the values inside a Chart.js graph.
Has anyone got any ideas on how to get my protractor program to look inside the canvas.
This is the canvas output in HTML.
<canvas id="test"
class="chart chart-line ng-isolate-scope"
data="test.data"
labels="test.labels"
series="test.series"
legend="true"
options="test.options"
colours="test.colours"
width="1816"
height="800"
style="width: 908px;
height: 400px;">
</canvas>
Thanks in advance.
I think you may get the underlying chart data, by evaluating in the canvas's context. Example:
var canvas = element(by.css("canvas#test[data]"));
canvas.evaluate("test.data").then(function (data) {
console.log(data);
});
Protractor can get attributes of the canvas, but it can't access object created within it. But it depends what you're looking to do. Strategies for canvas testing might include image diffs, and string diffs, and often make use of browser.actions for manipulating them.

How to manage memory in case of multiple fabric js canvas?

In my application, I have multiple Fabric.js canvases, There is no limit on the number of canvases. I'll render heavy JSON via loadFromJson method of Fabric.js.
So I want to release the fabric object memory if the canvas is not in use. How can I do that?
At a time only one canvas will be visible. But I have to render all the canvases as the page loads. Canvas is actually a page and user can switch between pages via clicking on page number or something else.
Remember user can come back to any canvas any time and try to doodle or use any other Fabric.js functionality.
Here is my HTML structure:
<style>
.fabricCanvas {
border: 1px solid green;
margin: 5px;
}
.canvas-container {
margin: 5px;
}
</style>
<canvas class="fabricCanvas" width="300" height="300"></canvas>
<canvas class="fabricCanvas" width="300" height="300"></canvas>
<canvas class="fabricCanvas" width="300" height="300"></canvas>
<canvas class="fabricCanvas" width="300" height="300"></canvas>
My JS code to store fabric instances
var canvasInstances = [];
$('canvas.fabricCanvas').each(function () {
var fabricCanvasObj = new fabric.Canvas(this, {
isDrawingMode: true
});
canvasInstances.push(fabricCanvasObj);
fabricCanvasObj.renderAll();
});
console.log(canvasInstances[0]);
I am storing instances so that I can use them later. I want this for better memory management, basically loading and unloading instances as and when needed.
Sample situation DEMO is here. In this demo consider that the canvases are over each other using z-indexes but they are the part of DOM and has already been rendered on page load.
Let me know in case of any doubt, I can explain further.
When ever there are more than 5 canvases iPad browser crashes which I think is the memory issue.
You might be interested in 3 things (in the order of significance/destruction):
canvas.clear() — removes all canvas objects from it.
canvas.dispose() — removes all canvas objects AND removes all event listeners
$(canvas.wrapperEl).remove() (using jQuery for illustrative purposes) — to remove canvas wrapper element (which contains upper and lower canvases used by Fabric). This can be done AFTER you call dispose, if the goal is to completely remove Fabric canvas from a document.

DOM Security Exception 18: Tainted Canvas

I'm nearly finished with a Javascript/HTML5-based game, and i've been testing it by using Chrome to open the HTML page on my local file system (i haven't uploaded anything anywhere). I'm using Chrome's file:// protocol to do this. But i'm running into a problem... At the beginning of the game, i display an image for a couple seconds before moving onto the menu screen. I pause the game by grabbing the canvas' pixel data, displaying that, then drawing a semi-transparent rectangle across the whole thing, with a crosshair as a custom pointer. However, Chrome is giving me trouble about a DOM Security Exception 18: "Unable to get image data from canvas because the canvas has been tainted by cross-origin data."
So i did some research on the Internet, and it turns out this is because Chrome sees that the image is grabbed from the local file system, and sees this as a security error. Using this question as a reference, i tried doing some research on Cross-Origin Resource Sharing, but quickly got lost. I figured it would be much easier to simply open the test HTML file using http:// and localhost like the question answerer suggested. But i have no idea how to do this, either.
I'd really like to use Chrome to continue testing my game (the developer tools accessed through Ctrl-Shift-I have proved to be invaluable), so i figured there were three solutions: Either figure out what CORS is and how to use it, learn how to open a local file using http://, or somehow hard-code my image data as a variable in my JavaScript script file (like a XPM file in C). I don't know how to do the first two, and i'm trying to avoid the third.
Yes, it’s probably time to download a local web server or sign up for a hosted server.
But if you want to continue testing without a server, you can sign up for a free dropbox.com account and host your images there.
Dropbox allows access to images using CORS friendly crossOrigin=”anonymous”.
Then CORS is no problem on Chrome & Mozilla. But, IE still fails to be CORS friendly—come on IE :(
Here’s how to load an image without CORS problems from dropbox (Chrome & Mozilla, not IE).
The “secret” is setting image.crossOrigin=”anonymous” before setting the image.src:
var externalImage2=document.createElement("img");
externalImage2.onload=function(){
canvas.width=externalImage2.width;
canvas.height=externalImage2.height;
ctx.drawImage(externalImage2,0,0);
// use getImageData to replace blue with yellow
var imageData=recolorImage(externalImage2,0,0,255,255,255,0);
// put the altered data back on the canvas
// this will FAIL on a CORS violation
ctxAnonymous.putImageData(imageData,0,0);
}
externalImage2.crossOrigin = "Anonymous";
externalImage2.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/colorhouse.png";
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/YdzHT/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasCORS=document.getElementById("canvasCORS");
var ctxCORS=canvasCORS.getContext("2d");
var canvasAnonymous=document.getElementById("canvasAnonymous");
var ctxAnonymous=canvasAnonymous.getContext("2d");
// Using image WITHOUT crossOrigin=anonymous
// Fails in all browsers
var externalImage1=new Image();
externalImage1.onload=function(){
canvas.width=externalImage1.width;
canvas.height=externalImage1.height;
ctx.drawImage(externalImage1,0,0);
// use getImageData to replace blue with yellow
var imageData=recolorImage(externalImage1,0,0,255,255,255,0);
// put the altered data back on the canvas
// this will FAIL on a CORS violation
ctxCORS.putImageData(imageData,0,0);
}
externalImage1.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/colorhouse.png";
// Using image WITH crossOrigin=anonymous
// Succeeds in Chrome+Mozilla, Still fails in IE
var externalImage2=new Image();
externalImage2.onload=function(){
canvas.width=externalImage2.width;
canvas.height=externalImage2.height;
ctx.drawImage(externalImage2,0,0);
// use getImageData to replace blue with yellow
var imageData=recolorImage(externalImage2,0,0,255,255,255,0);
// put the altered data back on the canvas
// this will FAIL on a CORS violation
ctxAnonymous.putImageData(imageData,0,0);
}
externalImage2.crossOrigin = "Anonymous";
externalImage2.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/colorhouse.png";
function recolorImage(img,oldRed,oldGreen,oldBlue,newRed,newGreen,newBlue){
var c = document.createElement('canvas');
var ctx=c.getContext("2d");
var w = img.width;
var h = img.height;
c.width = w;
c.height = h;
// draw the image on the temporary canvas
ctx.drawImage(img, 0, 0, w, h);
// pull the entire image into an array of pixel data
var imageData = ctx.getImageData(0, 0, w, h);
// examine every pixel,
// change any old rgb to the new-rgb
for (var i=0;i<imageData.data.length;i+=4)
{
// is this pixel the old rgb?
if(imageData.data[i]==oldRed &&
imageData.data[i+1]==oldGreen &&
imageData.data[i+2]==oldBlue
){
// change to your new rgb
imageData.data[i]=newRed;
imageData.data[i+1]=newGreen;
imageData.data[i+2]=newBlue;
}
}
return(imageData);
}
}); // end $(function(){});
</script>
</head>
<body>
<p>Original external image</p>
<canvas id="canvas" width=140 height=140></canvas>
<p>.getImageData with .crossOrigin='anonymous'
<p>[Succeeds in Chrome+Mozilla, still fails in IE]</p>
<canvas id="canvasAnonymous" width=140 height=140></canvas>
<p>.getImageData without .crossOrigin='anonymous'
<p>[Fails on all browsers]</p>
<canvas id="canvasCORS" width=140 height=140></canvas>
</body>
</html>
Developing using the local file system is generally not a good idea for precisely the reason you have discovered. To use the localhost option you'll need a web server installed on your PC. Google for a WAMP package (Windows, Apache. MysQL, PHP) which should give you everything you need.
Unfortunately, CORS will only work for you if you have a web server!
[edit] You can get a WAMP server from wampserver.com, obviously!

How to detect if an image generated via canvas is blank (transparent PNG)?

I am working on an application in which an image is created/edited on a HTML5 canvas and then saved into a file-store/cloud. The problem is that of "saving efficiency". On save of a blank canvas, i.e. a totally transparent blank PNG is sent with toDataURL(). One way of detecting a blank PNG is by switching a boolean value upon click of any editing/drawing functionality and reseting that value upon clear-screen.
However, such a method is not foolproof because a user may save the image after clicking a draw/edit function and yet not draw anything. Is there a more native approach to detect if the canvas returns a binary string that has changed since opening up on the browser? Or some other way to ensure that a blank transparent PNG is detected at client side?
HTML:
<canvas id="canvas_img" width="300" height="200" border="0"></canvas>
SCRIPT:
isCanvasTransparent(document.getElementById("canvas_img"));
function isCanvasTransparent(canvas) { // true if all pixels Alpha equals to zero
var ctx=canvas.getContext("2d");
var imageData=ctx.getImageData(0,0,canvas.offsetWidth,canvas.offsetHeight);
for(var i=0;i<imageData.data.length;i+=4)
if(imageData.data[i+3]!==0)return false;
return true;
}
UPDATE:
Dont use CSS style declarations like border: 1px solid black; for CANVAS, because border included into canvas image, and, as result, alpha chanel is always not equals to zero.
This isn't native, but this should work, because a blank canvas always generates the same data URL.
So, you could create a hidden canvas, get that canvas's data URL and if it matches that of your editor, then don't upload it. Simple as that.
Demo. First, go hit save without going over the canvas. Then go over it and then hit save. Tada!

Categories