I'm trying to do a canvas where you can draw a line with your mouse and this line is being mirrored while drawing. So at the end you got a symmetry drawing effect. I wrote a draw function and included the transform and scale attribute. Sadly now the line has small little gaps and is not fluent anymore.
function draw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.translate(canvas.width, 0);
ctx.scale(-1, 1);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
ctx.closePath();
}
See screenshot here
Without the translate() and scale() attribute the line is clear, as you can see here. Do you have any idea why the line is dotted and how I can fix that?
This is because every time you move the mouse, you are only drawing one line, either "normal" or "mirrored" but not both. You can see that your "dots" actually correspond to the dashes of the other side.
The switching happens because you apply the canvas transform once per draw call, but don't reset it, so at first mouse move, you mirror the context and at second one you'll finally draw mirrored and then switch back for the next move.
To do what you want, you could keep all your points in an Array, clear your context at the beginning of every frame and redraw the lines completely twice, once normal, and once mirrored.
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
// trace in normal mode
points.forEach(({x, y}) => ctx.lineTo(x, y));
// mirror
ctx.translate(canvas.width, 0);
ctx.scale(-1, 1);
// move to first point so we can paint in a single path
ctx.moveTo(points[0].x, points[0].y);
// trace all the points mirrored
points.forEach(({x, y}) => ctx.lineTo(x, y));
// paint
ctx.stroke();
// reset the context transform
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
Related
Say I have drawn a circle on a canvas that has something else drawn on it that stops me from clearing the canvas - due to the other element being randomly generated
var circleX = 50;
var circleY = 10;
var moveCircX = 2;
var moveCircY = 3;
function createCirc(){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(circleX, circleY, 10, 0, Math.PI*2, true);
ctx.fill();
}
function circMove(){
circleY = (circleY + circMoveY)
//then validation to stop it from being drawn of the canvas
So what I'm trying to do is move the circle but clear the previous drawn circle from the canvas. So is there a solution to clearing the circle or would it be easier to create a sprite that replicates the circle?
Since your background isn't changing, the simplest strategy is to copy the background before you first draw your circle, then draw your circle. When you're moving, redraw that part of the background from the copy you kept, then draw your circle in the new place.
An efficient way to do that is to use getImageData and putImageData.
So, (my javascript is rusty, so this may not be perfect. Feel free to correct any mistakes), before the first time you createCirc, simply do:
imageData = ctx.getImageData(0,0, ctx.canvas.width, ctx.canvas.height)
And, in your circMove function, before you move and redraw the circle, you want:
ctx.putImageData(imageData, circleX, circleY, circleX, circleY, 2*circle_radius, 2*circle_radius)
(You don't define circle_radius, but I'm sure you must have a similar value. I'm using 2x the radius to presumably be the size of the image that is drawn.)
I need to draw a dynamic donut chart - something similar to -
http://194.90.28.56/~dev1/t.jpg
The green part indicates the percentage (in this case 27%) - it must be dynamic.
I think I need to do something like - Android - How to draw an arc based gradient
But with JS..
Thanks.
Great question. Gradients along paths in canvas are hard. The easiest way is to fudge it.
Instead of thinking of your image as a gradient that follows a circular path, think of it as two linear gradients.
One on the left side, going from green to gray, top to bottom.
The other on the right side, going from white to gray, top to bottom.
Imagine a square made of those two gradients:
Now imagine a circle cutting through:
That's all you gotta do.
To "cut" through like that its easiest to use clipping regions, so I've made an example doing that.
Here's the live example: http://jsfiddle.net/simonsarris/Msdkv/
Code below! Hope that helps.
var greenPart = ctx.createLinearGradient(0,0,0,100);
greenPart.addColorStop(0, 'palegreen');
greenPart.addColorStop(1, 'lightgray');
var whitePart = ctx.createLinearGradient(0,0,0,100);
whitePart.addColorStop(0, 'white');
whitePart.addColorStop(1, 'lightgray');
var width = 20;
ctx.lineWidth = width;
// First we make a clipping region for the left half
ctx.save();
ctx.beginPath();
ctx.rect(-width, -width, 50+width, 100 + width*2);
ctx.clip();
// Then we draw the left half
ctx.strokeStyle = greenPart;
ctx.beginPath();
ctx.arc(50,50,50,0,Math.PI*2, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
// Then we make a clipping region for the right half
ctx.save();
ctx.beginPath();
ctx.rect(50, -width, 50+width, 100 + width*2);
ctx.clip();
// Then we draw the right half
ctx.strokeStyle = whitePart;
ctx.beginPath();
ctx.arc(50,50,50,0,Math.PI*2, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
So I have a rotating canvas element which has an arc drawn inside it (the smaller planet):
http://jsfiddle.net/neuroflux/9L689/4/ (updated)
But I can't seem to get the anti-aliasing on the edges of the smaller planet smoother - any ideas?
Cheers!
edit: is there a way to increase the number of iterations used within an arc?
Your problem is not that the arc doesn't have enough points, but that in Chrome the .clip() operation doesn't use anti-aliasing to produce the clipping path.
See Chromium Issues 7508 and 132442
To see this in action, look at http://jsfiddle.net/alnitak/YMtdZ/ in Chrome.
markup:
<canvas id="c" width="600" height="300" />
code:
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'black';
ctx.save();
ctx.beginPath();
ctx.arc(150, 150, 140, 0, 2 * Math.PI);
ctx.clip();
ctx.fillRect(0, 0, 600, 300);
ctx.restore();
ctx.beginPath();
ctx.arc(450, 150, 140, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
The left-hand circle is drawn with clipping, and is aliased. The right-hand circle is drawn "normally", and is anti-aliased.
FWIW, in Firefox and Safari both images look the same. I can't test it on IE.
The only work around I can imagine (until Chrome gets fixed) would be to render the image off-screen into a canvas 3 or 4 times larger, and then copy that with down-sampling into the displayed canvas.
I'm trying to draw a circle and then have an image follow the circle around. Later I want to rotate and move the image around with respect to the circle drawn. The problem I'm facing is that when I try to rotate the image it won't rotate. It also doesn't show me an error in the console. I have functions allowing me to move the circle around and the image moves with it, I just can't seem to rotate the image.
Here is the code:
draw: function(){
//draw self on canvas;
//intended only to be called from update, should never
//need to be deliberately called
ctx = this.context;
ctx.save();
ctx.fillStyle="#000000";
ctx.beginPath();
//void arc(double x, double y,
// double radius, double startAngle, double endAngle,
// optional boolean anticlockwise = false);
ctx.arc(this.x,this.y,this.size,0,Math.PI*2,true);
ctx.closePath();
ctx.fill();
//ctx.translate(this.x, this.y);
ctx.rotate(this.imgAngle);
//draw the hammer
ctx.drawImage(this.hammer,this.hammerX,this.hammerY,100,100)
ctx.rotate(Math.PI/2);
ctx.restore();
},
Live Demo
Try changing your code to the following. You need to perform the rotation before drawing. You can translate the canvas to the entities position and then draw the image at x:0,y:0 to get the effect you desire. Note I did 0-50,0-50 because that puts the point of origin in the center since the width and height are 100. Meaning your image will rotate around its center rather than around its corner.
//draw the hammer
ctx.translate(this.hammerX, this.hammerY);
ctx.rotate(this.imgAngle);
ctx.drawImage(this.hammer,0-50,0-50,100,100);
The rotation will only affect drawings made AFTER the rotation is done.
You could try moving the rotate calls to just before the object that needs to be rotated?
I have found similar questions out there, but no answer. I have sketched a circle like so
ctx.strokeStyle='rgb(0,0,0)';
ctx.lineWidth=10;
ctx.beginPath();
ctx.arc(100,100,45,0,Math.PI*2,true);
ctx.closePath();
ctx.stroke();
which gives a circle situated at (100,100) with radius 45, plus 5 for the linewidth, making it a circle of radius 50. Now, I want to sketch the exact same circle, but another color, and only 1/4 of the original circumfrance (think the XBOX 360 red ring of doom). So I tried this
ctx.strokeStyle='rgb(0,250,0)';
ctx.lineWidth=10;
ctx.beginPath();
ctx.arc(100,100,45,0,Math.PI/2,true); //use 1/4 of original angle
ctx.closePath();
ctx.stroke();
But that has the really annoying aspect of connecting the first and last points (sometimes I wonder who created the canvas element, like when embedding text, but don't get me started on that...)
I've commented out the line you don't want. By calling closePath(), you are closing the path of your arc.
Example
JavaScript
ctx.strokeStyle='rgb(0,250,0)';
ctx.lineWidth=10;
ctx.beginPath();
ctx.arc(100,100,45,0,Math.PI/2,true); //use 1/4 of original angle
//ctx.closePath();
ctx.stroke();
jsFiddle.