Simple shape transformation star -> circle canvas - javascript

I was just wondering if it is possible to transform between two shapes using just canvas.
ie: star to a cirle.
this is what I have so far:
var canvas,
ctx,
length = 15;
canvas = document.getElementById("star");
ctx = canvas.getContext("2d");
ctx.translate(30, 30);
ctx.rotate((Math.PI * 1 / 10));
for (var i = 5; i--;) {
ctx.lineTo(0, length);
ctx.translate(0, length);
ctx.rotate((Math.PI * 2 / 10));
ctx.lineTo(0, -length);
ctx.translate(0, -length);
ctx.rotate(-(Math.PI * 6 / 10));
}
ctx.lineTo(0, length);
ctx.closePath();
ctx.fill();

Here is a basic transition in pure canvas. Using arcs instead of lines is left as an exercise for the reader ;)
http://jsfiddle.net/pD9CM/
var canvas,
ctx,
length = 15;
canvas = document.getElementById("star");
ctx = canvas.getContext("2d");
var max = 50;
var inset = 0;
var direction = +1;
function draw() {
ctx.clearRect(0, 0, 300, 300);
ctx.beginPath();
var i = 11
while (i--) {
var angle = (i/10) * Math.PI * 2;
var distance = (i % 2 === 0) ? (max - inset) : max;
var pt = point(angle, distance);
if (i === 0) ctx.moveTo(pt.x + 150, pt.y + 150);
else ctx.lineTo(pt.x + 150, pt.y + 150);
}
ctx.fill();
if (inset < 0 || inset > 30) direction = -direction;
inset += direction;
}
function point(angle, distance) {
return {
x: Math.cos(angle) * distance,
y: Math.sin(angle) * distance
};
}
setInterval(draw, 20);

Related

Animating multiple circles in a canvas

