Rotation logic in html5 canvas - javascript

I have this animation, but i cant get over the logic. I hope someone can help me here.
Basicly i need this: http://jsfiddle.net/PDE85/9/ but without the arrow doing such crazy moves. It should be attached to the front of the open circle to simulate an expanding arrow.
I got the triangle to turn right here but it doesnt work when i mix it with position logic as seen in the first example.
Here is the code for reference
(function() {
var size = ($(window).height()/5)*4;
$("#intro-container").css('width',size);
$("#intro-canvas").css('width',size);
$("#intro-canvas").css('height',size);
var interval = window.setInterval(draw, 30);
var degrees = 0.0;
var offset = 20;
var rotate = 0;
var canvas = document.getElementById('intro-canvas');
var ctx = canvas.getContext('2d');
canvas.width = size;
canvas.height = size;
draw();
function draw() {
if (canvas.getContext) {
ctx.fillStyle="white";
ctx.strokeStyle="white";
ctx.clearRect(0, 0, size, size);
ctx.save();
ctx.translate(size/2, size/2);
ctx.rotate(-90 * Math.PI / 180);
ctx.beginPath();
ctx.lineWidth = size/8;
ctx.arc(0, 0, size/3, 0, rotate * Math.PI / 180);
//ctx.shadowBlur=1;
//ctx.shadowColor="black";
ctx.stroke();
ctx.restore();
ctx.beginPath();
ctx.save();
// moving logic
ctx.translate(size/2, size/2);
ctx.rotate(-Math.PI / 180 * -rotate+1);
ctx.translate(-size/3, -size/3);
// rotating logic
ctx.translate(size/2, size/2);
ctx.rotate((rotate * Math.PI + 420) / 180);
ctx.moveTo(0,0);
ctx.lineTo(size/6,0);
ctx.lineTo(0,size/6);
ctx.lineTo(0,0);
ctx.fill();
ctx.restore();
rotate += 1;
if(rotate > 360){
window.clearInterval(interval)
}
}
}
})();

I believe you are looking for this : http://jsfiddle.net/PDE85/12/
The rotation comes from, the rotate call which is unnecessary.
Plus you need an inverted triangle, hence the coordinates needed an update:
...
// ctx.rotate((rotate * Math.PI + 420) / 180);
ctx.moveTo(0,0);
ctx.lineTo(-size/6,0);
ctx.lineTo(0,-size/6);
...

Related

Create an arc distortion of an image with canvas

