converting a DIV to base64 - javascript

How do I convert items inside a DIV to base64? See examle FIddle
html:
<div id="img2b64"> <img src="http://indianapublicmedia.org/stateimpact/files/2013/01/apple-image-300x300.jpg"> <div><br />
jQuery:
$('#ss').click(function(event){
var imageUrl = $('#img2b64').find('input[name=url]').val();
console.log('imageUrl', imageUrl);
convertImgToBase64(imageUrl, function(base64Img){
$('.output')
.find('textarea')
.val(base64Img)
.end()
.find('a')
.attr('href', base64Img)
.text(base64Img)
.end()
.find('img')
.attr('src', base64Img);
});
event.preventDefault();
});
function convertImgToBase64(url, callback, outputFormat){
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function(){
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img,0,0);
var dataURL = canvas.toDataURL(outputFormat || 'image/png');
callback.call(this, dataURL);
// Clean up
canvas = null;
};
img.src = url;
}

var imageUrl = $('#img2b64').find('input[name=url]').val();
There is no <input name="url"> in your code so this is returning undefined. Perhaps you meant:
var imageUrl = $('#img2b64').find('img').prop("src");
However you'll find that this introduces its own error:
Image from origin 'http://indianapublicmedia.org' has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
Meaning that you can't load that image into a Canvas because the target server does not support CORS. You'll either have to download that image to your own server first & load it into your canvas from there. Either that or enable CORS on indianapublicmedia.org

To problem that I see directly is in your jQuery where you do:
var imageUrl = $('#img2b64').find('input[name=url]').val();
Probably the console.log() below that line doesn't give you any valid URL, that should ring a bell. So the line above the console.log() doesn't do it's job.
That's because you are looking for any <input /> element (with name attribute = "url") inside the DIV with ID "img2b64". Also you are getting the value of the element with val(). You probably wanted to do something differently there.
If you want to get all images inside the DIV, then you need to simply look for img. To get the src attribute, do attr('src'):
var imageUrl = $('#img2b64').find('img').attr('src');
If you want to add an additional check for images with non-data src, do:
var imageUrl = $('#img2b64').find('img:not([src^="data"])').attr('src');
I did not check if the rest of the code (e.g. convertImgToBase64() function) is correctly working. You should always specify in your question what does work and what doesn't to get the best answers on SE.

Related

Chrome extension tampering with the http header can get the image, but it can't handle the background-image

In development, I need to judge the color of the picture. Therefore, canvas is used, but it will face cross-domain problems when processing,
I am solving it by modifying the http header, but this method is useful for image tags, but for background-image, it will be invalid.
Do you have any good solutions?
I know that extracting the background-image url, and then creating a new image should solve this problem.
I would like to ask if there is a better and more convenient way?
In html
<div id='imgwiki'>this is wrong, because use background-image</div>
<img id="imgb" src="https://ssl.gstatic.com/images/icons/material/system_gm/2x/content_copy_black_24dp.png" alt="imgb"> this is always ok. because it is a img tag
In my content.js
var img=document.getElementById("imgwiki");
img=img.computedStyleMap().get("background-image");
img.crossOrigin="Anonymous";
var canvas=document.createElement('canvas');
var ctx=canvas.getContext('2d');
ctx.drawImage(img,0,0);
var data=ctx.getImageData(0,0,canvas.width,canvas.height).data; //here cross-domain
In my background.js
chrome.webRequest.onHeadersReceived.addListener(
function(details) {
luoism.log({details}, details.url);
return {responseHeaders: [...details.responseHeaders,{name: "Access-Control-Allow-Origin",value: "*"}]};
},
{urls: ['<all_urls>']}, // filters
['blocking','responseHeaders','extraHeaders'] // extraInfoSpec
);
#wOxxOm, actually this code is ok, but i want a more directly solution:
!async function getcssimage(e) {
var stylestr=e.computedStyleMap().get('background-image').toString();
//get url
var url='',ur=/url\("([^)"]+)/,x=false;
if(!ur.test(stylestr)) return false;
url=ur.exec(stylestr);
url=url[1];
//deal mix http & https
url=luoism.config.ishttps? url.replace(/^https*/,'https'):url;
//from background-image url to <img> tag
function makeimg(url) {
return new Promise(resolve => {
let img=new Image();
img.addEventListener("load",e => {
resolve(e.currentTarget);
});
img.onerror=e => {
let t=e.currentTarget;
t.onerror="";
t.style.display='none';
e.stopPropagation();
e.preventDefault();
resolve(false);
};
img.crossOrigin="anonymous";
img.src=url;
});
}
// deal this <img>, anything is ok
var img=await makeimg(url);
var canvas=document.createElement('canvas');
var ctx=canvas.getContext('2d');
luoism.log('消息begin',img.toString(),{img});
ctx.drawImage(img,0,0);
var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;
return x;
}(img);