I'm trying to make an animation inside a canvas: here, a numbered circle must be drawn and move from left to right one single time, disappearing as soon as it reaches the end of animation.
For now I managed to animate it in loop, but I need to animate at the same time (or with a set delay) multiple numbered circles, strating in different rows (changing y position) so they wont overlap.
Any idea how can I manage this? my JS code is the following:
// Single Animated Circle - Get Canvas element by Id
var canvas = document.getElementById("canvas");
// Set Canvas dimensions
canvas.width = 300;
canvas.height = 900;
// Get drawing context
var ctx = canvas.getContext("2d");
// Radius
var radius = 13;
// Starting Position
var x = radius;
var y = radius;
// Speed in x and y direction
var dx = 1;
var dy = 0;
// Generate random number
var randomNumber = Math.floor(Math.random() * 60) + 1;
if (randomNumber > 0 && randomNumber <= 10) {
ctx.strokeStyle = "#0b0bf1";
} else if (randomNumber > 10 && randomNumber <= 20) {
ctx.strokeStyle = "#f10b0b";
} else if (randomNumber > 20 && randomNumber <= 30) {
ctx.strokeStyle = "#0bf163";
} else if (randomNumber > 30 && randomNumber <= 40) {
ctx.strokeStyle = "#f1da0b";
} else if (randomNumber > 40 && randomNumber <= 50) {
ctx.strokeStyle = "#950bf1";
} else if (randomNumber > 50 && randomNumber <= 60) {
ctx.strokeStyle = "#0bf1e5";
}
function animate3() {
requestAnimationFrame(animate3);
ctx.clearRect(0, 0, 300, 900);
if (x + radius > 300 || x - radius < 0) {
x = radius;
}
x += dx;
ctx.beginPath();
ctx.arc(x, y, 12, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillText(randomNumber, x - 5, y + 3);
}
// Animate the Circle
animate3();
<canvas id="canvas"></canvas>
Here is a solution which doesn't use classes as such and separates the animation logic from the updating - which can be useful if you want more precise control over timing.
// Some helper functions
const clamp = (number, min, max) => Math.min(Math.max(number, min), max);
// Choose and remove random member of arr with equal probability
const takeRandom = arr => arr.splice(parseInt(Math.random() * arr.length), 1)[0]
// Call a function at an interval, passing the amount of time that has passed since the last call
function update(callBack, interval) {
let now = performance.now();
let last;
setInterval(function() {
last = now;
now = performance.now();
callBack((now - last) / 1000);
})
}
const settings = {
width: 300,
height: 150,
radius: 13,
gap: 5,
circles: 5,
maxSpeed: 100,
colors: ["#0b0bf1", "#f10b0b", "#0bf163", "#f1da0b", "#950bf1", "#0bf1e5"]
};
const canvas = document.getElementById("canvas");
canvas.width = settings.width;
canvas.height = settings.height;
const ctx = canvas.getContext("2d");
// Set circle properties
const circles = [...Array(settings.circles).keys()].map(i => ({
number: i + 1,
x: settings.radius,
y: settings.radius + (settings.radius * 2 + settings.gap) * i,
radius: settings.radius,
dx: settings.maxSpeed * Math.random(), // This is the speed in pixels per second
dy: 0,
color: takeRandom(settings.colors)
}));
function drawCircle(circle) {
ctx.strokeStyle = circle.color;
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillText(circle.number, circle.x - 5, circle.y + 3);
}
function updateCircle(circle, dt) {
// Update a circle's position after dt seconds
circle.x = clamp(circle.x + circle.dx * dt, circle.radius + 1, settings.width - circle.radius - 1);
circle.y = clamp(circle.y + circle.dy * dt, circle.radius + 1, settings.height - circle.radius - 1);
}
function animate() {
ctx.clearRect(0, 0, settings.width, settings.height);
circles.forEach(drawCircle);
requestAnimationFrame(animate);
}
update(dt => circles.forEach(circle => updateCircle(circle, dt)), 50);
animate();
<canvas id="canvas" style="border: solid 1px black"></canvas>
Here I transformed your sample code into a class ...
We pass all the data as a parameter, you can see that in the constructor, I simplified a lot of your code to keep it really short, but all the same drawing you did is there in the draw function
Then all we need to do is create instances of this class and call the draw function inside that animate3 loop you already have.
You had a hardcoded value on the radius:
ctx.arc(x, y, 12, 0, Math.PI * 2, false)
I assume that was a mistake and fix it on my code
var canvas = document.getElementById("canvas");
canvas.width = canvas.height = 300;
var ctx = canvas.getContext("2d");
class Circle {
constructor(data) {
this.data = data
}
draw() {
if (this.data.x + this.data.radius > 300 || this.data.x - this.data.radius < 0) {
this.data.x = this.data.radius;
}
this.data.x += this.data.dx;
ctx.beginPath();
ctx.arc(this.data.x, this.data.y, this.data.radius, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillText(this.data.number, this.data.x - 5, this.data.y + 3);
}
}
circles = []
circles.push(new Circle({radius:13, x: 10, y: 15, dx: 1, dy: 0, number: "1"}))
circles.push(new Circle({radius:10, x: 10, y: 50, dx: 2, dy: 0, number: "2"}))
function animate3() {
requestAnimationFrame(animate3);
ctx.clearRect(0, 0, canvas.width, canvas.height);
circles.forEach(item => item.draw());
}
animate3();
<canvas id="canvas"></canvas>
Code should be easy to follow let me know if you have any questions

How do I place an image over top of canvas ball in javascript/html

I am trying to put an image inplace of where my current canvas ball is. I have a bouncing ball that uses real physics when bouncing. I want to put an image of a ball instead of using the 2d canvas ball. I've tried placing an image in but it just bricks my program, would I be ableto add the image overtop of the canvas ball and be able to keep all the same physics I have applied to the canvas ballHow could I achieve this?
<script src="script.js"></script>
<canvas id="canvas" height="1080" width="1920">3D Ball</canvas>
<script>
var width = 1410;
var height = 800;
var canvas = ctx = false;
var frameRate = 1/40;
var frameDelay = frameRate * 1000;
var loopTimer = false;
var ball = {
position: {x: width/2, y: 0},
velocity: {x: 10, y: 0},
mass: 0.1,
radius: 15,
restitution: -0.7
};
var Cd = 0.47;
var snd = new Audio("bounce.mp3");
var rho = 1.22;
var A = Math.PI * ball.radius * ball.radius / (10000);
var ag = 9.81;
var mouse = {x: 0, y: 0, isDown: false};
var msg = "physics ball";
function getMousePosition(e) {
mouse.x = e.pageX - canvas.offsetLeft;
mouse.y = e.pageY - canvas.offsetTop;
}
var mouseDown = function(e) {
if (e.which == 1) {
getMousePosition(e);
mouse.isDown = true;
ball.position.x = mouse.x;
ball.position.y = mouse.y;
}
}
var mouseUp = function(e) {
if (e.which == 1) {
mouse.isDown = false;
ball.velocity.y = (ball.position.y - mouse.y) /10;
ball.velocity.x = (ball.position.x - mouse.x) / 10;
}
}
var setup = function() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
canvas.onmousemove = getMousePosition;
canvas.onmousedown = mouseDown;
canvas.onmouseup = mouseUp;
ctx.fillStyle = 'red';
ctx.strokeStyle = '#000000';
loopTimer = setInterval(loop, frameDelay);
}
var loop = function() {
if ( ! mouse.isDown) {
var Fx = -0.5 * Cd * A * rho * ball.velocity.x * ball.velocity.x * ball.velocity.x / Math.abs(ball.velocity.x);
var Fy = -0.5 * Cd * A * rho * ball.velocity.y * ball.velocity.y * ball.velocity.y / Math.abs(ball.velocity.y);
Fx = (isNaN(Fx) ? 0 : Fx);
Fy = (isNaN(Fy) ? 0 : Fy);
var ax = Fx / ball.mass;
var ay = ag + (Fy / ball.mass);
ball.velocity.x += ax*frameRate;
ball.velocity.y += ay*frameRate;
ball.position.x += ball.velocity.x*frameRate*100;
ball.position.y += ball.velocity.y*frameRate*100;
}
if (ball.position.y > height - ball.radius){
snd.play();
}
if (ball.velocity.x == 0 && ball.velocity.y == 0) {
snd.pause();
}
if (ball.position.y > height - ball.radius) {
ball.velocity.y *= ball.restitution;
ball.position.y = height - ball.radius;
}
if (ball.position.x > width - ball.radius) {
ball.velocity.x *= ball.restitution;
ball.position.x = width - ball.radius;
}
if (ball.position.x < ball.radius) {
ball.velocity.x *= ball.restitution;
ball.position.x = ball.radius;
}
ctx.clearRect(0,0,width,height);
ctx.save();
ctx.translate(ball.position.x, ball.position.y);
ctx.beginPath();
ctx.arc(0, 0, ball.radius, 0, Math.PI*2, true);
ctx.fill();
ctx.closePath();
ctx.restore();
ctx.fillStyle = "blue";
ctx.font = "20px Arial";
ctx.fillText(msg, 1300,20);
if (mouse.isDown) {
ctx.beginPath();
ctx.moveTo(ball.position.x, ball.position.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
ctx.closePath();
}
}
setup();
</script>
instead of:
ctx.translate(ball.position.x, ball.position.y);
ctx.beginPath();
ctx.arc(0, 0, ball.radius, 0, Math.PI*2, true);
ctx.fill();
ctx.closePath();
try something like:
ctx.drawImage(document.getElementById("ball_img", ball.position.x, ball.position.y)
And in your html you should have, something like:
<img id="ball_img" src="your_ball_img.png">
You could presumably scale this img using css to fit the radius.
Haven't tested it though.

How can we stop this HTML5 Canvas wheel at exact points after spin?

In the Below code link HTML5 canvas spin wheel game. I want to stop this canvas at a user-defined position as if the user wants to stop always at 200 texts or 100 texts like that.
Currently, it is stopping at random points I want to control where to stop as in if I want to stop circle at 100 or 200 or 0 whenever I want.
How can we achieve that??? Can anyone Help!!!!!
Attached Codepen link also.
Html file
<div>
<canvas class="spin-wheel" id="canvas" width="300" height="300"></canvas>
</div>
JS file
var color = ['#ca7','#7ac','#77c','#aac','#a7c','#ac7', "#caa"];
var label = ['10', '200','50','100','5','500',"0"];
var slices = color.length;
var sliceDeg = 360/slices;
var deg = 270;
var speed = 5;
var slowDownRand = 0;
var ctx = canvas.getContext('2d');
var width = canvas.width; // size
var center = width/2; // center
var isStopped = false;
var lock = false;
function rand(min, max) {
return Math.random() * (max - min) + min;
}
function deg2rad(deg){ return deg * Math.PI/180; }
function drawSlice(deg, color){
ctx.beginPath();
ctx.fillStyle = color;
ctx.moveTo(center, center);
ctx.arc(center, center, width/2, deg2rad(deg), deg2rad(deg+sliceDeg));
console.log(center, center, width/2, deg2rad(deg), deg2rad(deg+sliceDeg))
ctx.lineTo(center, center);
ctx.fill();
}
function drawText(deg, text) {
ctx.save();
ctx.translate(center, center);
ctx.rotate(deg2rad(deg));
ctx.textAlign = "right";
ctx.fillStyle = "#fff";
ctx.font = 'bold 30px sans-serif';
ctx.fillText(text, 130, 10);
ctx.restore();
}
function drawImg() {
ctx.clearRect(0, 0, width, width);
for(var i=0; i<slices; i++){
drawSlice(deg, color[i]);
drawText(deg+sliceDeg/2, label[i]);
deg += sliceDeg;
}
}
// ctx.rotate(360);
function anim() {
isStopped = true;
deg += speed;
deg %= 360;
// Increment speed
if(!isStopped && speed<3){
speed = speed+1 * 0.1;
}
// Decrement Speed
if(isStopped){
if(!lock){
lock = true;
slowDownRand = rand(0.994, 0.998);
}
speed = speed>0.2 ? speed*=slowDownRand : 0;
}
// Stopped!
if(lock && !speed){
var ai = Math.floor(((360 - deg - 90) % 360) / sliceDeg); // deg 2 Array Index
console.log(slices)
ai = (slices+ai)%slices; // Fix negative index
return alert("You got:\n"+ label[ai] ); // Get Array Item from end Degree
// ctx.arc(150,150,150,8.302780584487312,9.200378485512967);
// ctx.fill();
}
drawImg();
window.requestAnimationFrame(anim);
}
function start() {
anim()
}
drawImg();
Spin wheel codepen
Ease curves
If you where to plot the wheel position over time as it slows to a stop you would see a curve, a curve that looks like half a parabola.
You can get the very same curve if you plot the value of x squared in the range 0 to 1 as in the next snippet, the red line shows the plot of f(x) => x * x where 0 <= x <= 1
Unfortunately the plot is the wrong way round and needs to be mirrored in x and y. That is simple by changing the function to f(x) => 1 - (1 - x) ** 2 (Click the canvas to get the yellow line)
const size = 200;
const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size / 2}).getContext("2d");
document.body.appendChild(ctx.canvas);
ctx.canvas.style.border = "2px solid black";
plot(getData());
plot(unitCurve(x => x * x), "#F00");
ctx.canvas.addEventListener("click",()=>plot(unitCurve(x => 1 - (1 - x) ** 2), "#FF0"), {once: true});
function getData(chart = []) {
var pos = 0, speed = 9, deceleration = 0.1;
while(speed > 0) {
chart.push(pos);
pos += speed;
speed -= deceleration;
}
return chart;
}
function unitCurve(f,chart = []) {
const step = 1 / 100;
var x = 0;
while(x <= 1) {
chart.push(f(x));
x += step
}
return chart;
}
function plot(chart, col = "#000") {
const xScale = size / chart.length, yScale = size / 2 / Math.max(...chart);
ctx.setTransform(xScale, 0, 0, yScale, 0, 0);
ctx.strokeStyle = col;
ctx.beginPath();
chart.forEach((y,x) => ctx.lineTo(x,y));
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.stroke();
}
In animation this curve is an ease in.
We can create function that uses the ease function, takes the time and returns the position of the wheel. We can provide some additional values that controls how long the wheel will take to stop, the starting position and the all important stop position.
function wheelPos(currentTime, startTime, endTime, startPos, endPos) {
// first scale the current time to a value from 0 to 1
const x = (currentTime - startTime) / (endTime - startTime);
// rather than the square, we will use the square root (this flips the curve)
const xx = x ** (1 / 2);
// convert the value to a wheel position
return xx * (endPos - startPos) + startPos;
}
Demo
The demo puts it in action. Rather than using the square root the function in the demo defines the root as the constant slowDownRate = 2.6. The smaller this value the greater start speed and the slower the end speed. A value of 1 means it will move at a constant speed and then stop. The value must be > 0 and < 1
requestAnimationFrame(mainLoop);
Math.TAU = Math.PI * 2;
const size = 160;
const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size}).getContext("2d");
document.body.appendChild(ctx.canvas);
const stopAt = document.createElement("div")
document.body.appendChild(stopAt);
ctx.canvas.style.border = "2px solid black";
var gTime; // global time
const colors = ["#F00","#F80","#FF0","#0C0","#08F","#00F","#F0F"];
const wheelSteps = 12;
const minSpins = 3 * Math.TAU; // min number of spins before stopping
const spinTime = 6000; // in ms
const slowDownRate = 1 / 1.8; // smaller this value the greater the ease in.
// Must be > 0
var startSpin = false;
var readyTime = 0;
ctx.canvas.addEventListener("click",() => { startSpin = !wheel.spinning });
stopAt.textContent = "Click wheel to spin";
const wheel = { // hold wheel related variables
img: createWheel(wheelSteps),
endTime: performance.now() - 2000,
startPos: 0,
endPos: 0,
speed: 0,
pos: 0,
spinning: false,
set currentPos(val) {
this.speed = (val - this.pos) / 2; // for the wobble at stop
this.pos = val;
},
set endAt(pos) {
this.endPos = (Math.TAU - (pos / wheelSteps) * Math.TAU) + minSpins;
this.endTime = gTime + spinTime;
this.startTime = gTime;
stopAt.textContent = "Spin to: "+(pos + 1);
}
};
function wheelPos(currentTime, startTime, endTime, startPos, endPos) {
const x = ((currentTime - startTime) / (endTime - startTime)) ** slowDownRate;
return x * (endPos - startPos) + startPos;
}
function mainLoop(time) {
gTime = time;
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, size, size);
if (startSpin && !wheel.spinning) {
startSpin = false;
wheel.spinning = true;
wheel.startPos = (wheel.pos % Math.TAU + Math.TAU) % Math.TAU;
wheel.endAt = Math.random() * wheelSteps | 0;
} else if (gTime <= wheel.endTime) { // wheel is spinning get pos
wheel.currentPos = wheelPos(gTime, wheel.startTime, wheel.endTime, wheel.startPos, wheel.endPos);
readyTime = gTime + 1500;
} else { // wobble at stop
wheel.speed += (wheel.endPos - wheel.pos) * 0.0125;
wheel.speed *= 0.95;
wheel.pos += wheel.speed;
if (wheel.spinning && gTime > readyTime) {
wheel.spinning = false;
stopAt.textContent = "Click wheel to spin";
}
}
// draw wheel
ctx.setTransform(1,0,0,1,size / 2, size / 2);
ctx.rotate(wheel.pos);
ctx.drawImage(wheel.img, -size / 2 , - size / 2);
// draw marker shadow
ctx.setTransform(1,0,0,1,1,4);
ctx.fillStyle = "#0004";
ctx.beginPath();
ctx.lineTo(size - 13, size / 2);
ctx.lineTo(size, size / 2 - 7);
ctx.lineTo(size, size / 2 + 7);
ctx.fill();
// draw marker
ctx.setTransform(1,0,0,1,0,0);
ctx.fillStyle = "#F00";
ctx.beginPath();
ctx.lineTo(size - 13, size / 2);
ctx.lineTo(size, size / 2 - 7);
ctx.lineTo(size, size / 2 + 7);
ctx.fill();
requestAnimationFrame(mainLoop);
}
function createWheel(steps) {
const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size}).getContext("2d");
const s = size, s2 = s / 2, r = s2 - 4;
var colIdx = 0;
for (let a = 0; a < Math.TAU; a += Math.TAU / steps) {
const aa = a - Math.PI / steps;
ctx.fillStyle = colors[colIdx++ % colors.length];
ctx.beginPath();
ctx.moveTo(s2, s2);
ctx.arc(s2, s2, r, aa, aa + Math.TAU / steps);
ctx.fill();
}
ctx.fillStyle = "#FFF";
ctx.beginPath();
ctx.arc(s2, s2, 12, 0, Math.TAU);
ctx.fill();
ctx.beginPath();
ctx.lineWidth = 2;
ctx.arc(s2, s2, r, 0, Math.TAU);
ctx.moveTo(s2 + 12, s2);
ctx.arc(s2, s2, 12, 0, Math.TAU);
for (let a = 0; a < Math.TAU; a += Math.TAU / steps) {
const aa = a - Math.PI / steps;
ctx.moveTo(Math.cos(aa) * 12 + s2, Math.sin(aa) * 12 + s2);
ctx.lineTo(Math.cos(aa) * r + s2, Math.sin(aa) * r + s2);
}
//ctx.fill("evenodd");
ctx.stroke();
ctx.fillStyle = "#000";
ctx.font = "13px arial black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
const tr = r - 8;
var idx = 1;
for (let a = 0; a < Math.TAU; a += Math.TAU / steps) {
const dx = Math.cos(a);
const dy = Math.sin(a);
ctx.setTransform(dy, -dx, dx, dy, dx * (tr - 4) + s2, dy * (tr - 4) + s2);
ctx.fillText(""+ (idx ++), 0, 0);
}
return ctx.canvas;
}
body { font-family: arial }