I would like to create an arc distortion of an image with canvas.
My goal is to do the same thing as imagemagick but in javascript with canvas: https://legacy.imagemagick.org/usage/distorts/#circular_distorts
Here is the expected result with the angle parameter that corresponds to the images below:
60°, 120°, 180°, 270°, 360°
I only found two interesting codes that go in the right direction:
This experimental script
which works directly on the pixel array but does not keep the aspect ratio of the original image and the angle given as a parameter does not work well:
https://github.com/sergiks/canvas-text-arc
This other script
which makes a rotation on each column of the image with drawimage but does not allow to configure the angle of the arc, it is a 360° rotation by default:
http://jsfiddle.net/hto1s6fy/
var cv = document.getElementById('cv');
var ig = document.getElementById('ig');
var ctx = cv.getContext('2d');
// draw the part of img defined by the rect (startX, startY, endX, endY) inside
// the circle of center (cx,cy) between radius (innerRadius -> outerRadius)
// - no check performed -
function drawRectInCircle(img, cx, cy, innerRadius, outerRadius, startX, startY, endX, endY) {
var angle = 0;
var step = 1 * Math.atan2(1, outerRadius);
var limit = 2 * Math.PI;
ctx.save();
ctx.translate(cx, cy);
while (angle < limit) {
ctx.save();
ctx.rotate(angle);
ctx.translate(innerRadius, 0);
ctx.rotate(-Math.PI / 2);
var ratio = angle / limit;
var x = startX + ratio * (endX - startX);
ctx.drawImage(img, x, startY, 1, (endY - startY), 0, 0, 1, (outerRadius - innerRadius));
ctx.restore();
angle += step;
}
ctx.restore();
}
var cx = 300,
cy = 300;
var innerRadius = 0;
var outerRadius = 300;
var startX = 0,
endX = 1361,
startY = 0,
endY = 681;
drawRectInCircle(ig, cx, cy, innerRadius, outerRadius, startX, startY, endX, endY);
Imagemagick source code
Finally, I also looked at the C source code of imagemagick but I don't have the skills to transpose it:
https://github.com/imagemagick/imagemagick/blob/main/magickcore/distort.c
(to see what concerns arc distortion, use the keyword "ArcDistortion")
Though this is an interesting topic and I also like to re-invent the wheel sometimes, it isn't necessary in this case. Someone else had a go at it yet and released a JavaScript library called lens, which replicates some of ImageMagick's filters. Luckily the 'Arc distortion' is among those.
Lens offers a method called distort() which accepts an input like a <canvas> element, applies the transformation requested and outputs raw pixel data, which you can then use to make another <canvas>.
Here's a quick example:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.font = "48px sans";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#0";
ctx.fillText("Around the World", canvas.width / 2, canvas.height / 2);
async function makeArc(image, angle, rotate = 0) {
let result = await lens.distort(
image,
lens.Distortion.ARC, [angle, rotate], {
imageVirtualPixelMethod: angle === 360 ? lens.VirtualPixelMethod.HORIZONTAL_TILE : lens.VirtualPixelMethod.TRANSPARENT
}
);
let tempCanv = document.createElement("canvas");
let tempCtx = tempCanv.getContext("2d");
tempCanv.width = result.image.width;
tempCanv.height = result.image.height;
tempCtx.putImageData(result.image.imageData, 0, 0);
document.body.appendChild(tempCanv);
}
makeArc(canvas, 120, 0);
<script src="https://cdn.jsdelivr.net/npm/#alxcube/lens#1.0.0/dist/lens.min.js"></script>
<canvas id="canvas" width="450" height="50"></canvas>

Why is the drawn line not clearing from canvas?

