I am creating a two layer canvas. The bottom layer is a screenshot. The top layer is the drawing surface. I figured out a basic pen. I want to create a highlighter effect - meaning a larger line with a set opacity .5 or less. The problem I am having is that when I draw over the same area more than once the colors paint on top of each other. Eventually, the highlight will turn solid and block out the lower layer. I want to highlight evenly with the original color and be able to draw over existing highlights without the colors adding up.
I have been messing around with globalCompositeOperation but cannot get the desired results with the settings I have tried. Below i am trying color and using a globalAlpha setting. I have also tried using rgba colors. I also tried just using one layer, but I wanted to be able to clear doodles and erase easily so I went with two. Some of my source code is in the picture, but I'm not sure how helpful code is for my question. I can provide more if needed. Thanks.
EDIT: After using multiply as the globalCompositionOperation the color #FF0 aka yellow works great, among other colors. Some colors still have the original build-to-black effect, like the blue I use in the bottom picture. As a side note, these pictures are not to share my code, they are to show the highlighter effect. As a side note, this an Electron app so it is using Chromium ~ 61 at this time.
The normal way to create a highlighter effect is to use the blending mode "multiply".
This will be like on real paper (subtractive light, not technically but in appearance) so drawing on a dark background will produce an almost invisible highlighter effect.
Note that not all browsers support blending modes (this includes <= IE11).
const ctx = c.getContext("2d");
ctx.globalCompositeOperation = "multiply";
ctx.font = "40px sans-serif";
ctx.fillText("HIGHLIGHT ME", 5, 84); // replace with bg image
ctx.fillStyle = "#ff0";
c.onmousemove = e => ctx.fillRect(e.clientX-10, e.clientY-10, 20,20);
html, body {margin:0}
#c {border:1px solid}
<canvas id=c></canvas>
On dark background:
const ctx = c.getContext("2d");
ctx.fillStyle = "#333";
ctx.fillRect(0,0,c.width,c.height);
ctx.font = "40px sans-serif";
ctx.fillStyle = "#fff";
ctx.fillText("HIGHLIGHT ME", 5, 84); // replace with bg image
ctx.fillStyle = "#ff0";
ctx.globalCompositeOperation = "multiply";
c.onmousemove = e => ctx.fillRect(e.clientX-10, e.clientY-10, 20,20);
html, body {margin:0}
#c {border:1px solid}
<canvas id=c></canvas>
Related
I want to use an image like this on canvas:
The user will "paint and fill" the image, but not on top of the outline.
The problem is:
If I put behind the canvas, the paint will cover the outline.
If I put over the canvas the image block canvas interaction.
Can you help me guys?
Use compositing mode "destination-over" to draw behind existing content (from image, vectors etc.). It's necessary that the existing content provide an alpha channel or composition won't work. If there is no alpha-channel you can convert inverse luma / matte (the white) to alpha channel.
// a quick-n-dirty demo
var ctx = c.getContext("2d");
ctx.lineWidth = 10;
ctx.moveTo(100, 0); ctx.lineTo(150, 150); ctx.stroke();
ctx.fillStyle = "#09f";
// KEY: composite mode -
ctx.globalCompositeOperation = "destination-over";
// draw behind the line
c.onmousemove = function(e) {
ctx.fillRect(e.clientX - 10, e.clientY - 10, 20, 20);
};
body {margin:0}
canvas {border:#777 solid 1px}
<canvas id="c"></canvas>
Here is the example of drawImage function. You can draw any preloaded image onto canvas. You can also try to place the <img> overlay in front of the canvas and disable mouse events for it using pointer-events: none CSS property.
Earlier on I noticed that strokeRect (and any other method that involved stroke such as lineTo) created a grey 2 px wide line instead of a 1px wide black line. After some Google searching I found that context.translate(0.5, 0.5) fixed this. but now fillRect (and like before any other method that involves fill) creates a black box with a grey border around it.
Does anyone know a good way to make it so that both fillRect and strokeRect have crisp edges with no grey borders? I also don't know whether or not I should use context.translate(0.5, 0.5) for images, as it seems like SVGs have crisp edges regardless of whether or not I translate.
Here is a jsfiddle demonstrating this: http://jsfiddle.net/Tysonzero/ydm21pkt/1/
Note that the bottom strokeRect is crisp while the top one is blurry, and the top fillRect is crisp while the bottom one is blurry.
Strokes draw half-inside & half-outside the x,y coordinates. That's why you are seeing the blur with integer x,y and why it clears up when the x,y are offset by a half pixel. Here's more on why the blur occurs: http://www.mobtowers.com/html5-canvas-crisp-lines-every-time/
An easy way to make rects crisper is to add methods to your context instance that offset strokeRect & fillRect for best appearance:
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// add pixel aligned versions of strokeRect & fillRect to this context instance
context.sRect=function(x,y,w,h){
x=parseInt(x)+0.50;
y=parseInt(y)+0.50;
this.strokeRect(x,y,w,h);
}
context.fRect=function(x,y,w,h){
x=parseInt(x);
y=parseInt(y);
context.fillRect(x,y,w,h);
}
context.strokeStyle = "#000000";
context.fillStyle = "#000000";
context.strokeRect(100, 50, 100, 100);
context.fillRect(300.5, 50.5, 100, 100);
context.sRect(100,200,100,100);
context.fRect(300.5,200,100,100);
context.fillText('Unadjusted',20,100);
context.fillText('Adjusted',20,250);
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=500 height=500></canvas>
I'm building a website and I need a user to be able to select an image (a transparent gif with black sign on it) and select a color. I have all the gifs in black with transparent background.
How can I change the color of the gif (the black only) to the color the user chose? I was thinking about using a canvas for this but I'm not sure...
You can use a canvas for this. There is no need to iterate the pixel buffer as many suggests and I would recommend avoiding if possible for two reasons:
CORS restriction may apply if image is loaded from a different origin
Performance
There is a much easier way involving composite modes:
Live demo
/// load image here, then:
function render() {
/// this composite mode clears the canvas as well
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(img, 0, 0);
/// this mode fills in whatever, in the image
ctx.globalCompositeOperation = 'source-in';
/// color to change GIF to:
ctx.fillStyle = '#00c';
/// fill color into non-alpha pixels
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
The copy mode works here as the image is the only thing we want to draw to the canvas. If you need to draw in other details as well then use source-over instead and manually clear the canvas using clearRect().
Tip: You can also draw another image on top instead of a fill color.
Original GIF
Changed to blue color
ctx.fillStyle = '#00c';
Changed to red color
ctx.fillStyle = '#c00';
etc.
Assuming that you want to change the color of the black parts of the image (not the transparent parts) you'll have to use canvas to get the black pixels of the image and draw a new image replacing these pixels with the color your user has chosen.
If you want to replace the transparent parts, simply setting a background color using CSS will do the trick.
I'm trying to understand the combination of HTML5/CSS3 and Javascript more and more.
That's why I thought, make a little project so you learn all about that more.
In short, I like the new iOS7 wallpaper and use it on my website (http://www.betadevelops.com). Then I thought, let's make this more lightweight and draw it with pure Javascript.
I started and managed to get quite far (http://www.betadevelops.com/jOS7.html). But now I face a stupid problem I can't seem to get fixed.
I draw circles on the canvas, and dynamically assign colors to it. But each time a new circle (and so a new color gets chosen) it automatically recolors the old circles...
So let's say, 10 circles:
1: blue circle, draw's it and done
2: yellow circle, draw's it and done, but it also colors the first blue one to yellow
I also wanted to add opacity and blurring. The opacity kinda works in the sense it has opacity on only 2-3 circles from the 20 I draw. I think this is not possible because I use Math.Random the calculate a random opacity.
Considered the blurring, I can add blurring to the whole canvas with follow code
canvas.style.webkitFilter = "blur(3px)";
but that's not what I want. I want the blur on the circle itself and to be more precisely, the outline. I read about it and it's not possible, but you can mimmick the looks with using CSS box-shadow.
So I tried
canvas.style.webkitFilter = "box-shadow(10px 10px 5px #888)";
but this also doesn't work it seems...
So, you website guru's. What am I doing wrong and can you help me out?
You can find the code by clicking on the second link. Uploaded it there.
EDIT:
Nevermind the blur, managed to solve it partially with this code
if (blurred) {
ctx.shadowColor = color;
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
#Stig Runar Vangen has the correct answer.
I would just add that if you don't intend the circles to "run", you could use ctx.closePath after drawing each ctx.arc.
ctx.beginPath();
ctx.arc(centerX, centerY, diameter, 0, 2 * Math.PI, false);
ctx.closePath();
color = color.replace('opacity', Math.random().toString());
ctx.fillStyle = color;
ctx.fill();
The reason why you see that all your circles gets the same color, might be because you join all circles into one draw operation. To separate each circle draw operation, start each circle placement with:
ctx.beginPath();
Each arc should then also be drawn with a call to either ctx.stoke() or ctx.fill() after the definition of each single circle.
This is purely guesswork as I haven't seen your code.
I am trying to draw a sharp thin rectangle using canvas control.I want the canvas background to get loaded with an image and in foreground with some text .
Though i am able to set the color and text they are somehow appearing blurred.Is there a way to fix this issue ?
And i want to apply an image as background image for each canvas rectangle i will draw.These rectangels will appear a in a div control.So wherever there is a canvas rectangle i want its background image to be filled with the image i choose.entire div will not be filled with canvas rectangles but only half of it.Is there a way whether we can have one image for all the canvas rectangles i will draw or do ineed to draw it for every rectangle ?
html:
<div id="divBoard" >
<canvas id="canvasBoard" />
</div>
javascript:
canvas = document.getElementById("canvasBoard");
if (canvas.getContext) {
var context = canvas.getContext("2d");
}
context.fillStyle = "green";
context.fillRect(x, y, width,height);
context.font = "5px tahoma";
context.fillStyle = "black";
context.fillText("cell"+i, x, y + width);
this is the image displayed after executing my code
I have experienced the same issue with fonts not rendering as sharply on a canvas. In my project I did a workaround by placing the font in a separate DIV and overlaying it on the canvas using an appropriate z-index. This approach worked very well for me.
Have you tried adding 0.5 to x and y? The html5 canvas uses antialiasing so if you want "crisp" lines, you need to draw "in between" the pixels.
You will find a good explanation on how this works in mozilla developer reference page for lineWidth
PS. also see this question