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.
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 am trying to use canvas's globalCompositeOperation='destination-in' setting to draw a series of dots that are masked by a radial gradient. My desired outcome is shown in the screenshot below:
Instead, my canvas is showing the solid gradient with none of the dots visible. Here's my JS:
var canvas = document.getElementById('canvas')
, ctx = canvas.getContext('2d');
var coordMatrix = [
[50, 100, 150, 50, 100, 150],
[50, 50, 50, 100, 100, 100]
];
var gradient = ctx.createRadialGradient(100, 100, 0, 100, 100, 100);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 200);
ctx.globalCompositeOperation = 'destination-in';
coordMatrix[0].forEach(function(xCoord, i) {
var yCoord = coordMatrix[1][i];
ctx.moveTo(xCoord, yCoord);
ctx.arc(xCoord, yCoord, 10, 0, Math.PI * 2, false);
});
And here's a fiddle:
https://jsfiddle.net/73d9jawn/2/
Am I missing something?
You forgot to call ctx.fill() after setting the coordinates for the arcs. Also, you need to call ctx.fill() after the forEach has completed all iterations, otherwise globalCompositeOperation only applies to the first circle drawn. Here is an updated fiddle.
I have been working on a seemingly simple graphic. I wish to create circles, with a line connecting the circles, and filling the circles in with some background. I have almost got it, but this one piece is tripping me up.
I can define the canvas, create the circles, and line connecting them just fine:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = $(window).width();
canvas.height = $(window).height();
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.lineWidth = 10;
//Create two nodes
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
//line connecting two nodes
ctx.moveTo(100, 100);
ctx.lineTo(200, 200);
ctx.stroke();
This would look like this:
What I then do is fill the circles with an image (this is why I use clip()), but using a white color fill for the sake of example demonstrates the problem as well:
//simulate filling in nodes with image, in this case solid color
ctx.clip();
ctx.fillStyle = "white";
ctx.fill();
Now I am almost there, but there are some jagged edges there that I have read is just a little "bug" in Chrome, and also I like that thick black outline on the circles. So, I want to go back over just the 2 circles and outline them. It seems no matter what I do, the context always remembers that line connecting the two, and I end up with the connector line over the top of the image after calling stroke():
//would like to just re-outline circles, not connecting line
ctx.stokeStyle = "black";
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
ctx.stroke();
What I can't figure out is how to just outline the 2 circles again after filling in the white background (loading the image)?
I think about it like drawing in layers. First I draw some lines, then I put the images in, then I draw again on top. Not sure if the html canvas is meant to be used like that. Thanks.
JSFiddle Example Here
You are forgetting to begin a new path.
Whenever you start a new shape you must use ctx.beginPath or the context will redraw all the previous paths.
BTW the jaggy circles is because you are re-rendering them, this causes the edges to get jaggies.
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 500;
document.body.appendChild(canvas);
ctx.setTransform(1,0,0,1,0,-50); // just moving everything up to be seen in snippet.
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.fillStyle = "#FAFAFF";
ctx.lineWidth = 10;
//Create two nodes
/* dont draw the two circle the first time as you are
doubling the render causing the edges to get to sharp
making them appear jaggy.
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
*/
//line connecting two nodes
ctx.moveTo(100, 100);
ctx.lineTo(200, 200);
ctx.stroke();
ctx.beginPath(); // start a new path and removes all the previous paths
//Create two nodes
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
ctx.fill();
ctx.beginPath(); // start a new path and removes all the previous paths
//Create two nodes
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
ctx.stroke();
I am trying to make a radial gradient with a Javascipt/HTML canvas. The problem is that the gradients don't overlap properly as if the alpha channel isn't working.
This is the code I am using:
var gradient1 = ctx.createRadialGradient(300, 300, 300, 300, 300, 0);
gradient1.addColorStop(0,"rgba(0, 0, 0, 0)");
gradient1.addColorStop(1,"#FF0000");
ctx.fillStyle = gradient1;
ctx.fillRect(x1, y1, 600, 600);
ctx.fillRect(x1, y1, 600, 600);
Here is a picture:
This for some reason fades to a black-like color rather than staying red. This leads it to act weird when two of these gradients of different colors are touching.
How can I make this react normally?
Cause
The gradient defined is red-black and both the color and the alpha channel will be interpolated . At 50% it will be halfway between red and black, but also 50% visible which is why it is becoming black-ish.
Cure
To fix make sure both color stops are the same color which just the alpha channel changed. This will keep the color the same all the way:
gradient1.addColorStop(0, "rgba(255, 0, 0, 0)"); // red, but transparent
gradient1.addColorStop(0, "#f00"); // red, solid
Click the link below to see this in action:
var ctx = document.querySelector("canvas").getContext("2d");
var gradient1 = ctx.createRadialGradient(300, 300, 300, 300, 300, 0);
gradient1.addColorStop(0,"rgba(255, 0, 0, 0)");
gradient1.addColorStop(1,"#FF0000");
ctx.fillStyle = gradient1;
ctx.fillRect(0, 0, 600, 600);
ctx.fillRect(0, 0, 600, 600);
<canvas width=600 height=600></canvas>
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?