Link to JSFiddle for entire code: https://jsfiddle.net/u4mk0gdt/
I read the Mozilla docs on save() and restore() and I thought that "save" saved the current state of the entire canvas and "restore" restored the canvas to the most recent "save" state. Hence I placed the saves and restores in such a way that it should clear the white line that is drawn to canvas after is is drawn. However when I run this code the white line is never cleared from the canvas and is drawn continually without clearing.
ctx.restore();
ctx.save(); // <--should save blank canvas
//DRAW LINE
ctx.moveTo(tMatrix.x1, tMatrix.y1);
ctx.lineTo(w/2,h/2);
ctx.strokeStyle = "white";
ctx.stroke();
ctx.restore(); // <-- should restore to the "save()" above
ctx.save(); // <-- <--should save blank canvas again
As you can see, I made a lot of modifications to your code:
console.log("rotating_recs");
// create canvas and add resize
var canvas, ctx;
function createCanvas() {
canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.left = "0px";
canvas.style.top = "0px";
canvas.style.zIndex = 1000;
document.body.appendChild(canvas);
}
function resizeCanvas() {
if (canvas === undefined) {
createCanvas();
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
}
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
var Player = function(x, y, height, width, rot) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.rot = rot;
this.objWinX = 0; //translate the window object and then apply to this
this.objWinY = 0;
this.draw = function() {
//rotate by user.rot degrees, from the players center
ctx.translate(this.x + this.width / 2, this.y + this.height / 2)
ctx.rotate(this.rot * Math.PI / 180)
ctx.translate(-this.x - this.width / 2, -this.y - this.height / 2)
ctx.fillStyle = "grey";
ctx.fillRect(this.x, this.y, this.height, this.width);
ctx.translate(this.x + this.width / 2, this.y + this.height / 2)
ctx.rotate(-this.rot * Math.PI / 180)
ctx.translate(-this.x - this.width / 2, -this.y - this.height / 2)
}
}
var user = new Player(0, 0, 40, 40, 0);
var user2 = new Player(0, 0, 40, 40, 0);
let rot = 0;
function update(time) {
var w, h;
w = canvas.width; // get canvas size incase there has been a resize
h = canvas.height;
ctx.clearRect(0, 0, w, h); // clear the canvas
//MIDDLE RECT
/*
if you don't want this you can just translate by w/2 and h/2, but I would recommend just making the p layers position the middle
*/
user.x = w / 2 - 20;
user.y = h / 2 - 20;
user.rot += 0.5 // or whatever speed
user.draw(); //draw player -- look at the draw function I added some stuff
//LINE
/*
I don't know what you are trying to do, but I just drew the line to the user2's position,
if this doesn't work for your scenario you can change it back
*/
ctx.beginPath()
ctx.moveTo(user2.x + user2.width/2, user2.y + user2.height/2);
ctx.lineTo(w / 2, h / 2);
ctx.strokeStyle = "white";
ctx.stroke();
//FAST SPIN RECT
/*
There are multiple ways to do this, the one that I think you should do, is actually change the position of user two, this uses some very simple trigonometry, if you know this, this is a great way to do this, if not, you can do it how you did previously, and just translate to the center, rotate, and translate back. Similar to what I did with the player draw function. I am going to demonstrate the trig way here:
*/
user2.rot += 5
rot += 2;
user2.x = w/2 + (w/2) * Math.cos(rot * (Math.PI/180))
user2.y = h/2 + (w/2) * Math.sin(rot * (Math.PI/180))
user2.draw();
//RED RECT
ctx.fillStyle = 'red';
ctx.fillRect(140, 60, 40, 40);
requestAnimationFrame(update); // do it all again
}
requestAnimationFrame(update);
While I think you should add some of these modifications into you code, they are not super necessary. To fix you line problem, all you had to do was add ctx.beginPath() before you drew it. The demonstration that I made was not very good (hence demonstration), and you probably shouldn't use it exactly, but definitely look over it. The modified code for you line drawing would look like:
//LINE
ctx.beginPath()
ctx.moveTo(tMatrix.x1, tMatrix.y1);
ctx.lineTo(w/2,h/2);
ctx.strokeStyle = "white";
ctx.stroke();
ctx.restore();
ctx.save();
Hope this helps :D
Sorry for bad spelling

canvas rotate img horizontally

