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>
Related
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>
I want to make something like this in HTML5 canvas: The circle has gleaming light and its shadow spreads out to the far edge of the circle. You can see that in the image although it is blurry it still has a very clear line between the blurry shadow and the circle. And the circle is transparent so you can see it through.
But I want to avoid using shadowBlur. What I have tried is to use radial gradient. But I get the effect below. Although I have tried tweaking with different numbers I am not able to to make a similar circle like the image shows.
draw(document.getElementById('canvas').getContext('2d'));
function draw(ctx) {
var radgrad = ctx.createRadialGradient(90,90,0,90,90,90);
radgrad.addColorStop(0, 'rgba(255,0,0,0.4)');
radgrad.addColorStop(0.25, 'rgba(228,0,0,.23)');
radgrad.addColorStop(1, 'rgba(228,0,0,0)');
// draw shape
ctx.fillStyle = radgrad;
ctx.fillRect(0,0,190,190);
ctx.arc(90, 90, 15, 0, Math.PI * 2);
ctx.fill();
}
<canvas id="canvas" height="300" width="300">
Any suggestion will be appreciated.
I'm just started working with Leap Motion (it is so much fun). The Leap works mainly with vectors. And now I want to create a program where I can visualise where is a vector pointing. The only way I can imagine doing this is by using a small image which appears when this fuction is on and positioning by using the img.style.left , img.style.top instructions. Any other ideas?
If your goal is to represent 2D Vectors,
You can use canvas to draw lines.
A canvas is like a div but you can draw whatever you want in it, I don't know anything about Leap Motion but if you want to draw lines and circles at precise coordinates, it may be a good solution instead of working with the DOM itself.
The JS part looks like this :
var canvas = document.getElementById('my-canvas');
var ctx = canvas.getContext('2d');
//For exemple here is how to draw a rectangle
//fillStyle support all valid css color
ctx.fillStyle = "rgba(50, 255, 24, 0.7)";
//Create the rectangle, with (startX, startY, height, width)
ctx.fillRect(20, 15, 50, 50);
ctx.beginPath(); //Tells canvas we want to draw
ctx.moveTo(250,250); //Moves the cursor to the coordinates (250, 250);
ctx.lineTo(75, 84); //Draws a line from the cursor (250, 250) to (75, 84);
ctx.closePath(); //Tells canvas to 'close' the drawing
ctx.strokeStyle = "red";
ctx.stroke(); //Draws the line stroke
And the HTML is simply :
<canvas id="my-canvas" height="500px" width="500px">
Here is the text displayed when the browser doesnt support canvas.
</canvas>
I made a jsfiddle to show you what simple things we can do with canvas.
http://jsfiddle.net/pq8g0bf0/1/
A nice website to learn canvas : http://www.html5canvastutorials.com/tutorials/html5-canvas-element/
Since it's javascript, you are free to do calculations for your vectors coordinates, addding eventListeners etc ...
I have various functions on my canvas that allow it to be drawn on. However, when lines overlap and get drawn over previously drawn spots, I want the color of the spot that was re-drawn over to change color to a darker shade. What would be the code I would write to evaluate whether or not a spot was drawn over?
You can use "Blending" to darken overlapping areas on your canvas
The advantage of using blending is that you don't have to keep track of previously drawn strokes & fills.
If you don't care about supporting Internet Explorer/Edge, you can use context.globalCompositeOperation="multiply" which will darken by applying a multiply filter on new overlapping drawings. (P.S. to Microsoft: Come on MS...give us more good stuff like Blending!).
// For browser except IE/Edge...
// darken overlapping strokes using "multiply" compositing
ctx.fillStyle='skyblue';
ctx.fillRect(100,100,100,100);
ctx.globalCompositeOperation='multiply';
ctx.fillRect(75,150,100,100);
For IE/Edge, there is a "manual" multiply filter which involves reading each pixel's RGBA information using context.getImageData and doing the following computation against every red, green & blue color value. This manual method is considerably slower than using compositing.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// rgb values for skyblue
var r=135;
var g=206;
var b=235;
multiplyFilter(100,100,100,100,r,g,b);
multiplyFilter(75,150,100,100,r,g,b);
function multiplyFilter(x,y,width,height,newRed,newGreen,newBlue){
var imgData=ctx.getImageData(x,y,width,height);
var data=imgData.data;
for (var i=0;i<data.length;i+=4) {
if(data[i+3]>0){
data[i+0] = (data[i+0]*newRed)/255;
data[i+1] = (data[i+1]*newGreen)/255;
data[i+2] = (data[i+2]*newBlue)/255;
}else{
data[i+0]=newRed;
data[i+1]=newGreen;
data[i+2]=newBlue;
data[i+3]=255;
}
}
ctx.putImageData(imgData,x,y);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Darken overlaps using a "manual" multiply filter</h4>
<canvas id="canvas" width=300 height=300></canvas>
Let say I got the sprite to the left and I want to apply a red filter over the image before drawing it in a canvas so it looks like the sprite to the right. http://puu.sh/6dQ0E.png
Is there a way to do that?
It could be done in two steps, but I don't know if it's possible to draw the geometric figure that has the exact same shape than the image. (Ex: Drawing a rectangle over it won't match.)
Note: I want the filter to only be applied to this image, not the whole canvas.
You can combine 2 filtering operations to draw the red filter only on top of your existing sprite.
globalCompositeOperation="source-atop" will cause new drawings to only draw on top of existing non-transparent pixels.
globalAlpha will make your new drawings semi-transparent.
Taken together, you can draw a semi-transparent filter that only fills where your non-transparent ghost exsits.
Then just draw a red rectangle over your existing sprite (the rectangle will only be visible inside the existing ghost).
ctx.drawImage(ghostSprites,0,0);
ctx.globalAlpha=0.62;
ctx.globalCompositeOperation="source-atop";
ctx.fillStyle="red";
ctx.fillRect(spriteX,spriteY,spriteWidth,spriteHeight);
Demo: http://jsfiddle.net/m1erickson/W4XrG/
From here...
Notice the black outlines of your sprite become washed from the red filter.
You could also use context.getImageData to grab only the black pixels of your ghost. Then redraw those black pixels over your red-filtered ghost so the black outlines are not washed. If you feel ambitious, you could give that a try!
Good luck with your project!
Here’s code
<style>
body{ background-color: ivory; padding:20px;}
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=start;
img.crossOrigin="anonymous";
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/temp0a.png";
function start(){}
var spriteX=0;
var spriteY=0;
var spriteWidth=133;
var spriteHeight=161
$("#recolor").click(function(){
ctx.drawImage(img,0,0);
ctx.globalAlpha=0.62;
ctx.globalCompositeOperation="source-atop";
ctx.fillStyle="red";
ctx.fillRect(spriteX,spriteY,spriteWidth,spriteHeight);
});
}); // end $(function(){});
</script>
</head>
<body>
<p>Before</p>
<img src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/temp0a.png">
<p>After</p>
<button id="recolor">Click to recolor the green sprite</button><br>
<canvas id="canvas" width=300 height=161></canvas>
</body>
</html>