I have this image drawn to a HTML5 canvas:
What I want to do is apply color to just a part of it.
The part where I want to apply color is defined by the following overlay image:
So, basically, I would like to guide my coloring by the overlay. So where the overlay pixels meets the main image pixels I should apply a color on the main image. At least that's how I see it working.
Notice that the overlay matches the whole image except for the lacing.
The catch is that I would like to retain the main image texture while applying the color. You can see that it has a leather texture and a "real" feel which I want to keep.
Can you please show me some methods of achieving this or share some thoughts?
Thank you!
globalCompositeOperation is your friend here.
Basically, you draw your overlay, then you set the gCO to 'source-atop' composite mode, which will make all your future drawings to only stay where there were already opaque pixels drawn, so it is important that your overlay has transparent parts.
So then you just fill a rectangle of your desired command, and finally you draw your original image, either behind, or blended to the new shape we just created.
var ctx = canvas.getContext('2d');
var loaded = 0;
function onload(){
if(++loaded === 2){
canvas.width = this.width;
canvas.height = this.height;
ctx.font = "40px sans-serif";
draw();
}
}
var original = new Image();
var overlay = new Image();
original.onload = overlay.onload = onload;
original.src = 'https://i.stack.imgur.com/vIKpI.png';
overlay.src = 'https://i.stack.imgur.com/10Tre.png';
// list of blending modes.
// Note that destination-over is a composite mode,
// which place the new drawings behind the already-there ones
var currentMode = 0;
var modes = ['destination-over', 'lighter', 'multiply', 'screen', 'overlay', 'darken',
'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light',
'exclusion', 'hue', 'saturation', 'color', 'luminosity' ];
function draw(){
// switch between different Blending modes
var mode = modes[currentMode];
currentMode = (currentMode+1)%(modes.length);
// clear previous
ctx.clearRect(0,0,canvas.width, canvas.height);
// draw our overlay
ctx.drawImage(overlay, 0,0);
// this will keep new drawings only where we already have existing pixels
ctx.globalCompositeOperation = 'source-atop';
ctx.fillStyle = 'red';
ctx.fillRect(0,0,canvas.width, canvas.height);
// now choose between the list of blending modes
ctx.globalCompositeOperation = mode;
// draw our original image
ctx.drawImage(original, 0,0);
// go back to default
ctx.globalCompositeOperation = 'source-over';
// just so we can know which one is shown
ctx.fillStyle = 'black';
ctx.fillText(mode, 40,40)
// do it again
setTimeout(draw, 1000)
}
canvas{
width: 100%;
}
<canvas id="canvas"></canvas>
I have the following code in this jsfiddle which has 3 images and each one is clipped inside it's own square using html5 canvas and fabricjs:
http://jsfiddle.net/tbqrn/68/
But how can i stop the images going into another clipping region/area? I want the clipping to be specific to that image.
The following question gives an answer with an accompanying jsfiddle but when scaling/rotating the image it completely alters the clipping area. Plus the clipping area doesn't match up with the black 'zones' I've tried adapting the code as it's close to what i need but without success.
Multiple clipping areas on Fabric.js canvas
http://jsfiddle.net/32Acb/
How can i achieve this? My code is:
var canvas = new fabric.Canvas('c');
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.rect(10,10,150,150);
ctx.rect(170,10,200,200);
ctx.rect(10,170,150,150);
ctx.closePath();
ctx.stroke();
ctx.clip();
fabric.Image.fromURL(img01URL, function(oImg) {
oImg.scale(.25);
oImg.left = 10;
oImg.top = 10;
canvas.add(oImg);
canvas.renderAll();
});
fabric.Image.fromURL(img02URL, function(oImg) {
oImg.scale(.40);
oImg.left = 180;
oImg.top = 0;
canvas.add(oImg);
canvas.renderAll();
});
fabric.Image.fromURL(img03URL, function(oImg) {
oImg.left = 10;
oImg.top = 170;
canvas.add(oImg);
canvas.renderAll();
I am looking to simply add a dot to the canvas.
I have the following code:
var canv = document.getElementById("myCanvas");
var context = canv.getContext("2d");
var radius = 5;
var putPoint = function(e){
context.beginPath();
context.arc(e.clientX, e.clientY, radius, 0, Math.PI*2);
context.fill();
}
canv.addEventListener('mousedown', putPoint);
I was learning how to do this with a video tutorial. However they were setting the canvas
as the full width/height of the browser window, whereas my canvas is on 400px * 400px and is contained within a div. I think this is the problem.
So my question is are the "e.client" parameters not working because of my canvas being only
a small part of the window?
If so, how can I track the mouse co-ordinates on my canvas?
You must adjust e.clientX/e.clientY by the offset of the canvas element.
Otherwise you're miscalculating the position of your mouse and your dot is probably being drawn outside the bounds of your canvas.
Here's your code modified to take the canvas offset into account.
var putPoint = function(e){
var BB=canvas.getBoundingClientRect();
var offsetX=BB.left;
var offsetY=BB.top;
var mouseX=e.clientX-BB.left;
var mouseY=e.clientY-BB.top;
context.beginPath();
context.arc(mouseX,mouseY,radius,0,Math.PI*2);
context.fill();
}
I have this code that for some reason it should draw canvas many times (using setTimeout):
function drawImage() {
var img = new Image();
var canvas = document.getElementById("event");
var context = canvas.getContext("2d");
img.src = "../img/event.png";
img.onload = function () {
context.drawImage(img, 0, 0);
}
setTimeout(drawImage, 3000);
}
but it cause dark edge as draw more times as this:
Is it possible to prevent edges become dark in repetitively drawing?
As long as the image has an alpha-channel you cannot avoid this when re-painting it over and over without some mechanism to reset the alpha values drawn.
The reason for this is that the alpha-value for that pixel will accumulate so when you draw a non-opaque edge (or anti-aliased edge) on top of each other the value for the alpha channel will be added to the value already drawn resulting in that the edge will be more and more visible.
There are fortunately a couple of ways to avoid this:
A) If you want to keep the alpha-channel in the image use clearRect before drawing the image.
context.clearRect(0, 0, img.width, img.height);
context.drawImage(img, 0, 0);
This will clear the canvas and the alpha channel.
ONLINE DEMO
B) If alpha channel is not important save out the image without any alpha-channel (use PNG with transparency off or use JPEG).
Also a note to your loop in the example - this is not a good way to redraw an image as you are initiating a load/cache check as well as image decoding each time.
You can modify your code as this (adopt as you please):
var canvas = document.getElementById("event");
var context = canvas.getContext("2d");
var img = document.createElement('img'); // due to chrome bug
img.onload = drawImage; // set onload first
img.src = "../img/event.png"; // src last..
function drawImage() {
context.clearRect(0, 0, img.width, img.height);
context.drawImage(img, 0, 0);
setTimeout(drawImage, 3000);
}
Using the HTML5 <canvas> element, I would like to load an image file (PNG, JPEG, etc.), draw it to the canvas completely transparently, and then fade it in. I have figured out how to load the image and draw it to the canvas, but I don't know how to change its opacity.
Here's the code I have so far:
var canvas = document.getElementById('myCanvas');
if (canvas.getContext)
{
var c = canvas.getContext('2d');
c.globalAlpha = 0;
var img = new Image();
img.onload = function() {
c.drawImage(img, 0, 0);
}
img.src = 'image.jpg';
}
Will somebody please point me in the right direction like a property to set or a function to call that will change the opacity?
I am also looking for an answer to this question, (to clarify, I want to be able to draw an image with user defined opacity such as how you can draw shapes with opacity) if you draw with primitive shapes you can set fill and stroke color with alpha to define the transparency. As far as I have concluded right now, this does not seem to affect image drawing.
//works with shapes but not with images
ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
I have concluded that setting the globalCompositeOperation works with images.
//works with images
ctx.globalCompositeOperation = "lighter";
I wonder if there is some kind third way of setting color so that we can tint images and make them transparent easily.
EDIT:
After further digging I have concluded that you can set the transparency of an image by setting the globalAlpha parameter BEFORE you draw the image:
//works with images
ctx.globalAlpha = 0.5
If you want to achieve a fading effect over time you need some kind of loop that changes the alpha value, this is fairly easy, one way to achieve it is the setTimeout function, look that up to create a loop from which you alter the alpha over time.
Some simpler example code for using globalAlpha:
ctx.save();
ctx.globalAlpha = 0.4;
ctx.drawImage(img, x, y);
ctx.restore();
If you need img to be loaded:
var img = new Image();
img.onload = function() {
ctx.save();
ctx.globalAlpha = 0.4;
ctx.drawImage(img, x, y);
ctx.restore()
};
img.src = "http://...";
Notes:
Set the 'src' last, to guarantee that your onload handler is called on all platforms, even if the image is already in the cache.
Wrap changes to stuff like globalAlpha between a save and restore (in fact use them lots), to make sure you don't clobber settings from elsewhere, particularly when bits of drawing code are going to be called from events.
Edit: The answer marked as "correct" is not correct.
It's easy to do. Try this code, swapping out "ie.jpg" with whatever picture you have handy:
<!DOCTYPE HTML>
<html>
<head>
<script>
var canvas;
var context;
var ga = 0.0;
var timerId = 0;
function init()
{
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
timerId = setInterval("fadeIn()", 100);
}
function fadeIn()
{
context.clearRect(0,0, canvas.width,canvas.height);
context.globalAlpha = ga;
var ie = new Image();
ie.onload = function()
{
context.drawImage(ie, 0, 0, 100, 100);
};
ie.src = "ie.jpg";
ga = ga + 0.1;
if (ga > 1.0)
{
goingUp = false;
clearInterval(timerId);
}
}
</script>
</head>
<body onload="init()">
<canvas height="200" width="300" id="myCanvas"></canvas>
</body>
</html>
The key is the globalAlpha property.
Tested with IE 9, FF 5, Safari 5, and Chrome 12 on Win7.
This suggestion is based on pixel manipulation in canvas 2d context.
From MDN:
You can directly manipulate pixel data in canvases at the byte level
To manipulate pixels we'll use two functions here - getImageData and putImageData.
getImageData usage:
var myImageData = context.getImageData(left, top, width, height);
The putImageData syntax:
context.putImageData(myImageData, x, y);
Where context is your canvas 2d context, and x and y are the position on the canvas.
So to get red green blue and alpha values, we'll do the following:
var r = imageData.data[((x*(imageData.width*4)) + (y*4))];
var g = imageData.data[((x*(imageData.width*4)) + (y*4)) + 1];
var b = imageData.data[((x*(imageData.width*4)) + (y*4)) + 2];
var a = imageData.data[((x*(imageData.width*4)) + (y*4)) + 3];
Where x is the horizontal offset, y is the vertical offset.
The code making image half-transparent:
var canvas = document.getElementById('myCanvas');
var c = canvas.getContext('2d');
var img = new Image();
img.onload = function() {
c.drawImage(img, 0, 0);
var ImageData = c.getImageData(0,0,img.width,img.height);
for(var i=0;i<img.height;i++)
for(var j=0;j<img.width;j++)
ImageData.data[((i*(img.width*4)) + (j*4) + 3)] = 127;//opacity = 0.5 [0-255]
c.putImageData(ImageData,0,0);//put image data back
}
img.src = 'image.jpg';
You can make you own "shaders" - see full MDN article here
You can. Transparent canvas can be quickly faded by using destination-out global composite operation. It's not 100% perfect, sometimes it leaves some traces but it could be tweaked, depending what's needed (i.e. use 'source-over' and fill it with white color with alpha at 0.13, then fade to prepare the canvas).
// Fill canvas using 'destination-out' and alpha at 0.05
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = "rgba(255, 255, 255, 0.05)";
ctx.beginPath();
ctx.fillRect(0, 0, width, height);
ctx.fill();
// Set the default mode.
ctx.globalCompositeOperation = 'source-over';
I think this answers the question best, it actually changes the alpha value of something that has been drawn already. Maybe this wasn't part of the api when this question was asked.
Given 2d context c.
function reduceAlpha(x, y, w, h, dA) {
let screenData = c.getImageData(x, y, w, h);
for(let i = 3; i < screenData.data.length; i+=4){
screenData.data[i] -= dA; //delta-Alpha
}
c.putImageData(screenData, x, y );
}
Set global Alpha draw the object that has opacity then set back to normal.
//////////////////////// circle ///////////////////////
ctx.globalAlpha = 0.75;
ctx.beginPath();
ctx.arc(x1, y1, r1, 0, Math.PI*2);
ctx.fillStyle = colour;
ctx.fill();
ctx.closePath();
ctx.globalAlpha = 1;
How i made it..on canvas i first draw rect in a selfrun function 0,0,canvas.width,canvas.height as a background of canvas and i set globalAlpha to 1 .then i draw other shapes in ather own functions and set their globalAlpha to 0.whatever number they dont affect each other even images.
Like Ian said, use c.globalAlpha = 0.5 to set the opacity, type up the rest of the settings for the square, then follow up with c.save();. This will save the settings for the square then you can c.rect and c.fillStyle the square how you want it. I chose not to wrap it with c.restore afterwards and it worked well
If you use jCanvas library you can use opacity property when drawing. If you need fade effect on top of that, simply redraw with different values.
You can't. It's immediate mode graphics. But you can sort of simulate it by drawing a rectangle over it in the background color with an opacity.
If the image is over something other than a constant color, then it gets quite a bit trickier. You should be able to use the pixel manipulation methods in this case. Just save the area before drawing the image, and then blend that back on top with an opacity afterwards.