Base64 image not opening up when converted from Image URL

I am converting an image to Base64 from Image URL using canvas. The approach I am taking is similar to below 2 links but the converted Base64 string is not opening up the image when viewed in Chrome Tab.
Links
CONVERT Image url to Base64
(1st answer with 81 thumbs up).
https://jsfiddle.net/3qshvc54/
I tried consoling the img, canvas, ctx in fiddle and my code. The console output is the same. Please see below screenshots.
If I do it by fiddle the converted Base64 URL opens up fine in a new tab, but the one generated from my code does not display an image when opened up in a new tab.
I am using same Image URL in my code and Fiddle
Console screenshot when running from my code
Console screenshot when running from Fiddle
The Base64 generated from my code:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAABDCAYAAAAlFqMKAAAApklEQVR4Xu3VsQ0AIAwEMbL/0tShQLrejHB6kzneKjB67AKCPIsQRJD/J2EhFmIh6ZAigwwyyKQCyCCTBuPKIIMMMqkAMsikwbgyyCCDTCqADDJpMK4MMsggkwogg0wajCuDDDLIpALIIJMG48oggwwyqQAyyKTBuDLIIINMKoAMMmkwrgwyyCCTCiCDTBqMK4MMMsikAsggkwbjyiCDDDKpADL/XBfFQABED54eAwAAAABJRU5ErkJggg==
I tried validating the same online it shows that the Base64 string is valid:
When I try to decode the same:
It seems that the result of decoding is a binary data (MIME type detected as “image/png”) and because of this the data from “Text” may be damaged during the output.
My code:
<div className='image-root'>
<img id={`imageBlock-${props.photoBoxID}`} className="multi-image-photo" src={props.imgUrl} alt="photo"></img>
</div>
getBase64Image = (img) => {
img.crossOrigin = 'Anonymous';
var canvas = document.createElement("CANVAS");
canvas.width = img.width;
canvas.height = img.height;
console.log('img', img);
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
console.log('canvas', canvas);
console.log('ctx', ctx);
var dataURL = canvas.toDataURL();
console.log('dataURL', dataURL);
}
componentDidUpdate(prevProps, prevState){
if(this.props.isUploaded && (prevProps.isUploaded !== this.props.isUploaded)){
let imageRef = document.getElementById(`imageBlock-${this.props.photoBoxID}`);
imageRef.onload = this.getBase64Image(imageRef);
}
}
Can you suggest why the Base64 string generated is not opening up fine if converted from my code, though it opens up from Fiddle? Is it corrupted?
onload is an event you need function there and in that function you need to update src.
componentDidUpdate(prevProps, prevState){
if(this.props.isUploaded && (prevProps.isUploaded !== this.props.isUploaded)){
let imageRef = document.getElementById(`imageBlock-${this.props.photoBoxID}`);
imageRef.onload = () => {
delete imageRef.onload;
imageRef.src = this.getBase64Image(imageRef);
};
}
}
but better is to add base64 image to state and don't use getElementById directly in React applications.
EDIT:
for infinite loop I would create 2 images, if you need to render image on canvas you usually render outside of DOM:
componentDidUpdate(prevProps, prevState){
if(this.props.isUploaded && (prevProps.isUploaded !== this.props.isUploaded)){
let imageRef = document.getElementById(`imageBlock-${this.props.photoBoxID}`);
var srcImg = new Image();
srcImg.onload = () => {
imageRef.src = this.getBase64Image(srcImg);
};
srcImg.src = prevProps.imgUrl;
}
}
and remove the props from JSX template you will not need it.
And side note: if you have big image it's better to create object URL from blob, because there is limit of data that can be put into URL (for smaller images it should not matter):
function asObjectUrl(canvas) {
return new Promise(function(resolve) {
canvas.toBlob(function(blob) {
resolve(URL.createObjectURL(blob));
});
});
}
The object url need to be removed when not needed to not create memory leaks (use URL.revokeObjectURL)
change your img's src attribute adding src="data:image/jpeg;base64 tag with the encoded string like below:
<img id={`imageBlock-${props.photoBoxID}`} className="multi-image-photo" src="data:image/jpeg;base64, ${props.imgUrl}" alt="photo"></img>
This should work.
N.B.: Please check your imageString variable carefully, I might have used the wrong one, not sure.