How to remove straight lines from multiple rotated arc on canvas?

I want to create Star Trail on canvas.
The idea to create it, i have to draw multiple images that increment size, and some calculation for the next image position. Store multiple image in array. Choose the image randomly each draw and draw it with different angle each image.
I made it.
var canvas=document.getElementById('canvas'),ctx=canvas.getContext('2d');
var starsimage=['https://1.bp.blogspot.com/-prwJzDDwQRU/XTjNfQhQnDI/AAAAAAAACBo/wpbhqkfc-9wQQeg95O6poFbFyu77q4vdACLcBGAs/s1600/CircleWhite100.png','https://1.bp.blogspot.com/-lm4F2UOMCdE/XTjNghXjrOI/AAAAAAAACB0/L6pya6HQk0cU5R5RP9Wo_-Bm_UhO_qCawCLcBGAs/s1600/CircleWhite75.png','https://1.bp.blogspot.com/-7ennrlohEo0/XTjNfoji8KI/AAAAAAAACBw/G0SQhFEZ0IMMf2z3g_Mvbon97BMktSw-QCLcBGAs/s1600/CircleWhite50.png','https://1.bp.blogspot.com/-I7aeA-F4OWY/XTjNfiO-rxI/AAAAAAAACBs/lKYC-SmaWSQWc0PoPVdgCHeyDUPdoJd7gCLcBGAs/s1600/CircleWhite25.png','https://1.bp.blogspot.com/-V2Ak6YU2XNA/XTjNh7iIwZI/AAAAAAAACCA/tNo5Ho6iC4gndoftPJfSCInGqgyfcd6nQCLcBGAs/s1600/TrailBlue100.png','https://1.bp.blogspot.com/-ylpi3AZvces/XTjNi7uy1kI/AAAAAAAACCM/uWZ7_zYRXXQN4q3QRSngCFeT5RoEeG4xgCLcBGAs/s1600/TrailBlue75.png','https://1.bp.blogspot.com/-NoPiS9k0o0U/XTjNivmKVjI/AAAAAAAACCI/gZzDMn9zomMWrQc2hhKfNB9JK0ruh2wyQCLcBGAs/s1600/TrailBlue50.png','https://1.bp.blogspot.com/-KYkWDwtmS7A/XTjNiSE8Y_I/AAAAAAAACCE/jDyTIbJqBBs3FP1tyGFICjShyx_GCCy0gCLcBGAs/s1600/TrailBlue25.png'];
var stars=new Array();var starsloaded=0;
var xpos=0,ypos=0,w=25,h=25,ix=0,xa=25,ya=25,rot=0,ang=0;
for(var i=0;i<starsimage.length;i++){//load images
stars[i]=new Image();stars[i].src=starsimage[i];
stars[i].onload=function(){starsloaded++;if(starsloaded==starsimage.length){
draw();
}}
}
function minmaxdraw(min,max){return Math.floor(Math.random() * (max - min + 1)) + min}
function randomindeximage(l){return Math.floor(Math.random()*l)}
function draw(){
xpos=canvas.width/2;ypos=canvas.height/2;
for(var i=0;i<500;i++){//500 images
xy=minmaxdraw(2,4);//gap randomly
ix=randomindeximage(starsimage.length);
w+=xy;h+=xy;ang=minmaxdraw(0,359);
if(i>0){xa=w-xa;ya=h-ya;xpos=xpos-xa/2;ypos=ypos-ya/2;
ctx.save();
ctx.translate(xpos+w/2,ypos+h/2);
rot=ang*Math.PI/180;ctx.rotate(rot);
ctx.drawImage(stars[ix],-w/2,-h/2,w,h);
ctx.restore();
xa=w;ya=h;
}
else{ctx.drawImage(stars[ix],xpos,ypos,w,h)}
}
}
body{margin:0;padding:0;position:relative;background:#101010;height:580px;width:100%}
#canvas{z-index:2;background-color:transparent;position:absolute;width:100vw;left:50%;top:50%;transform:translate(-50%,-50%)}
body:before{content:'';z-index:1;position:absolute;width:100%;height:100%;left:0;top:0;background:linear-gradient(15deg, #1458ac, #000);}
<canvas id="canvas" width="640" height="580"></canvas>
But. the quality is bad. Because the image size is static, blurry if the increment width is larger than actually image size, and the small stars is barely visible
I think, it's better to create it with multiple arc.
The idea is same, i have to draw multiple arc. But, i'm dummy, i made with two canvas, different angle each canvas. (It's actually same with my work before, but at the time, i use 1 image that have cutted circle). Because if i made it only on one canvas, the straight line appears. So, i draw multiple arc in each canvas. It works, and no straight line appears. (It's still basic arc)
var canvas1=document.getElementById('canvas1'),ctx=canvas1.getContext('2d'),canvas2=document.getElementById('canvas2'),ctx2=canvas2.getContext('2d');var xy=0;var angle=new Array();
function minmaxdraw(min,max){return Math.floor(Math.random() * (max - min + 1)) + min}
function randomindeximage(l){return Math.floor(Math.random()*l)}
function draw1(){
var xpos=canvas1.width/2;var ypos=canvas1.height/2;var r=15,ang=0;
ctx.beginPath();
for(var i=0;i<55;i++){
r+=minmaxdraw(4,9);
ctx.moveTo(xpos + r, ypos);
ctx.arc(xpos, ypos, r, 0, 1.5*Math.PI,true);
}ctx.stroke();
}
function draw2(){
var xpos=canvas2.width/2;var ypos=canvas2.height/2;var r=15;
ctx2.beginPath();
for(var t=0;t<55;t++){
r+=minmaxdraw(4,9);
ctx2.moveTo(xpos, ypos + r);
ctx2.arc(xpos, ypos, r, Math.PI / 2, Math.PI);
}ctx2.stroke();
}
draw1();draw2();
body{margin:0;padding:0;position:relative;height:480px;width:100%}
.canvas{background-color:transparent;position:absolute;width:100vw;left:50%;top:50%;margin:-50% 0 0 -50%}
<canvas id='canvas1' class='canvas' width='640' height='480'></canvas>
<canvas id='canvas2' class='canvas' width='640' height='480'></canvas>
It's not rotated yet.
So, if i want make circle trail, i have to rotate each arc randomly like i did before with image, but the problem is here. The straight lines come wickedly.
var canvas1=document.getElementById('canvas1'),ctx=canvas1.getContext('2d'),canvas2=document.getElementById('canvas2'),ctx2=canvas2.getContext('2d');var xy=0;var angle=new Array();
function minmaxdraw(min,max){return Math.floor(Math.random() * (max - min + 1)) + min}
function randomindeximage(l){return Math.floor(Math.random()*l)}
function draw1(){
var xpos=canvas1.width/2;var ypos=canvas1.height/2;
var r=15,ang=0,dx=0,dy=0;
ctx.beginPath();
for(var i=0;i<55;i++){//200 images
ang=minmaxdraw(0,359);
dx=minmaxdraw(0,canvas1.width),dy=minmaxdraw(0,canvas1.height);
r+=minmaxdraw(3,7);
ang=Math.atan2(dx-xpos,dy-ypos);angle[i]=ang;
ctx.moveTo(xpos, ypos);
ctx.arc(xpos, ypos, r, ang + 0 * Math.PI, ang + Math.PI * 1.5,true);
}ctx.stroke();
}
function draw2(){
var xpos=canvas2.width/2;var ypos=canvas2.height/2;var r=15;
ctx2.beginPath();
for(var t=0;t<55;t++){
r+=minmaxdraw(3,7);
ctx2.moveTo(xpos, ypos + r);
ctx2.arc(xpos, ypos, r, angle[t] + Math.PI / 2, angle[t] + Math.PI);
}ctx2.stroke();
}
draw1();draw2();
body{margin:0;padding:0;position:relative;height:480px;width:100%}
.canvas{background-color:transparent;position:absolute;width:100vw;left:50%;top:50%;margin:-50% 0 0 -50%}
<canvas id='canvas1' class='canvas' width='640' height='480'></canvas>
<canvas id='canvas2' class='canvas' width='640' height='480'></canvas>
Is there any solution to remove that straight lines?
Not sure why you have the calls to moveTo and maybe I just didn't understand the question but I just moved beginPath and stroke inside the loops.
var canvas1 = document.getElementById('canvas1'),
ctx = canvas1.getContext('2d'),
canvas2 = document.getElementById('canvas2'),
ctx2 = canvas2.getContext('2d');
var xy = 0;
var angle = new Array();
function minmaxdraw(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
function randomindeximage(l) {
return Math.floor(Math.random() * l)
}
function draw1() {
var xpos = canvas1.width / 2;
var ypos = canvas1.height / 2;
var r = 15,
ang = 0,
dx = 0,
dy = 0;
for (var i = 0; i < 55; i++) { //200 images
ang = minmaxdraw(0, 359);
dx = minmaxdraw(0, canvas1.width), dy = minmaxdraw(0, canvas1.height);
r += minmaxdraw(3, 7);
ang = Math.atan2(dx - xpos, dy - ypos);
angle[i] = ang;
ctx.beginPath();
ctx.arc(xpos, ypos, r, ang + 0 * Math.PI, ang + Math.PI * 1.5, true);
ctx.stroke();
}
}
function draw2() {
var xpos = canvas2.width / 2;
var ypos = canvas2.height / 2;
var r = 15;
for (var t = 0; t < 55; t++) {
r += minmaxdraw(3, 7);
ctx2.beginPath();
ctx2.arc(xpos, ypos, r, angle[t] + Math.PI / 2, angle[t] + Math.PI);
ctx2.stroke();
}
}
draw1();
draw2();
body {
margin: 0;
padding: 0;
position: relative;
height: 550px;
width: 100%
}
.canvas {
background-color: transparent;
position: absolute;
width: 100vw;
left: 50%;
top: 50%;
margin: -50% 0 0 -50%
}
<canvas id='canvas1' class='canvas' width='640' height='480'></canvas>
<canvas id='canvas2' class='canvas' width='640' height='480'></canvas>
Also I don't understand why you have 2 canvases. It works fine with just one
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
var xy = 0;
var angle = new Array();
function minmaxdraw(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
function randomindeximage(l) {
return Math.floor(Math.random() * l)
}
function draw1() {
var xpos = canvas.width / 2;
var ypos = canvas.height / 2;
var r = 15,
ang = 0,
dx = 0,
dy = 0;
for (var i = 0; i < 55; i++) { //200 images
ang = minmaxdraw(0, 359);
dx = minmaxdraw(0, canvas.width), dy = minmaxdraw(0, canvas.height);
r += minmaxdraw(3, 7);
ang = Math.atan2(dx - xpos, dy - ypos);
angle[i] = ang;
ctx.beginPath();
ctx.arc(xpos, ypos, r, ang + 0 * Math.PI, ang + Math.PI * 1.5, true);
ctx.stroke();
}
}
function draw2() {
var xpos = canvas.width / 2;
var ypos = canvas.height / 2;
var r = 15;
for (var t = 0; t < 55; t++) {
r += minmaxdraw(3, 7);
ctx.beginPath();
ctx.arc(xpos, ypos, r, angle[t] + Math.PI / 2, angle[t] + Math.PI);
ctx.stroke();
}
}
draw1();
draw2();
<canvas id='canvas' class='canvas' width='640' height='480'></canvas>
The reason you got lines before is all arc does is add points.
arc is effectively this
// pseudo code
function arc(x, y, radius, start, end) {
const numSegments = 100; // no idea what number real arc uses here
for (let i = 0; i < numSegments; ++i) {
const angle = start + (end - start) * i / numSegments;
ctx.lineTo(Math.cos(angle) * radius, Math.sin(angle) * radius);
}
}
As you can see the code above just adds the points around the arc. So if you do a
ctx.moveTo(x, y);
ctx.arc(x, y, ...);
you're adding a point in the center of the arc and then more points to the edge. That's why you're getting a line from the centers to the edge of each arc.
If you wanted to leave the code the same as you had it and just stroke all the arcs at once then you'd need to change the moveTo to move to the edge of the arc instead of the center.
ctx.moveTo(x + Math.cos(start) * radius, y + Math.sin(start) * radius);
ctx.arc(x, y, radius, start, ...);

javascript - Get canvas stroke points

How can I get the black points from a canvas stroke?
Here - https://jsfiddle.net/dsu3Lmfm/2/ - all points from the canvas are black.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(20, 100);
ctx.lineTo(70, 100);
ctx.closePath();
ctx.stroke();
var imageData = ctx.getImageData(0, 0, c.width, c.height);
var pix = imageData.data;
// Loop over each pixel and get the black pixels
for (var i = 0, n = pix.length; i < n; i += 4) {
if (pix[i] === 0 && pix[i+1] === 0 && pix[i+2] === 0) {
var x = (i / 4) % c.width;
var y = (i / 4) / c.width;
console.log("x=" + parseInt(x, 10) + ", y=" + parseInt(y, 10));
}
}

Categories

Resources