I'm currently drawing arcs with the canvas tag and I've been asked if I can feather (soften) the edges of the arcs. Is this possible? Googling and searching on here it seems like more hassle than it's worth.
I've tried looking on mdn too but resources for feathering an element seem sparse.
From the image you link to in your comment, it seems you're trying to do a shadow.
If this is the case, you can do this :
With this code :
c.beginPath();
c.arc(33, 33, 22, 0, Math.PI, false);
c.shadowOffsetX = 2;
c.shadowOffsetY = 2;
c.shadowBlur = 5;
c.shadowColor = 'rgba(0, 0, 0, 0.8)';
c.fillStyle = "red";
c.fill();
Demonstration
Or maybe you're trying to do this :
c.beginPath();
c.arc(33, 33, 22, 10, Math.PI*2, false);
c.lineWidth = 2;
var gradient = c.createLinearGradient(20, 0, 50, 40);
gradient.addColorStop(0, "white");
gradient.addColorStop(0.5, 'red');
gradient.addColorStop(1, "white");
c.strokeStyle = gradient;
c.stroke();
Demonstration
There is unfortunately no feather option (yet), but someone who answered this question
came up a this fiddle which comes probably pretty close to what you want to achieve using RGBA gradients:
gradient.addColorStop(0, "rgba("+r+","+g+","+b+",0)");
See his jsfiddle here: http://jsfiddle.net/chdh/MmYAt/
But it still is a bit of a hassle. I would like to submit a feature request for this but I simply have no idea where?
Related
I'm trying to create a blurry donut shape like this on a canvas with js.
I tried
gradient.addColorStop(0, "rgba(0, 0, 0, .1)");
gradient.addColorStop(0.5, "rgba(128, 128, 128, .1)");
gradient.addColorStop(1, "rgba(0, 0, 0, .1)");
But I only get
It has a distinct defined circle at the stop radius. What I want is a smooth falloff. Something like this..
Is this possible?
It looks like you're after a Gaussian blur rather than a gradient.
You can create such a blur using the ctx.filter property and pass in a CSS filter value "blur(Npx)".
However Safari still doesn't support this property, so for this browser, we need to use a shadow as a workaround.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
if (ctx.filter === "none") {
ctx.filter = "blur(60px)";
}
else { // Safari still doesn't support ctx.filter...
ctx.shadowColor = "#34aaff";
ctx.shadowBlur = 120; // x2
ctx.shadowOffsetX = 800;
ctx.translate(-800, 0); // we draw the actual shape outside of the visible context
}
ctx.arc(400, 400, 200, 0, Math.PI*2);
ctx.lineWidth = 125;
ctx.strokeStyle = "#34aaff";
ctx.stroke();
<canvas width=800 height=800></canvas>
You can try to add one or more color stops to control the shape:
gradient.addColorStop(0, "White");
gradient.addColorStop(0.3, "rgba(128, 128, 256, .5)");
gradient.addColorStop(0.4, "rgba(128, 128, 256, .5)");
gradient.addColorStop(1, "White");
I'm working with some canvas and processing.js but i cant figure out how to fill an arc/ellipse etc with an image.
Using JavaScript usually i do something like this:
ctx.save();
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2, true);
ctx.closePath();
ctx.clip();
ctx.drawImage(thumbImg, 0, 0, 400, 400);
ctx.beginPath();
ctx.arc(x, y, size, Math.PI * 2, true);
ctx.clip();
ctx.closePath();
ctx.restore();
and the game is done, but how can i do it with processing.js?
I've tried those options but seems that I'm doing something wrong:
b = loadImage("nicola.png");
fill(b)
background(b);
ellipse(x, y, size, size);
any idea?
I believe that what you are trying to get at is called image masking
an example of masking
Description:
Masks part of an image from displaying by loading another image and using it as an alpha channel. This mask image should only contain grayscale data, but only the blue color channel is used. The mask image needs to be the same size as the image to which it is applied.
In addition to using a mask image, an integer array containing the alpha channel data can be specified directly. This method is useful for creating dynamically generated alpha masks. This array must be of the same length as the target image's pixels array and should contain only grayscale data of values between 0-255.
Example:
var g2;
var setup = function(){
createCanvas(200,200);
background(0, 0, 0, 0);
smooth();
fill(255, 255, 255);
ellipse(100, 100, 200, 200);
var g1 = get(0, 0, 200, 200);
background(0, 0, 0, 0);
noStroke();
for(let i = 0; i < 360; i++){
fill(sin(radians(i))*255, i, 200);
rect(0, i, 200, 1);
}
g2 = get(0, 0, 200, 200);
g2.mask(g1);
}
var draw = function(){
background(255, 255, 255);
image(g2, 0, 0);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>
an image of what the above code returns:
You can either use img.mask(maskImg) to apply an (pixel based) alpha mask or use img.blend(…) as described here for example.
A semicolon ';' is missing after fill(b)
So it should be fill(b);
I hope this solves your problem.
I am in the process of updating to kineticjs 4.7.0. I am struggling with adding text to a custom shape.
Here the code:
var triangle = new Kinetic.Shape({
drawFunc: function(context) {
this.setFill('#00D2FF');
context.beginPath();
context.moveTo(200, 50);
context.lineTo(420, 80);
context.quadraticCurveTo(300, 100, 260, 170);
context.closePath();
context.fillStrokeShape(this);
this.setFill('#FFFFFF');
context.beginPath();
context.fillText('Hello World!', 200, 150);
context.closePath();
context.fillStrokeShape(this);
},
stroke: 'black',
strokeWidth: 4
});
How do I make the text a different color to the filling of the shape, so I don't need to use Kinetic.Shape and Kinetic.Text in a group?
Here the is jsfiddle http://jsfiddle.net/qQU6G/1/
Yes, it appears that KineticJS 4.7 has a more complete wrapper of canvas.context, but the fillText method does not yet respect either context.fillStyle or this.setFill.
[ Update ]
Since Kinetic's "context" is not full-featured in regards to fillText, here's a way to get the underlying context and use that to fillText with your different color.
Here's a Fiddle example: http://jsfiddle.net/m1erickson/df6Uv/
var triangle = new Kinetic.Shape({
drawFunc: function(context) {
this.setFill('#00D2FF');
context.beginPath();
context.moveTo(200, 50);
context.lineTo(420, 80);
context.quadraticCurveTo(300, 100, 260, 170);
context.closePath();
context.fillStrokeShape(this);
var ctx=this.getContext()._context;
ctx.save();
ctx.font="18px verdana";
ctx.fillStyle="#ffffff";
ctx.fillText("Hello World!",225,90);
ctx.restore();
},
stroke: 'black',
strokeWidth: 4
});
Be warned that drawFunc can be invoked more than once and not always you will get context from canvas you expect. Kinetic will use helper canvases sometimes. I run into problems using above approach, but wrapping "extra" code with simple check helped:
...
if(context.canvas._canvas.parentNode!=null){
var ctx=this.getContext()._context;
ctx.save();
ctx.font="18px verdana";
ctx.fillStyle="#ffffff";
ctx.fillText("Hello World!",225,90);
ctx.restore();
}
...
As a part of a course in school, we're learning to work with the Canvas element of HTML5, this also means we're learning how to work with Javascript. The assignment is to create something graphic and some sort of interaction with this something graphic.
I decided to create a few simple diagrams and have the user be able to input values and see as the diagrams change.
http://people.dsv.su.se/~tojo0551/graf/lines.html contains a few diagrams I've drawn up, now comes the tricky part- working with Javascript and creating interaction. This is probably easy, but I never touched Javascript aside from JQuery before thus a bit at loss.
Say I want the user to be able to interact with the bar diagram at the bottom and to fill in a value between 1-5 and have the bar grow accordingly.
The Canvas code is simple, it looks like this:
function bars(){
var canvas = document.getElementById("bars");
if (canvas.getContext)
{
var ctx = canvas.getContext("2d");
var bar1 = canvas.getContext("2d");
bar1.fillStyle = "rgba(0, 50, 0, .2)";
bar1.fillRect(20, 400, 30, 90);
var bar2 = canvas.getContext("2d");
bar2.fillStyle = "rgba(0, 50, 0, .4)";
bar2.fillRect(55, 360, 30, 130);
ctx.fillStyle = "rgba(0, 50, 0, .2)";
ctx.fillRect(90, 260, 30, 230);
ctx.fillStyle = "rgba(0, 50, 0, .4)";
ctx.fillRect(125, 290, 30, 200);
ctx.fillStyle = "rgba(0, 50, 0, .2)";
ctx.fillRect(160, 270, 30, 220);
ctx.fillStyle = "rgba(0, 50, 0, .4)";
ctx.fillRect(195, 250, 30, 240);
ctx.fillStyle = "rgba(0, 50, 0, .2)";
ctx.fillRect(230, 300, 30, 190);
ctx.fillStyle = "rgba(0, 50, 0, .2)";
ctx.fillRect(20, 400, 30, 90);
ctx.fillStyle = "rgba(0, 50, 0, .4)";
ctx.fillRect(55, 360, 30, 130);
ctx.fillStyle = "rgba(0, 50, 0, .2)";
ctx.fillRect(90, 260, 30, 230);
ctx.fillStyle = "rgba(0, 50, 0, .4)";
ctx.fillRect(125, 290, 30, 200);
ctx.fillStyle = "rgba(0, 50, 0, .2)";
ctx.fillRect(160, 270, 30, 220);
ctx.fillStyle = "rgba(0, 50, 0, .4)";
ctx.fillRect(195, 250, 30, 240);
ctx.fillStyle = "rgba(0, 50, 0, .2)";
ctx.fillRect(230, 300, 30, 190);
But where do I go from here? I am new to programming, I know how to create functions and control flow with selection and loops, you know, I am a beginner programmer. What is the natural path to go to create user control? I want some input before I start working so I don't work in the wrong direction. Any pages that have good advice on how to or any ideas you got are very welcome.
/Tomas
This is pretty straight forward. If you have some experience of jQuery, I would suggest including it here, it would make things simpler.
Assuming you do use jQuery, you just need to call your bars() function whenever the user changes a value in an html input. First just change your bars() declaration so that it can be called with a value. Like this:
function bars(userVal) {
// All your existing code ...
}
Then you will need to clear whatever was drawn last time bars() was called. Something like:
function bars(userVal) {
var canvas = document.getElementById("bars");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
// Clear the bars that were drawn last time
ctx.clearRect(x,y,w,h);
// The rest of your existing code ...
}
}
clearRect takes an origin location (x and y) and a height and a width (w and h), and will clear the canvas in that area. So pass in the necessary limits of the drawing area.
Next add a text input to your page, and use jQuery to call bars() whenever it's changed
$(document).ready(function() {
$("input").on("change", function() {
var value = $(this).val();
bars(value);
});
});
Finally, you must decide what you want to do with the value the user has entered. This is entirely up to you, but I guess a quick and easy test to check it will work is just to use a numerical value in one of the drawing calls.
Examples:
// This would change the start position of a bar
ctx.fillRect(userVal, 260, 30, 230);
// This would change the width of a bar
ctx.fillRect(90, 260, userVal, 230);
// This would change the colour of a bar
ctx.fillStyle = "rgba(userVal, 50, 0, .4)";
Have fun.
Take a look at fabricjs. It begins to get very complicated to do relatively simple things using canvas, and this seems to make life easier in general though it supports interaction as well. It's important to know the fundamentals, but don't try to build a house without your carpentry tools.
If plotting is what interests you more, you should take a look at flot.
Then for the adventurous amongst us, there's three which plots 3d. Though you should work up to that.
Why does the following code not produce three lines, all with similar gradients?
<html>
<body style="background: black;">
<canvas id="Test" width="516" height="404"> </canvas>
<script>
var ctx = document.getElementById('Test').getContext('2d');
ctx.lineWidth = 8;
function addColorStops(gradient) {
gradient.addColorStop(0.5, 'rgba(151, 165, 193, 0.5)');
gradient.addColorStop(1, 'rgba(151, 165, 193, 1)');
}
function drawLine(x1, x2, y) {
var g = ctx.createLinearGradient(x1, y, x2, y);
addColorStops(g);
ctx.strokeStyle = g;
ctx.moveTo(x1, y);
ctx.lineTo(x2, y);
ctx.stroke();
}
drawLine(10, 100, 10);
drawLine(10, 100, 30);
drawLine(10, 100, 50);
</script>
</body>
</html>
Instead the first line gets a very very slight gradient, the second line gets a pretty good gradient, and the last gets a drastic gradient.
I think this stems from misunderstanding of either how the parameters to createLinearGradient are supposed to work, or misunderstanding how strokeStyle assignments influence future strokes...
Agh, I figured it out. I need to add a ctx.beginPath() right before the ctx.strokeStyle = g;. It turns out that lines are part of a path and thus if you don't begin a new path it'll think you're still continuing the old one, and thus use your original start point and final end point as the start and end for gradient purposes.
I was getting the strokeStyle overridden! By adding a beginPath my stroke colors work..
ctx.beginPath();
ctx.moveTo( x, y );
ctx.lineTo( x, y );
ctx.strokeStyle = "#182945";
ctx.stroke();
Thanks