coloring a line on canvas - javascript

I am trying to color the following line, but my canvas either colors all the lines or does not color at all. Any help would be appreciated
canvas.save();
canvas.scale(1, 0.75);
canvas.beginPath();
canvas.arc(100, 95, 8, 0, Math.PI * 2, false);
canvas.stroke();
canvas.strokeStyle= "red";
canvas.closePath();
canvas.restore();

You are using canvas, I assume you mean context.
canvas=getElementById("mycanvas");
context.getContext("2d");
A few points:
1. Start 1 or more draws with context.beginPath();
2. When you tell the context to context.stroke(), it will use the last strokeStyle you set (previous strokeStyles are ignored)
3. always to context.stroke() to physically apply your drawn lines,arcs,etc to the canvas.
// draw a red circle
context.beginPath();
context.arc(100, 95, 8, 0, Math.PI * 2, false);
context.strokeStyle="red";
context.stroke();
//then begin a new path and draw a blue circle
context.beginPath();
context.arc(150, 95, 8, 0, Math.PI * 2, false);
context.strokeStyle="blue";
context.stroke();

Related

Overlapping clipping issue

I have an issue where I am grabbing a users displayAvatar() and then I use an arc to make the image round. This works fine, however, I need to place a circle on top of that image, but it is getting cut in half because of the previous clip()
Without clip() : https://i.gyazo.com/b474c656f33a1f004f5e3becffcef527.png
With clip() : https://i.gyazo.com/da13377cd3f6dc7516c2b8fd1f0f8ac9.png
I know that in the 'With clip()' image, it appears as if the arc border is showing outside of the clip, but that is hardcoded into the image, I put it as a guide with an image editor.
I tried moving around the code, I removed the line ctx.clip() and saw that my circle displays fine on top of the image.
// circle around avatar
ctx.beginPath();
ctx.arc(122.5, 141.8, 81, 0, Math.PI * 2, true);
ctx.closePath();
ctx.clip();
const avatar = await Canvas.loadImage(
message.author.displayAvatarURL({ format: 'png' })
);
ctx.strokeStyle = '#ffffff';
ctx.strokeRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(avatar, 41.5, 60.5, 162, 162);
// presence circle
ctx.beginPath();
ctx.arc(184.5, 193.5, 19, 0, Math.PI * 2, true);
ctx.strokeStyle = '#000000';
ctx.lineWidth = 8;
ctx.stroke();
ctx.fillStyle = userStatusColor;
ctx.fill();
Take a look at the canvas clip() definition:
https://www.w3schools.com/tags/canvas_clip.asp
Tip: Once a region is clipped, all future drawing will be limited to the clipped region (no access to other regions on the canvas). You can however save the current canvas region using the save() method before using the clip() method, and restore it (with the restore() method) any time in the future.
Below is an example using the save and restore
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(90, 90, 81, 0, Math.PI * 2, true);
ctx.stroke();
ctx.save();
ctx.clip();
ctx.beginPath();
ctx.arc(150, 50, 19, 0, Math.PI * 2, true);
ctx.fillStyle = '#0000ff';
ctx.lineWidth = 8;
ctx.stroke();
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.arc(170, 99, 19, 0, Math.PI * 2, true);
ctx.fillStyle = '#ff0000';
ctx.lineWidth = 8;
ctx.stroke();
ctx.fill();
<canvas id="canvas"></canvas>

How can we take advantage of moveTo( ) HTML5 method?

please I am a little confused so I need your help .
My question is how can we take advantage of moveTo() html5 method ?
for example I found this example on stackOverflow
function drawSmile(ctx, x, y, faceRadius, eyeRadius) {
ctx.save(); // save
ctx.fillStyle = '#FF6'; // face style : fill color is yellow
ctx.translate(x, y); // now (x,y) is the (0,0) of the canvas.
ctx.beginPath(); // path for the face
ctx.arc(0, 0, faceRadius, 0, 6.28);
ctx.fill();
ctx.fillStyle = '#000'; // eye style : fill color is black
ctx.beginPath(); // path for the two eyes
ctx.arc(faceRadius / 2, - faceRadius /3, eyeRadius, 0, 6.28);
ctx.moveTo(-faceRadius / 2, - faceRadius / 3); // sub path for second eye
ctx.arc(-faceRadius / 2, - faceRadius / 3, eyeRadius, 0, 6.28);
ctx.fill();
ctx.restore(); // context is just like before entering drawSmile now.
}
drawSmile(c, 200,200, 60, 12);
but when I removed the line number 11 in the code which uses the moveTo method no thing changed!!!!.
The moveTo() HTML5 method lets you to move your (0,0) origin to another point in the space.
Here you have and example. To draw some kind of triangle:
// first part of the path
ctx.moveTo(20,20);
ctx.lineTo(100, 100);
ctx.lineTo(100,0);
// second part of the path
ctx.moveTo(120,20);
ctx.lineTo(200, 100);
ctx.lineTo(200,0);
// indicate stroke color + draw the path
ctx.strokeStyle = "#0000FF";
ctx.stroke();
In this example we simply called moveTo(x, y) after drawing the first part of the path (the shape on the left). Then, we only called stroke() once to draw the whole path.