I am working on animation optimisation and i wanted to try out canvas to see how it performs but i am not experienced well in canvas and i dont know how to prepare concept of this kind of animation.
this is the gif that shows how animation should rotate like:
this is my current code of js:
var cvs = document.getElementById('coin-spin'),
ctx = cvs.getContext('2d'),
w = cvs.width = 400,
h = cvs.height = 400,
cx = w / 2,
cy = h / 2,
a = 0;
var img = new Image();
var loop = function() {
// BG
ctx.fillStyle = '#ccc';
ctx.fillRect(0, 0, w, h);
// draw image
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(Math.PI / 180 * a);
ctx.translate(-cx, -cy);
ctx.drawImage(img, cx - (img.width / 2), cy - (img.height / 2));
ctx.restore();
// axis
ctx.strokeStyle = '#000';
ctx.beginPath();
ctx.moveTo(cx, 0);
ctx.lineTo(cx, h);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, cy);
ctx.lineTo(w, cy);
ctx.stroke();
//mod angle
a++;
window.requestAnimationFrame(loop);
};
img.onload = function() {
loop();
};
img.src = 'https://image.ibb.co/gqkeXx/coin.png';
and the working demo on fiddle.
Could someone show how to add to the code so the image would rotate horizontally like on the gif?
EDIT ----
I added the spin, as it was also something to do, but still struggling on how to rotate it.
To get around the problem of rotating the object along two axes (faking one by mapping width to a sine wave), you can use an offscreen canvas to render the coin rotating around one axis, then render that canvas applying the second rotation ;
//make an offscreen canvas for rendering the coin rotating around one axis
var offscreenCanvas = document.createElement('canvas');
var cvs = document.getElementById('coin-spin'),
ctx = cvs.getContext('2d'),
w = cvs.width = 400,
h = cvs.height = 400,
cx = w / 2,
cy = h / 2,
a = 0;
var img = new Image();
var frameCount = 0;
var loop = function() {
frameCount++;
// BG
ctx.fillStyle = '#ccc';
ctx.fillRect(0, 0, w, h);
offscreenContext.fillStyle = '#ccc';
offscreenContext.fillRect(0, 0, w, h);
//determine how wide to render the offscreen canvas so we can fake
//rotation around the second axis
var imgRenderWidth = offscreenCanvas.width * Math.sin(frameCount/10.0)
//render the coin rotating around one axis to the offscreen canvas
offscreenContext.save();
offscreenContext.translate(img.width/2, img.height/2);
offscreenContext.rotate(Math.PI / 180 * a);
offscreenContext.translate((0-img.width)/2, (0-img.height)/2);
offscreenContext.drawImage(img, 0,0);
offscreenContext.restore();
// draw offscreen canvas to the screen with our precalculated width
ctx.save();
ctx.drawImage(offscreenCanvas, cx - (imgRenderWidth / 2), cy - (offscreenCanvas.height / 2), imgRenderWidth, offscreenCanvas.height);
ctx.restore();
// axis
ctx.strokeStyle = '#000';
ctx.beginPath();
ctx.moveTo(cx, 0);
ctx.lineTo(cx, h);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, cy);
ctx.lineTo(w, cy);
ctx.stroke();
//mod angle
a++;
window.requestAnimationFrame(loop);
};
//once the image has loaded, we know what size our offscreen canvas needs to be
img.onload = function() {
offscreenCanvas.width = img.width;
offscreenCanvas.height = img.height;
loop();
};
img.src = 'https://image.ibb.co/gqkeXx/coin.png';
//prepare the offscreen context so we can render to it later
var offscreenContext = offscreenCanvas.getContext('2d');
https://jsfiddle.net/ay3h5vuo/

Canvas figures moves to the right side