Is an event triggered when an HTML link element (<a/>) containing base64 data as href is ready?

I have created a webpage that basically displays 2 images side by side.
It has a "download" button, which triggers a vanilla Javascript function, which creates a <canvas> HTML element and concatenates the two images inside of it. It then creates a link with the base64-encoded result image as href and clicks on it:
<a download="image.png" id="dllink" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAAMnCAYAAABhnf9DAAAgAElEQVR4nOzdR48kD3rn96j03pfv6qo21dVd3qT3JryP9Jll281..."></a>
Here is what the function I'm using looks like:
/**
* Create canvas, draw both images in it, create a link with the result
* image in base64 in the "href" field, append the link to the document,
* and click on it
*/
function saveImage() {
// Get left image
var imgLeft = new Image();
imgLeft.setAttribute('crossOrigin', 'anonymous');
imgLeft.src = "imgleft/" + idxImageShownLeft + ".jpg";
imgLeft.onload = function() {
// Once the left image is ready, get right image
var imgRight = new Image()
imgRight.setAttribute('crossOrigin', 'anonymous');
imgRight.src = "imgright/" + idxImageShownRight + ".jpg";
imgRight.onload = function() {
// Once the right image is ready, create the canvas
var canv = document.createElement("canvas");
var widthLeft = parseInt(imgLeft.width);
var widthRight = parseInt(imgRight.width);
var width = widthLeft + widthRight;
var height = imgLeft.height;
canv.setAttribute("width", width);
canv.setAttribute("height", height);
canv.setAttribute("id", "myCanvas");
canv.setAttribute('crossOrigin', 'anonymous');
var ctx = canv.getContext("2d");
// Draw both images in canvas
ctx.drawImage(imgLeft, 0, 0);
ctx.drawImage(imgRight, widthLeft, 0);
// Create PNG image out of the canvas
var img = canv.toDataURL("image/png");
// Create link element
var aHref = document.createElement('a');
aHref.href = img;
aHref.setAttribute("id", "dllink");
aHref.download = "image.png";
// Append link to document
var renderDiv = document.getElementById("render");
renderDiv.replaceChild(aHref, document.getElementById("dllink"));
// Click on link
aHref.click();
}
}
}
My problem is that this works fine on Firefox, but not on Chrome.
After a bit of investigating, I realized that by setting a breakpoint before the aHref.click(); line in Chrome, it worked fine. I think that it means that the aHref.click(); is called before the <a href="data:image/png;base64,...></a> is ready to be clicked, but I don't know for sure.
I couldn't find a duplicate of this topic. What keywords should I use just to be 100% sure?
Am I investigating in the right direction?
Is there an event I could rely on in order to call aHref.click(); only when it is ready?
You could wrap it in an init function that gets called when the window completes loading.
function init() {
aHref.click();
}
window.onload = init;
Its similar to the vanilla equivalent of jQuery's .ready() method.
aHref , document.getElementById("dllink") appear to be same element ? Though "dllink" has not yet been appended to document when .replaceChild called ?
Try substituting
renderDiv.appendChild(aHref);
for
renderDiv.replaceChild(aHref, document.getElementById("dllink"));

How to save image from canvas with CSS filters