HTML5 Canvas, my arc draw a startline to the center

The arc have the following result. Do I have to calculate the start point of the arc myself? jsfiddle link here: jsfiddle link here
canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d');
context.beginPath();
context.strokeStyle = "#FF0000";
context.lineWidth = 1;
// context.moveTo(49, 49);
context.arc(19, 19, 15, 0, 1 * Math.PI);
context.moveTo(49, 49);
context.arc(49, 49, 15, 0, 1 * Math.PI);
context.stroke();
You need to close the path each time:
http://jsfiddle.net/8EDHb/1/
context.beginPath();
context.arc(19, 19, 15, 0, 1 * Math.PI);
context.stroke();
context.closePath();
context.moveTo(49, 49);
context.beginPath();
context.arc(49, 49, 15, 0, 1 * Math.PI);
context.stroke();
context.closePath();
If you want a complete circle you should use 2 * Math.PI.
You can make javascript calculate the starting position, for a circle with center (cx,cy) and radius r.
Use context.arc(cx + r,cy) and so on
If you have definite values,
Eg. Centre at 100,100 and radius 50 use
context.arc(150,100,...)

How to scale a canvas image larger and smaller in a continuous loop?

I created a cloud shape in canvas, and I'm wondering how I can scale the shape back and forth between larger and smaller. Like I want the cloud to get bigger, than smaller, then bigger then smaller, etc.
I was able to be able to move a separate canvas image up and down using a when-then method, but I don't think that method will work by increasing the canvas size because the actual image stays to scale.
Here is my canvas code:
<script>
var canvas = document.getElementById('hardware-cloud');
var context = canvas.getContext('2d');
// begin cloud shape-Hardware
context.beginPath();
context.moveTo(180, 80);
context.bezierCurveTo(150, 132, 143, 165, 203, 154);
context.bezierCurveTo(203, 154, 180, 200, 260, 175);
context.bezierCurveTo(297, 231, 352, 198, 344, 185);
context.bezierCurveTo(344, 185, 372, 215, 374, 175);
context.bezierCurveTo(473, 165, 462, 132, 429, 110);
context.bezierCurveTo(473, 44, 407, 33, 374, 55);
context.bezierCurveTo(352, 10, 275, 22, 275, 55);
context.bezierCurveTo(210, 20, 165, 22, 180, 80);
// complete cloud shape-Hardware
context.closePath();
context.lineWidth = 5;
context.strokeStyle = 'navy';
context.stroke();
context.fillStyle = 'white';
context.fill();
//font inside hardware cloud
context.beginPath();
context.font = 'bold 15pt Calibri';
context.textAlign = 'center';
context.fillStyle ="navy"; // <-- Text colour here
context.fillText('Why not the electronic store?', 300, 120);
context.lineWidth = 2;
context.strokeStyle = 'grey';
context.stroke();
context.closePath();
//top hardware circle
context.beginPath();
context.arc(380, 220, 13, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = 'navy';
context.stroke();
context.fillStyle = 'white';
context.fill();
context.closePath();
//middle hardware circle
context.beginPath();
context.arc(398, 253, 10, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = 'navy';
context.stroke();
context.fillStyle = 'white';
context.fill();
context.closePath();
//bottom hardware circle
context.beginPath();
context.arc(425, 273, 7, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = 'navy';
context.stroke();
context.fillStyle = 'white';
context.fill();
context.closePath();
</script>
And here is my attempt at the jQuery. The first part of it is to get the div region to slide into view. The second part is an attempt to scale up and down.:
$(document).ready(function(){
$('#hardware').hide();
$('#hardware').show("slide", {direction: "left"}, 400 );
ani();
function ani(){
$.when(
$('#hardware-cloud').effect({
scale: "120"},700),
$('#hardware-cloud').effect({
scale: "100"},700)
.then(ani));}
});
You can use scale and translate to change the size of the shape.
All transforms works for the next drawn shape and doesn't affect already drawn shapes.
So for it to work you'll need to clear the canvas, transform and then redraw the shape.
For example, re-factor the code so that you can call shape() to draw the cloud, then:
Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height);
Apply scale ctx.scale(scaleX, scaleY);
Optionally translate the shape using ctx.translate(deltaX, deltaY);
Redraw shape shape();
Repeat using for example requestAnimationFrame() in a loop.
Scale value is 1 = 1:1, 0.5 = half etc. Just remember these transforms are accumulative (you can use setTransform() to set absolute transforms each time).
Update
Here is one way you can do this:
var maxScale = 1, // for demo, this represents "max"
current = 0, // angle (in radians) used to scale smoother
step = 0.02, // speed
pi2 = Math.PI; // cached value
// main loop clears, transforms and redraws shape
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
transform();
drawShape();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
// set scale based on rotation
function transform() {
current += step;
current %= pi2;
// just play around with different combinations here
var s = (maxScale * Math.abs(Math.sin(current))) / maxScale + 0.5;
// set absolute scale
context.setTransform(s, 0, 0, s, 0, 0);
}
// wrap shape calls in a function so it can be reused
function drawShape() {
// begin cloud shape-Hardware
context.beginPath();
... rest goes here...
Online demo
In addition you can use translate() to re-position the shape linked to the rotation value.
Hope this helps!

Problems with using fill() in a canvas - illogical behaviour

I'm trying to learn how to draw/fill different shapes by using canvas and JavaScript, but my shapes doesn't get filled in the way I want them to, at all. The body of my HTML-document is this simple line:
<canvas id="canvas1" width="500" height="500"></canvas>
And my JavaScript-file looks like this:
function draw() {
var canvas1 = document.getElementById('canvas1');
if(canvas1.getContext) {
var ctx = canvas1.getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, 50, 0);
gradient.addColorStop(0, "blue");
gradient.addColorStop(1, "white");
ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(100, 25);
ctx.stroke();
ctx.moveTo(25, 50);
ctx.bezierCurveTo(25, 50, 50, 80, 75, 60)
ctx.fillStyle = "black";
ctx.fill();
ctx.beginPath();
ctx.moveTo(75, 100);
ctx.arc(50, 100, 25, 0, Math.PI*2, true);
ctx.fillStyle = "black";
ctx.fill();
ctx.beginPath();
ctx.fillStyle = gradient;
ctx.arc(75, 150, 25, 0, Math.PI*2, true);
ctx.fill();
}
}
But this is the result:
And I don't get it. I've tried filling my second circle with every other color, and that works just fine. Also if I remove the last "ctx.beginPath();" my first circle gets painted in gradient. But I can't get the same bug to work on my second circle by changing the position of the code or something. And every guide I've found tells me that this should work, as far as I understand it.
Gradients are defined with an absolute position so if you draw your circle outside the area defined by the gradient it will appear transparent instead of filled.
There is no need to close the path as the fill() method will close it implicit for you, but just make sure the coordinates in the gradient covers the area you want to fill.
Instead of calculating for each time you need to fill an arc you could create a generic wrapper function which takes a position and colors to fill (adjust as needed):
A demo here
/**
* Fills a circle with a two-color gradient.
* #param {Number} cx - center X
* #param {Number} cy - center Y
* #param {Number} radius - radius
* #param {String} col1 - start color as CSS color string
* #param {String} col2 - end color as CSS color string
* #param {Boolean} [horiz=false] - Set true for horizontal gradient
*/
function fillCircle(cx, cy, radius, col1, col2, horiz) {
var x = cx - radius,
y = cy - radius,
d = radius * 2,
gradient;
if (horiz) {
gradient = ctx.createLinearGradient(x, 0, x+d, d);
}
else {
gradient = ctx.createLinearGradient(0, y, 0, y+d);
}
gradient.addColorStop(0, col1);
gradient.addColorStop(1, col2);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
}
Then just use it this way:
fillCircle(200, 200, 70, 'yellow', 'red');
The last flag is optional here and makes a horizontal gradient if set to true.
Use ctx.closePath(); After each separate shape/line you want is done.
ctx.beginPath();
ctx.moveTo(25, 50);
ctx.bezierCurveTo(25, 50, 50, 80, 75, 60)
ctx.strokeStyle = "black";
ctx.stroke();
ctx.closePath();
The gradient needs to be set with the coordinates matching where your shape is on the canvas.
You have the gradient starting at 0,0,
var gradient = ctx.createLinearGradient(0, 0, 50, 0);
But your circle is locates at 25,50. Make your gradient coordinates the same as you circle coordinates.
http://jsfiddle.net/bC75t/1/

Categories