How can I save fragment in memory?
var img = document.getElementById('img1');
ctx.save();
ctx.rotate(Math.PI / 180 * 45);
ctx.drawImage(img, 100, 100, img.width, img.height);
ctx.restore();
And now I want save this fragment (already rotated) to some array, and then just call drawImage without new rotating.
Is it real in JS and canvas or I should rotate image every time, when I want draw it?
var url = canvas.toDataURL();
may assist you with storing the canvas data in memory. It can then be re-drawn from this url later. e.g.
img.src = url;
ctx.drawImage(img,0,0);
I assume what you want is to store a rotated image.
To do it, most easy way is to create a canvas, then draw rotated into this canvas : you now have your rotated image inside a canvas, that you might want to transform into an image.
However, the security policy prevents you from creating an image out of a canvas if you used an image coming from another domain. So be sure to use canvas only in such a case.
http://jsbin.com/gemijekewewu/1/edit?js,output
(thx #markE for careful reading).
function getRotatedImage(sourceImage, provideImage) {
var cv = document.createElement('canvas');
cv.width = sourceImage.height;
cv.height = sourceImage.width;
var ctx = cv.getContext('2d');
ctx.translate(cv.width/2, cv.height/2);
ctx.rotate(Math.PI / 2);
ctx.drawImage(sourceImage, -cv.width/2, -cv.height/2);
if (provideImage) {
var img=new Image();
img.src= cv.toDataURL();
return img;
} else
return cv;
}
use with :
// during the init ...
var rotatedImage=getRotatedImage(myImage);
// ... later ...
ctx.drawImage(rotatedImage, ... , ...);
Related
I'm using HTML5 canvas in a project and occasionally need to draw drop shadows on SVGs within a canvas. I've noticed that, compared to Chrome, Safari does two things incorrectly when doing this:
Safari draws a shadow on each individual shape within an SVG
Safari crops off parts of the shadow that go beyond the SVG's bounds
These issues can be illustrated by the following code:
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.shadowOffsetX = 10;
context.shadowOffsetY = 10;
context.shadowColor = 'red'
var image = new Image();
image.src = 'https://storage.googleapis.com/card-conjurer/img/manaSymbols/0.svg';
image.onload = function() {
context.drawImage(image, 10, 10, 100, 100);
}
<canvas id='canvas'></canvas>
I can't embed images yet, but here are some links to images that illustrate the problem:
SVG Shadows with Google Chrome
SVG Shadows with Safari
(they are screenshots of the code above)
The results from Safari are... quite ugly, as you can see. Is there a way make Safari to render SVGs with shadows on HTML5 canvas like Chrome does?
Any help would be greatly appreciated.
Thanks so much for your time!
That's a bug, you should report it to webkit's bug-tracker.
Though you can workaround it by first drawing the image on a second canvas just to rasterize that svg image and use that canvas as source for the shadowing:
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var image = new Image();
image.src = 'https://storage.googleapis.com/card-conjurer/img/manaSymbols/0.svg';
image.onload = function() {
const off = canvas.cloneNode();
off.getContext('2d').drawImage(image, 10, 10, 100, 100);
context.shadowOffsetX = 10;
context.shadowOffsetY = 10;
context.shadowColor = 'red';
context.drawImage(off, 0, 0);
}
<canvas id='canvas'></canvas>
In order to use a single canvas, we need to use an offset trick, but it's not always easy to do since it requires knowing clearly the position of our drawing:
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var image = new Image();
image.src = 'https://storage.googleapis.com/card-conjurer/img/manaSymbols/0.svg';
image.onload = function() {
// first pass without shadow
context.drawImage(image, 10, 10, 100, 100);
// set shadow offsets to the position in page of bottom-right corner
context.shadowOffsetX = 10 + 110;
context.shadowOffsetY = 10 + 110;
context.shadowColor = 'red';
// draw behind
context.globalCompositeOperation = "destination-over";
// draw with inverse offset, so that the image is not visible
// but the shadow is in-screen
context.drawImage(canvas, -110, -110);
}
<canvas id='canvas'></canvas>
I am unable to save this rounded image as png.
Here I am getting blank canvas as base code in console.
Anyone Please tell Me how can I save canvas content like rouneded image as an png or base 64 code.
// Grab the Canvas and Drawing Context
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
// Create an image element
var img = document.createElement('IMG');
// When the image is loaded, draw it
img.onload = function () {
// Save the state, so we can undo the clipping
ctx.save();
// Create a circle
ctx.beginPath();
ctx.arc(106, 77, 74, 0, Math.PI * 2, false);
// Clip to the current path
ctx.clip();
ctx.drawImage(img, 0, 0);
// Undo the clipping
ctx.restore();
}
// Specify the src to load the image
img.src = "http://i.imgur.com/gwlPu.jpg";
var base = canvas.toDataURL();
console.log(base);
You need to wait for the image to load before trying to use it.
On top of that you can't call toDataURL on tainted canvases. Tainted canvases are canvases that have had images drawn in them from other domains unless you both request permission to use the image AND the server gives you permission to use the image.
For your example imgur's servers usually give permission. To request permission you need to set img.crossOrigin. See: https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
// Grab the Canvas and Drawing Context
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
// Create an image element
var img = document.createElement('IMG');
// When the image is loaded, draw it
img.onload = function () {
// Save the state, so we can undo the clipping
ctx.save();
// Create a circle
ctx.beginPath();
ctx.arc(106, 77, 74, 0, Math.PI * 2, false);
// Clip to the current path
ctx.clip();
ctx.drawImage(img, 0, 0);
// Undo the clipping
ctx.restore();
var base = canvas.toDataURL();
console.log(base);
}
// Specify we want to ask the server for permission to use this image
img.crossOrigin = "anonymous";
// Specify the src to load the image
img.src = "http://i.imgur.com/gwlPu.jpg";
<canvas id="c"></canvas>
I have a PNG graphic I want to use as a background for my canvas in JS. It should be displayed in a tiled format. For this I'm currently doing something like this.
const ratioX = Math.ceil(canvas.width / image.width);
const ratioY = Math.ceil(canvas.height / image.height);
for (var x = 0; x < ratioX; x++) {
for (var y = 0; y < ratioY; y++) {
ctx.drawImage(image, x*image.width, y*image.height, image.width, image.height);
}
}
It's working as intended, but the performance might get improved by not doing the calculations every frame, but buffering the bigger tiled version of my image. So I'm looking for a way to create this image as an instance of the Image class or any other class I can use for drawImage().
At first I might need to get the image data (so the rgba info for every pixel). I've seen a way to do it like here, using canvas's context, but do I really need to do that? Is there no simpler way?
The second step would be to create a drawable image object out of that data. How would I do that?
I would love to see a way that's equivalent to Java's bufferedImage.getRgb(x, y) and bufferedImage.setRgb(x, y, color). Does something like this exist in JS?
As shown here in W3Schools you can use ctx createPattern to achieve what you want:
var ctx = document.getElementById("canvas").getContext("2d");
var img = new Image();
img.src = 'http://placehold.it/50/50'
img.onload = function() {
var pat = ctx.createPattern(img, 'repeat');
ctx.rect(0, 0, 350, 350);
ctx.fillStyle = pat;
ctx.fill();
};
<canvas id='canvas' width=350 height=350/>
I'm looking for a solution to change the texture/pattern for a product.
At this moment i have:
A .png picture of a couch with a transparent background
A .png picture of a texture
With the following code:
<canvas id="a" width="800" height="500">Canvas not supported on your browser</canvas>
var width = $(window).width();
var height = $(window).height();
var c = document.getElementById("a");
var ctx = c.getContext("2d");
var can2 = document.createElement('canvas');
document.body.appendChild(can2)
can2.width = c.width;
can2.height = c.height;
var ctx2 = can2.getContext("2d");
var test = new Image();
test.src = "Images/newBank.png";
test.onload = function () {
ctx2.drawImage(test, 0, 0);
};
var img = new Image();
img.src = "Images/texturetrans.png";
img.onload = function () {
ctx2.globalCompositeOperation = 'source-in';
var ptrn = ctx2.createPattern(img, 'repeat');
ctx2.fillStyle = ptrn;
ctx2.fillRect(0, 0, can2.width, can2.height);
}
`
I get this result:
As you can see, the whole object is filled with my texture. No definitions of the pillows etc. are visible anymore. Is it possible to let my texture be a sort of transparent mask?
I'm already able to change the color of the couch:
But I'd like to be able to also add a pattern to my couch!
Any help will be appreciated and I'm already very sorry for my bad English.
If you're just after an illustrative approximation you can use a combination of blending and composition modes.
First thing is to make sure your main image has transparency - this is important for composition to work (I made a rough cut-off in the following demo).
Main steps:
Draw the pattern
Draw the main image on top with blending mode multiply
Draw the main image on top with compositing mode destination-in - this will make a cut-out
If you want to reduce the size of the pattern you can either do this by using a smaller version of the image, draw to a temporary canvas at a smaller size and use that as pattern, or use the new transform methods on the pattern itself.
Demo
var img1 = new Image, img2 = new Image, cnt = 2,
canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
// image loading for demo (ignore)
img1.onload = img2.onload = function() {if (!--cnt) go()};
img1.src = "//i.imgur.com/8WqH9v4.png"; // sofa
img2.src = "//i.stack.imgur.com/sQlu8.png"; // pattern
// MAIN CODE ---
function go() {
// create a pattern
ctx.fillStyle = ctx.createPattern(img2, "repeat");
// fill canvas with pattern
ctx.fillRect(0, 0, canvas.width, canvas.height);
// use blending mode multiply
ctx.globalCompositeOperation = "multiply";
// draw sofa on top
ctx.drawImage(img1, 0, 0, img1.width*.5, img1.height*.5);
// change composition mode
ctx.globalCompositeOperation = "destination-in";
// draw to cut-out sofa
ctx.drawImage(img1, 0, 0, img1.width*.5, img1.height*.5);
}
<canvas id="canvas" width=600 height=400></canvas>
You can also reverse the order of which image is drawn etc., if you prefer. This is just an example of one way.
If you need accurate texture then there is no way around to either take photos or use a 3D software, or hand-drawn the textures.
NOTE: IE does not support multiply - For this you need to manually iterate through the pixels and multiply each component with each other.
You can test for support this way:
ctx.globalCompositeOperation = "multiply";
if (ctx.globalCompositeOperation === "multiply") {
// blend as above
}
else {
// iterate and blend manually
}
Blending mode luminosity is mentioned in comments and this can be used too of course. I just want to point a couple of things to consider. The first is that this is a non-separable blending mode meaning it depends on all components as it goes through the HSL color model. This makes it a bit more compute intensive.
The second is that if you end up having to do this manually (in for example IE) the code is a bit more complex to emulate, and will be noticeably slower.
i create an apps in HTML5 canvas, for cropping image. there are 3 canvases in the DOM, one for drawing the line, one for original image, and another one for the result.
and here is the code i use.
// copy image data to secondary canvas
var pixelData = ctx.getImageData(x-10, y-10, 20, 20);
var tmpCanvas = document.createElement("canvas");
tmpCanvas.width = 20; tmpCanvas.height = 20;
var tmpCtx = tmpCanvas.getContext("2d");
tmpCtx.putImageData(pixelData, x, y);
var tmpImageEl = document.createElement("img");
tmpImageEl.onload = function(){
ctxCopy.drawImage(this, x, y);
}
console.log(tmpCanvas.toDataURL());
tmpImageEl.src = tmpCanvas.toDataURL();
//ctxCopy.putImageData(pixelData, x, y);
//document.getElementsByTagName("body")[0].appendChild(tmpCanvas);
in this line
tmpImageEl.src = tmpCanvas.toDataURL();
i got nothing but blank/transparent image. what i expect is the imagedata from original source..what is wrong with the code? if there is rounded options for putImageData this could be easier for me (i aleady googling about rounded putImageData, no luck)
Your ctxCopy does not seem to be defined; you probably intend to do this:
var tmpCtx = tmpCanvas.getContext("2d");
...
tmpImageEl.onload = function(){
tmpCtx.drawImage(this, x, y); /// for your temporary canvas
//ctxCopy.drawImage(this, x, y);
}
There is also an issue in Chrome with data-uris as source; you can get around that by setting first an empty string, then the data-uri:
tmpImageEl.src = '';
tmpImageEl.src = tmpCanvas.toDataURL();
However, you can save yourselves a bit of trouble by simply using the drawImage() method directly on your temporary canvas to draw in the source canvas (it can take image, canvas and video as source input):
var tmpCanvas = document.createElement("canvas");
tmpCanvas.width = tmpCanvas.height = 20;
var tmpCtx = tmpCanvas.getContext("2d");
tmpCtx.drawImage(ctx.canvas, 0, 0);
No need for getPixelData()/putImageData()/image, and it's much faster this way.
Hope this helps.