I need to save an image after using CSS filters on the client-side (without using a backend). What I have so far:
Use CSS filters
Convert to canvas
Save with var data = myCanvas.toDataURL("image/png");
Crying. Image was saved without effects.
Index.html
<div class="large-7 left">
<img id="image1" src="./img/lusy-portret-ochki-makiyazh.jpg"/><br>
<canvas id="myCanvas"></canvas>
</div>
Photo.js
var buttonSave = function() {
var myCanvas = document.getElementById("myCanvas");
var img = document.getElementById('image1');
var ctx = myCanvas.getContext ? myCanvas.getContext('2d') : null;
ctx.drawImage(img, 0, 0, myCanvas.width, myCanvas.height);
var grayValue = localStorage.getItem('grayValue');
var blurValue = localStorage.getItem('blurValue');
var brightnessValue = localStorage.getItem('brightnessValue');
var saturateValue = localStorage.getItem('saturateValue');
var contrastValue = localStorage.getItem('contrastValue');
var sepiaValue = localStorage.getItem('sepiaValue');
filterVal = "grayscale("+ grayValue +"%)" + " " + "blur("+ blurValue +"px)" + " " + "brightness("+brightnessValue+"%)" + " " + "saturate(" + saturateValue +"%)" + " " + "contrast(" + contrastValue + "%)" + " " + "sepia(" + sepiaValue + "%)" ;
$('#myCanvas')
.css('filter',filterVal)
.css('webkitFilter',filterVal)
.css('mozFilter',filterVal)
.css('oFilter',filterVal)
.css('msFilter',filterVal);
var data = myCanvas.toDataURL("image/png");
localStorage.setItem("elephant", data);
if (!window.open(data)) {
document.location.href = data;
}
}
However, this produces an image without any filters.
There is a little known property on the context object, conveniently named filter.
This can take a CSS filter as argument and apply it to the bitmap. However, this is not part of the official standard and it only works in Firefox so there is the limitation.. This has since this answer was originally written become a part of the official standard.
You can check for the existence of this property and use CSS filters if it does, or use a fallback to manually apply the filters to the image if not. The only advantage is really performance when available.
CSS and DOM is a separate world from the bitmaps that are used for images and canvas. The bitmaps themselves are not affected by CSS, only the elements which acts as a looking-glass to the bitmap. The only way is to work with at pixel levels (when context's filter property is not available).
How to calculate the various filters can be found in the Filter Effects Module Level 1. Also see SVG Filters and Color Matrix.
Example
This will apply a filter on the context it self. If the filter property does not exist a fallback must be supplied (not shown here). It then extracts the image with applied filter as an image (version to the right). The filter must be set before next draw operation.
var img = new Image();
img.crossOrigin = "";
img.onload = draw; img.src = "//i.imgur.com/WblO1jx.jpg";
function draw() {
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d");
canvas.width = this.width;
canvas.height = this.height;
// filter
if (typeof ctx.filter !== "undefined") {
ctx.filter = "sepia(0.8)";
ctx.drawImage(this, 0, 0);
}
else {
ctx.drawImage(this, 0, 0);
// TODO: manually apply filter here.
}
document.querySelector("img").src = canvas.toDataURL();
}
canvas, img {width:300px;height:auto}
<canvas></canvas><img>
CSS filters applied to the canvas will not be applied to the image that is produced. You either need to replicate the filters in canvas or rather re apply the same filters to the generated image.
Try putting the generated image data into the source of an img tag & apply the same filters.
Your CSS properties are not actually applied to the canvas data. Think of the CSS as being another layer placed over the canvas element. You can implement your own image filters by using context.getImageData to get an array of raw RGBA values, then do your filter work and then write it back with context.putImageData. However, I think you really just want to save the output of the CSS filters. You may be able to do this using a tool like rasterizeHTML
Note, if src of img is not located at same origin calling var data = myCanvas.toDataURL("image/png") , may cause error
Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': tainted canvases may not be exported.
Note also that image at html at Question appear to be type jpg , not png
<img id="image1" src="./img/lusy-portret-ochki-makiyazh.jpg"/>
A possible "workaround" could be to set img src as a data URI of image ; calling
var data = myCanvas.toDataURL("image/jpg")
Though as noted , at Answers above , would not appear to preserve css filter set at img element.
Note, "workaround" ; "save image" here , would be "save html" ; as the "download" would be an objectURL of the DOM html img element.
Note also , img src within saved html file will still be original local or external src of image ; if not converted to data URI before loading.
Approach is to set window.location.href as an objectURL reference to DOM img element outerHTML , which should preserve style attribute set at .css("[vendorPrefix]-filter", filterVal)
Try utilizing URL.createObjectURL , URL.revokeObjectURL ; setting css filter at img , instead of canvas element ; creating Blob of img outerHTML , type:text/html ; create reference to URL.createObjectURL: objURL ; set window.location.href to objURL ;
call URL.revokeObjectURL on objectURL reference objURL
var buttonSave = function() {
var img = document.getElementById("image1");
// filters
var grayValue = "0.2";
var blurValue = "1px";
var brightnessValue = "150%";
var saturateValue = "0.2";
var contrastValue = "0.2";
var sepiaValue = "0.2";
// `filterVal`
var filterVal = "grayscale(" + grayValue + ") "
+ "blur(" + blurValue + ") "
+ "brightness(" + brightnessValue + ") "
+ "saturate(" + saturateValue + ") "
+ "contrast(" + contrastValue + ") "
+ "sepia(" + sepiaValue + ")";
// set `img` `filter` to `filterVal`
$(img)
.css({
"webkit-filter": filterVal,
"moz-filter": filterVal,
"ms-filter": filterVal,
"o-filter": filterVal
});
// create `blob` of `img` `outerHTML` ,
// `type`:`text/html`
var blob = new Blob([img.outerHTML], {
"type": "text/html"
});
// create `objectURL` of `blob`
var objURL = window.URL.createObjectURL(blob);
console.log(objURL);
// download `filtered` `img` as `html`
var download = $("<a />", {
"download": "image-" + $.now(),
"href": objURL,
// notify file is type `html` , not image
"title":"click to download image as `html` file"
}).appendTo("body");
$(img).appendTo("a");
$("a").on("click", function() {
// set `location.href` to `objURL`
window.location.href = objURL;
$(window).one("focus", function() {
// revoke `objURL` when `window` regains `focus`
// after "Save as" dialog
window.URL.revokeObjectURL(objURL);
});
});
}
window.onload = buttonSave;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div class="large-7 left">
<img id="image1" src="http://lorempixel.com/200/200/cats" />
</div>

Capturing load event for images dynamically changing their source

I am changing src for image dynamically and want to capture on load event for that image. Is there a way I can do it?
Several years ago, I discovered that the onload event was not reliable in some browsers (I don't remember which ones) when setting .src for the second time. As such, I settled for replacing the image object. I would create a new image object, set the onload handler, set the .src value and then when it loads, insert it into the page in place of the existing image.
When creating an image from scratch in javascript, you do this:
var img = new Image();
img.onload = function() {
// put your onload code here
};
img.src = "xxx.jpg"
If you just want to change the .src of an image, you just find the DOM object in the page and set it's .src property.
var img = document.getElementById("myImage");
img.src = "xxx.jpg";
If you want to try to capture the onload event when resetting the .src (what I had reliability problems with a couple years ago, you would do this:
var img = document.getElementById("myImage");
img.onload = function() {
// your code here
};
img.src = "xxx.jpg";
If you want to load a new image and replace an existing one with it when it loads, you would do this:
function replaceImg(oldImage, newSrc) {
var img = new Image();
img.onload = function() {
var parent = oldImage.parentNode;
parent.insertBefore(img, oldImage);
parent.removeChild(oldImage);
// put any other code you want here when the
// replacement image is loaded and in place
};
img.src = newSrc;
}
And, you would call this like this:
<img id="myImage" src="yyy.jpg">
var oldImage = document.getElementById("myImage");
replaceImg(oldImage, "xxx.jpg");
// create image object
var image = new Image();
image.onload = function () {
// do stuff
$('#myimageelementid').prop('src', image.src); // set the element in the DOM with this image
}
image.src = "image.jpg"; // will trigger onload when loaded

Categories