Here is the code:
$(document).ready(function() {
var canvas = $("#canvas")[0];
var context = canvas.getContext("2d");
canvas.width = window.innerWidth
canvas.height = window.innerHeight
polygon(context, 120, 120, 50, 12);
context.stroke();
})
function polygon(ctx, x, y, radius, sides) {
if (sides < 3) return;
var a = ((Math.PI * 2) / sides);
ctx.beginPath();
ctx.translate(x, y);
ctx.moveTo(radius, 0);
for (var i = 1; i < sides; i++) {
ctx.lineTo(radius * Math.cos(a * i), radius * Math.sin(a * i));
}
ctx.closePath();
}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<canvas id="canvas">OOPS.. Upgrade your Browser</canvas>
This code works fine. BUT each of my polgon appears at wrong place. For example I call
polygon(context, 120,120,50,12);
and
polygon(context, 120,220,50,12);
and the second polygon appears at x=220, y=220
I mean, they moves in the right side, but they should appear one under another with the same x coordinates.
After drawing the shape you need to translate it back to the original position so the next shape is drawn from the same relative location as the first.
ctx.translate(-x, -y);
$(document).ready(function() {
var canvas = $("#canvas")[0];
var context = canvas.getContext("2d");
canvas.width = window.innerWidth
canvas.height = window.innerHeight
polygon(context, 120, 120, 50, 12);
context.stroke();
polygon(context, 120,220,50,12);
context.stroke();
})
function polygon(ctx, x, y, radius, sides) {
if (sides < 3) return;
var a = ((Math.PI * 2) / sides);
ctx.beginPath();
ctx.translate(x, y);
ctx.moveTo(radius, 0);
for (var i = 1; i < sides; i++) {
ctx.lineTo(radius * Math.cos(a * i), radius * Math.sin(a * i));
}
ctx.closePath();
ctx.translate(-x, -y);
}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<canvas id="canvas">OOPS.. Upgrade your Browser</canvas>
Reset the translation matrix to the identity matrix before drawing each shape:
context.setTransform(1, 0, 0, 1, 0, 0);
I think it happens because of:
ctx.translate(x, y);
If you look closely at HTML canvas translate() Method Definition and Usage you will see that translate() method remaps the (0,0) position of the canvas instead of setting starting point for your drawing. So if you do function call like this:
ctx.translate(120, 120);
ctx.translate(120, 220);
You actually moving registration point of canvas twice. First time it will be moved by (120,120) and later it will be moved by (120,220), so your first polygon will be drawed correctly but the second will be drawed on position (240,340), because coordinates of both starting points will eventually be summed.
You are using the same 2d context over and over for your polygons. The context will save the state of your translations, so consecutive translations add up. You could "revert" the effect of translations by translating with same-negative-values at the end of each polygon call.
$(document).ready(function() {
var canvas = $("#canvas")[0];
var context = canvas.getContext("2d");
canvas.width = window.innerWidth
canvas.height = window.innerHeight
polygon(context, 120, 120, 50, 12);
context.stroke();
polygon(context, 120, 220, 50, 12);
context.stroke();
})
function polygon(ctx, x, y, radius, sides) {
if (sides < 3) return;
var a = ((Math.PI * 2) / sides);
ctx.beginPath();
ctx.translate(x, y);
ctx.moveTo(radius, 0);
for (var i = 1; i < sides; i++) {
ctx.lineTo(radius * Math.cos(a * i), radius * Math.sin(a * i));
}
ctx.closePath();
ctx.translate(-1 * x, -1 * y);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<canvas id="canvas">OOPS.. Upgrade your Browser</canvas>

drawing Inscribe a Circle in a Triangle using a canvas

I am creating
"Inscribe a Circle in a Triangle using a canvas". But facing lots of problem. Well I tried to draw and Triangle in the middle of the canvas though its created i am wondering where to start drawing an circle which could be perfectly work for me.
With respective to mathematically I knew to draw circle, but when it comes to java script i am stuck.
kindly help me.
Thanks.
i have tried the following code to draw an traing at center of the canvas:-
var c=document.getElementById("myCanvas");
var context =c.getContext("2d");
check(ctx, 100, c.width/2, c.height/2);
function check(ctx, side, cx, cy){
var h = side * (Math.sqrt(3)/2);
ctx.strokeStyle = "black";
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
ctx.moveTo(0,-h/2);
ctx.lineTo(-side/2, h / 2); // line a
ctx.lineTo(side /2, h / 2); // line b
ctx.lineTo(0,-h /2); // line c
ctx.stroke();
ctx.closePath();
ctx.save();
}
like this i want..
Ok check this .. Live Demo for equilateral triangle
Radius of circle inscribed in equilateral triangle = Sqrt(3)/6 * side
of triangle;
window.onload = function()
{
var c=document.getElementById("myCanvas");
var context =c.getContext("2d");
check(context,100,c.width/2,c.height/2);
circle(context,100,c.width/2,c.height/2);
}
function check(ctx, side, cx, cy){
var h = side * (Math.sqrt(3)/2);
ctx.strokeStyle = "black";
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
ctx.moveTo(0,-h/2);
ctx.lineTo(-side/2, h / 2); // line a
ctx.lineTo(side /2, h / 2); // line b
ctx.lineTo(0,-h /2); // line c
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function circle(ctx,side,cx,cy)
{
var h = side * (Math.sqrt(3)/2);
var radius = Math.sqrt(3)/6 * side; // Radius of the circle
cy = cy + h/2 - radius; // Center Y of circle
ctx.beginPath();
ctx.arc(cx,cy,radius,0,Math.PI * 2,false);
ctx.stroke();
ctx.closePath();
}
Check all formulas to find the radius of circle inscribed in different triangles here

Categories