circle progressbar doesn't start from the middle - javascript

I have a circular progress bar that must start from 0 degree - middle of a top part of the circle. But I've noticed that it starts not in the middle, but a little to the left. How do I fix that?
Here's the code:
var bar = document.getElementById('progressbar'),
createProgressBar = function(bar) {
var options = {
start: 0,
width: bar.getAttribute('data-width'),
height: bar.getAttribute('data-height'),
percent: 90,
lineWidth: bar.getAttribute('data-linewidth')
},
canvas = document.createElement('canvas'),
paper = canvas.getContext('2d'),
span = document.createElement('span'),
radius = (options.width - options.lineWidth) / 2,
color = paper.createLinearGradient(0, 0, options.width, 0),
step = 1.5;
span.style.width = bar.style.width = options.width + 'px';
span.style.height = bar.style.height = options.height + 'px';
canvas.width = options.width;
canvas.height = options.height;
span.style.lineHeight = options.height + 'px';
color.addColorStop(0, "red");
color.addColorStop(0.5, "red");
color.addColorStop(1.0, "red");
bar.appendChild(span);
bar.appendChild(canvas);
(function animat() {
span.textContent = options.start + '%';
createCircle(options, paper, radius, color, Math.PI * 1.5, Math.PI * step);
console.log(step);
options.start++;
step += 0.02;
if (options.start <= options.percent) {
setTimeout(animat, 10);
}
})();
},
createCircle = function(options, paper, radius, color, start, end) {
paper.clearRect(
options.width / 2 - radius - options.lineWidth,
options.height / 2 - radius - options.lineWidth,
radius * 2 + (options.lineWidth * 2),
radius * 2 + (options.lineWidth * 2)
);
paper.beginPath();
paper.arc(options.width / 2, options.height / 2, radius, 0, Math.PI * 2, false);
paper.strokeStyle = '#dbd7d2';
paper.lineCap = 'round';
paper.lineWidth = options.lineWidth;
paper.stroke();
paper.closePath();
paper.beginPath();
paper.arc(options.width / 2, options.height / 2, radius, start, end, false);
paper.strokeStyle = color;
paper.lineCap = 'square';
paper.lineWidth = options.lineWidth;
paper.stroke();
paper.closePath();
};
createProgressBar(bar);
#progressbar {
position: fixed;
top: 50%;
left: 50%;
margin-top: -150px;
margin-left: -150px;
}
canvas {
position: absolute;
display: block;
left: 0;
top: 0
}
span {
display: block;
color: #222;
text-align: center;
font-family: "sans serif", tahoma, Verdana, helvetica;
font-size: 30px
}
<div id="progressbar" data-width="320" data-height="320" data-linewidth="16"></div>
That's what the progress bar looks like: https://jsfiddle.net/ue20b8bd/
P.S. I've modified the code from github: https://github.com/su-ning/html5-circle-progressbar

The problem was with the lineCap property. Using 'butt' as a lineCap fixed the problem!

Related

How to Draw on rotated canvas using mouse?

I want to draw on img its work fine but when i rotate the image its drawing axis is total change and it not draw on the right area
https://codepen.io/ali-shahzil/project/editor/AddPLW
var canvas;
var ctx;
var SCALE_MIN = 1,
SCALE_MAX = 25;
var currScale = 0; // def pic width=600px, 100px=1scale unit
var xscale = 1.0;
var scaleFactor = 1.00;
var painting = false,
mark = true,
lastX = 0,
lastY = 0,
lineThickness = 0.3,
width = 600,
height = 600;
var img = new Image();
img.src = 'img.JPG';
img.onload = function() {
canvas = document.getElementById("canvas1"),
ctx = canvas.getContext("2d");
canvas.height = height;
canvas.width = width;
ctx.drawImage(img, 5, 40, canvas.width, canvas.height);
canvas = document.getElementById("canvas2"),
ctx = canvas.getContext("2d");
canvas.height = height;
canvas.width = width;
ctx.drawImage(img, 5, 40, canvas.width, canvas.height);
canvas = ctx = ''; //reset
}
function doMarking() {
var checkBox = document.getElementById("mark");
if (checkBox.checked == true) {
mark = true;
if (canvas != null)
canvas.style.cursor = "pointer";
//currImgId = '';
} else {
mark = false;
if (canvas != null);
canvas.style.cursor = "";
}
lastX = 0,
lastY = 0,
painting = false;
}
function mouseDown(e) {
if (!mark)
return;
painting = true;
ctx.fillStyle = "#ffffff";
lastX = e.pageX - (e.target).offsetLeft;
lastY = e.pageY - (e.target).offsetTop;
//Calculating the scale how much it increase
var rect = canvas.getBoundingClientRect(); // abs. size of element
scaleX = canvas.width / rect.width; // relationship bitmap vs. element for X
scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y
lastX = lastX * scaleX;
lastY = lastY * scaleY;
//console.log('Before lasX=' + lastX + ' lastY=' + lastY+',currScale='+currScale);
//lastX=transformSimple(lastX);
// lastY=transformSimple(lastY);
//console.log('After lasX=' + lastX + ' lastY=' + lastY+', currScale='+currScale);
//console.log('offleft=' + (e.target).offsetLeft + ', offsetTop=' + (e.target).offsetTop);
// console.log('e=' + e);
}
/*
canvas1.onmousedown=function (e) {
console.log('mousedown2 id=' + e);
if (!mark)
return;
painting = true;
ctx.fillStyle = "#ffffff";
lastX = e.pageX - this.offsetLeft;
lastY = e.pageY - this.offsetTop;
console.log('lasX=' + lastX + ' lastY=' + lastY);
}
*/
function mouseUp(e) {
if (!mark)
return;
painting = false;
}
function mouseMove(e) {
if (!mark)
return;
if (painting) {
mouseX = e.pageX - (e.target).offsetLeft;
mouseY = e.pageY - (e.target).offsetTop;
//Calculating the scale how much it increase
var rect = canvas.getBoundingClientRect(); // abs. size of element
scaleX = canvas.width / rect.width; // relationship bitmap vs. element for X
scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y
mouseX = mouseX * scaleX;
mouseY = mouseY * scaleY;
// mouseX=transformSimple(mouseX);
// mouseY=transformSimple(mouseY);
//console.log('mx=' + mouseX + ', my=' + mouseY);
// find all points between
var x1 = mouseX,
x2 = lastX,
y1 = mouseY,
y2 = lastY;
var steep = (Math.abs(y2 - y1) > Math.abs(x2 - x1));
if (steep) {
var x = x1;
x1 = y1;
y1 = x;
var y = y2;
y2 = x2;
x2 = y;
}
if (x1 > x2) {
var x = x1;
x1 = x2;
x2 = x;
var y = y1;
y1 = y2;
y2 = y;
}
var dx = x2 - x1,
dy = Math.abs(y2 - y1),
error = 0,
de = dy / dx,
yStep = -1,
y = y1;
if (y1 < y2) {
yStep = 1;
}
lineThickness = 5 - Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) / 10;
if (lineThickness < 1) {
lineThickness = 1;
}
for (var x = x1; x < x2; x++) {
if (steep) {
// translate(y,x);
ctx.fillRect(y, x, lineThickness, lineThickness);
//ctx.fillRect(transformSimple(y), transformSimple(x), lineThickness, lineThickness);
} else {
ctx.fillRect(x, y, lineThickness, lineThickness);
}
error += de;
if (error >= 0.5) {
y += yStep;
error -= 1.0;
}
// console.log('x=' + x + ', y=' + y );
}
lastX = mouseX;
lastY = mouseY;
}
}
/*
canvas.addEventListener('click', function (event) {
if (mark)
return;
//get the img of convas
});*/
//-------------- img zooms
function zoomin_canvas() {
if (canvas != null) {
var currWidth = canvas.clientWidth;
//console.log('zoomin currWidth='+currWidth);
if (currWidth >= 1500) return false;
else {
canvas.style.width = (currWidth + 100) + "px";
//if (currScale < SCALE_MAX)
// currScale++;
//console.log('scale=' + currScale);
}
}
}
function zoomout_canvas() {
if (canvas != null) {
var currWidth = canvas.clientWidth;
if (currWidth <= 100) return false;
else {
canvas.style.width = (currWidth - 100) + "px";
//if (currScale > SCALE_MIN)
// currScale--;
//console.log('scale=' + currScale);
}
}
}
var currImgId = null;
function selectImg(e) {
if (currImgId != null) {
document.getElementById(currImgId).style.border = "none";
}
e.target.style.border = "2px solid orange";
currImgId = (e.target).getAttribute('id');
if (typeof canvas !== 'undefined') {
// the variable is defined
canvas = e.target;
ctx = canvas.getContext("2d");
}
//ctx.drawImage(img, 5, 40, canvas.width, canvas.height);
}
function rotate() {
if (currImgId != null) {
document.getElementById(currImgId).setAttribute("class", "rotated-image");
}
}
var degrees = 0;
function rotateRight() {
console.log('currimgid=' + currImgId);
var img = document.getElementById(currImgId);
degrees = parseInt(img.getAttribute("rotate"));
degrees = (degrees + 90) % 360;
img.style.setProperty('-ms-transform', 'rotate(' + degrees + 'deg)');
img.style.setProperty('-webkit-transform', 'rotate(' + degrees + 'deg)');
img.style.setProperty('transform', 'rotate(' + degrees + 'deg)');
img.setAttribute("rotate", degrees);
}
function rotateLeft() {
var img = document.getElementById(currImgId);
degrees = parseInt(img.getAttribute("rotate"));
degrees = (degrees - 90) % 360;
img.style.setProperty('-ms-transform', 'rotate(' + degrees + 'deg)');
img.style.setProperty('-webkit-transform', 'rotate(' + degrees + 'deg)');
img.style.setProperty('transform', 'rotate(' + degrees + 'deg)');
img.setAttribute("rotate", degrees);
}
function translate(X, Y) {
console.log('untransformed x=' + X + ', y=' + Y);
// const point = {x: 0, y: 0};
const matrix = ctx.getTransform();
const transformedPoint = {
x: matrix.a * X + matrix.c * Y + matrix.e,
y: matrix.b * X + matrix.d * Y + matrix.f,
};
console.log('transformed x=' + transformedPoint.x + ', y=' + transformedPoint.y);
}
function translateSimple(X, Y) {
//console.log('scalefactor='+scaleFactor);
console.log('untransformed x=' + X + ', y=' + Y);
if (scaleFactor >= 1.0)
console.log('transformed x=' + X / scaleFactor + ', y=' + Y / scaleFactor);
else
console.log('transformed x=' + X * scaleFactor + ', y=' + Y * scaleFactor);
}
function transformSimple(a) {
//return (parseInt(a/(scaleFactor*scaleFactor)));
if (currScale == 0)
return (a);
else
return (a - 16 * (currScale));
}
function draw() {
for (var x = 100; x < 102; x++)
ctx.fillRect(100, x, 4.9, 4.9);
}
.main_bottom {
background-color: #e8e9eb;
display: flex;
align-items: center;
justify-content: space-around;
border: 10px solid #e8e9eb;
border-top: 30px solid #e8e9eb;
height: 90vh;
}
form {
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
margin-bottom: 5px;
}
.scrollit {
/*overflow-y: auto;*/
/* overflow-y: scroll;*/
height: 300px;
overflow-x: hidden;
overflow-y: auto;
}
.first {
display: flex;
flex-direction: row;
visibility: hidden;
}
.submit {
display: flex;
flex-direction: row;
}
img {
width: 100%;
max-width: 800px;
height: auto;
display: block;
margin-left: auto;
margin-right: auto;
}
.fix {
height: 300px;
margin-top: 200px;
}
body,
html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#container {
width: 100%;
height: 100%;
}
#left_panel {
display: flex;
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 700px;
background-color: white;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
flex-direction: column;
overflow: scroll;
}
#right_panel {
display: flex;
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 700px;
margin-right: 15px;
background-color: white;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
flex-direction: column;
overflow: scroll;
}
#drag {
position: absolute;
left: -4px;
top: 0;
bottom: 0;
width: 8px;
cursor: w-resize;
}
/*img zooms*/
#navbar {
overflow: hidden;
background-color: #099;
position: fixed;
top: 0;
width: 100%;
padding-top: 3px;
padding-bottom: 3px;
padding-left: 20px;
}
#navbar a {
float: left;
display: block;
color: #666;
text-align: center;
padding-right: 20px;
text-decoration: none;
font-size: 17px;
}
#navbar a:hover {
background-color: #ddd;
color: black;
}
#navbar a.active {
background-color: #4caf50;
color: white;
}
.main {
padding: 16px;
margin-top: 30px;
width: 100%;
height: 100vh;
overflow: auto;
cursor: grab;
cursor: -o-grab;
cursor: -moz-grab;
cursor: -webkit-grab;
}
.main img {
height: auto;
width: auto;
}
.button {
width: 300px;
height: 60px;
}
/*---- toggle switch*/
.switch {
position: relative;
display: inline-block;
width: 30px;
height: 17px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: "";
height: 13px;
width: 13px;
left: 4px;
bottom: 2px;
background-color: white;
-webkit-transition: 0.4s;
transition: 0.4s;
}
input:checked+.slider {
background-color: #2196f3;
}
input:focus+.slider {
box-shadow: 0 0 1px #2196f3;
}
input:checked+.slider:before {
-webkit-transform: translateX(13px);
-ms-transform: translateX(13px);
transform: translateX(13px);
}
/* Rounded sliders */
.slider.round {
border-radius: 17px;
}
.slider.round:before {
border-radius: 50%;
}
.both {
margin-top: 50px;
display: flex;
justify-content: center;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./styles.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<title>Order by Picture</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<div id="navbar">
<button type="button" onclick="zoomin_canvas()">+</button>
<button type="button" onclick="zoomout_canvas()">-</button>
<button id="rotateRight" onclick="rotateRight()">Right</button>
<button id="rotateLeft" onclick="rotateLeft()">Left</button>
<button id="Button1" onclick="draw()">draw</button
<label> Marking</label>
<label class="switch">
<input type="checkbox" checked onclick="doMarking()" id="mark">
<span class="slider round"></span>
</label>
</div>
<div class="both">
<div class="canvas1">
<canvas id="canvas1" rotate="0" onclick="selectImg(event)" onmousedown="mouseDown(event)" onmouseup="mouseUp(event)" onmousemove="mouseMove(event)">
Your browser does not support the HTML5 canvas tag.
</canvas>
</div>
<div class="canvas2">
<canvas id="canvas2" rotate="0" onclick="selectImg(event)" onmousedown="mouseDown(event)" onmouseup="mouseUp(event)" onmousemove="mouseMove(event)">
Your browser does not support the HTML5 canvas tag.
</canvas>
</div>
</div>
</body>
</html>
Don't rotate the canvas
Draw the rotated image on the canvas, rather than rotate the canvas.
When you draw image on the canvas, rotate it, then you can draw over it using the normal mouse coords.
Rotating image on canvas
The code below will draw an image rotated by any amount. The image will be scaled down if needed to ensure it fits the canvas. The function will rotate the image any angle you want.
// ctx is canvas 2D context
// deg in degrees rotated CW from 3 O-clock
// img to render. NOTE image must be loaded first
function drawRotatedImage(ctx, deg, img) {
const w = img.naturalWidth;
const h = img.naturalHeight;
const cw = ctx.canvas.width;
const ch = ctx.canvas.height;
// convert deg to radians
const rad = deg * Math.PI / 180;
// Get vector for rotated xAxis ax, ay. With aax, aay in first quadrant
const ax = Math.cos(rad), aax = Math.abs(ax);
const ay = Math.sin(rad), aay = Math.abs(ay);
// get the rotated width and height of image
const tw = aax * w + aay * h;
const th = aay * w + aax * h;
// get scale so that image fits the canvas. Dont enlarge only reduce if to big
const scale = Math.min(1, cw / tw, ch / th);
// set canvas transform to center of canvas, rotated and scaled to fit
ctx.setTransform(ax * scale, ay * scale, -ay * scale, ax * scale, cw / 2, ch / 2);
// draw image on canvas offset by half its width and height
ctx.drawImage(img, -w / 2, -h / 2);
// restore canvas transform to default
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
Example
Example using the above function to render image to canvas and then use mouse to draw over the image. Click rotate to rotate the image.
const ctx = canvas.getContext("2d");
const mouse = {x: 0, y: 0, b: false, ox: 0, oy: 0};
var rot = 90;
const img = new Image;
img.src = "https://i.stack.imgur.com/C7qq2.png?s=420&g=1";
img.onload = () => rotImage(ctx, rot, img);
resize();
addEventListener("resize", resize);
rotBtn.addEventListener("click", () => img.complete && rotImage(ctx, rot += 90, img));
addEventListener("mousemove", mouseEvent);
addEventListener("mousedown", mouseEvent);
addEventListener("mouseup", mouseEvent);
addEventListener("mouseout", mouseEvent);
function resize() {
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.lineWidth = 3;
ctx.lineCap = "round";
ctx.strokeStyle = "#00F";
img.complete && rotImage(ctx, rot, img);
}
function mouseEvent(e) {
mouse.ox = mouse.x;
mouse.oy = mouse.y;
mouse.x = e.pageX;
mouse.y = e.pageY;
if (e.type === "mousedown") { mouse.b = true }
else if (e.type === "mouseup" || e.type === "mouseout") { mouse.b = false }
mouse.b && drawWithMouse(ctx, mouse);
}
function drawWithMouse(ctx, mouse) {
ctx.beginPath();
ctx.lineTo(mouse.ox, mouse.oy);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
}
function rotImage(ctx, deg, img) {
const cw = ctx.canvas.width, ch = ctx.canvas.height;
const w = img.naturalWidth, h = img.naturalHeight;
const rad = deg * Math.PI / 180;
const ax = Math.cos(rad), aax = Math.abs(ax);
const ay = Math.sin(rad), aay = Math.abs(ay);
const tw = aax * w + aay * h;
const th = aay * w + aax * h;
const sc = Math.min(1, cw / tw, ch / th);
ctx.clearRect(0, 0, cw, ch);
ctx.setTransform(ax * sc, ay * sc, -ay * sc, ax * sc, cw / 2, ch / 2);
ctx.drawImage(img, -w / 2, -h / 2);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
* {font-family: arial;}
button { position: absolute; top: 10px; left: 10px }
canvas { position: absolute; top: 0px; left: 0px }
<canvas id="canvas"></canvas>
<button id="rotBtn">Rotate</button>

Include Countdown Timer multiple times

I found this countdown timer that I would like to include in one of my modules. Unfortunately I can't figure out how to use the timer multiple times on one page (the classic one - only the last timer is running) - I also know that probably (?) the go function should be the reason - but currently I can't figure out how to instantiate the timer.
What I have so far (but it does not work):
Codepen Original: https://codepen.io/l422y/pen/cdwhm
I have in-depth JS basic knowledge but this is still beyond my horizon.
var ringer = {
//countdown_to: "10/31/2014",
//countdown_to: targetdaytime,
rings: {
'DAYS': {
s: 86400000, // mseconds in a day,
max: 365
},
'HOURS': {
s: 3600000, // mseconds per hour,
max: 24
},
'MINUTES': {
s: 60000, // mseconds per minute
max: 60
},
'SECONDS': {
s: 1000,
max: 60
},
'MICROSEC': {
s: 10,
max: 100
}
},
r_count: 5,
r_spacing: 10, // px
r_size: 100, // px
r_thickness: 2, // px
update_interval: 11, // ms
init: function(container, targetdaytime) {
$r = ringer;
$r.cvs = document.createElement('canvas');
$r.size = {
w: ($r.r_size + $r.r_thickness) * $r.r_count + ($r.r_spacing * ($r.r_count - 1)),
h: ($r.r_size + $r.r_thickness)
};
$r.cvs.setAttribute('width', $r.size.w);
$r.cvs.setAttribute('height', $r.size.h);
$r.ctx = $r.cvs.getContext('2d');
$(container).append($r.cvs);
$r.cvs = $($r.cvs);
$r.ctx.textAlign = 'center';
$r.actual_size = $r.r_size + $r.r_thickness;
$r.countdown_to_time = new Date(targetdaytime).getTime();
$r.cvs.css({
width: $r.size.w + "px",
height: $r.size.h + "px"
});
$r.go();
},
ctx: null,
go: function() {
var idx = 0;
$r.time = (new Date().getTime()) - $r.countdown_to_time;
for (var r_key in $r.rings) $r.unit(idx++, r_key, $r.rings[r_key]);
setTimeout($r.go, $r.update_interval);
},
unit: function(idx, label, ring) {
var x, y, value, ring_secs = ring.s;
value = parseFloat($r.time / ring_secs);
$r.time -= Math.round(parseInt(value)) * ring_secs;
value = Math.abs(value);
x = ($r.r_size * .5 + $r.r_thickness * .5);
x += +(idx * ($r.r_size + $r.r_spacing + $r.r_thickness));
y = $r.r_size * .5;
y += $r.r_thickness * .5;
// calculate arc end angle
var degrees = 360 - (value / ring.max) * 360.0;
var endAngle = degrees * (Math.PI / 180);
$r.ctx.save();
$r.ctx.translate(x, y);
$r.ctx.clearRect($r.actual_size * -0.5, $r.actual_size * -0.5, $r.actual_size, $r.actual_size);
// first circle
$r.ctx.strokeStyle = "rgba(128,128,128,0.2)";
$r.ctx.beginPath();
$r.ctx.arc(0, 0, $r.r_size / 2, 0, 2 * Math.PI, 2);
$r.ctx.lineWidth = $r.r_thickness;
$r.ctx.stroke();
// second circle
$r.ctx.strokeStyle = "rgba(253, 128, 1, 0.9)";
$r.ctx.beginPath();
$r.ctx.arc(0, 0, $r.r_size / 2, 0, endAngle, 1);
$r.ctx.lineWidth = $r.r_thickness;
$r.ctx.stroke();
// label
$r.ctx.fillStyle = "#ffffff";
$r.ctx.font = '12px Helvetica';
$r.ctx.fillText(label, 0, 23);
$r.ctx.fillText(label, 0, 23);
$r.ctx.font = 'bold 40px Helvetica';
$r.ctx.fillText(Math.floor(value), 0, 10);
$r.ctx.restore();
}
}
jQuery(document).ready(function($) {
new ringer.init('#cd-container-1', '12/24/2020');
new ringer.init('#cd-container-2', '10/31/2020');
});
body,
html {
width: 100%;
height: 100%;
margin: 0;
}
html {
display: table;
}
canvas {
width: 900px;
height: 200px;
display: block;
position: relative;
background: transparent;
margin: 40px auto;
}
body {
background: #000000;
background-image: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/12399/free-pumpkin-wallpaper-25771-26455-hd-wallpapers.jpg");
background-position: top center;
background-size: cover;
color: #fff;
margin: 0;
padding: 0;
overflow: hidden;
display: table-cell;
vertical-align: middle;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="cd-container-1"></div>
<div id="cd-container-2"></div>
It is because your object is not a JS class, therefore it is not possible to create instances of it. Its basically a single instance of a class.
I transformed it to a class for you, so now you can initialize multiple instances of it:
class Ringer { // <-- make class from it
rings = { // <-- object properties to class fields
'DAYS': {
s: 86400000, // mseconds in a day,
max: 365
},
'HOURS': {
s: 3600000, // mseconds per hour,
max: 24
},
'MINUTES': {
s: 60000, // mseconds per minute
max: 60
},
'SECONDS': {
s: 1000,
max: 60
},
'MICROSEC': {
s: 10,
max: 100
}
};
r_count = 5;
r_spacing = 10; // px
r_size = 100; // px
r_thickness = 2; // px
update_interval = 11; // ms
ctx = null;
init (container, targetdaytime) { // <-- functions to class methods
this.cvs = document.createElement('canvas'); // <-- just access this instead of reference to the object
this.size = {
w: (this.r_size + this.r_thickness) * this.r_count + (this.r_spacing * (this.r_count - 1)),
h: (this.r_size + this.r_thickness)
};
this.cvs.setAttribute('width', this.size.w);
this.cvs.setAttribute('height', this.size.h);
this.ctx = this.cvs.getContext('2d');
$(container).append(this.cvs);
this.cvs = $(this.cvs);
this.ctx.textAlign = 'center';
this.actual_size = this.r_size + this.r_thickness;
this.countdown_to_time = new Date(targetdaytime).getTime();
this.cvs.css({
width: this.size.w + "px",
height: this.size.h + "px"
});
this.go();
}
go() {
var idx = 0;
this.time = (new Date().getTime()) - this.countdown_to_time;
for (var r_key in this.rings) this.unit(idx++, r_key, this.rings[r_key]);
setTimeout(() => this.go(), this.update_interval); // () => this.go() instead of $r.go
}
unit(idx, label, ring) {
var x, y, value, ring_secs = ring.s;
value = parseFloat(this.time / ring_secs);
this.time -= Math.round(parseInt(value)) * ring_secs;
value = Math.abs(value);
x = (this.r_size * .5 + this.r_thickness * .5);
x += +(idx * (this.r_size + this.r_spacing + this.r_thickness));
y = this.r_size * .5;
y += this.r_thickness * .5;
// calculate arc end angle
var degrees = 360 - (value / ring.max) * 360.0;
var endAngle = degrees * (Math.PI / 180);
this.ctx.save();
this.ctx.translate(x, y);
this.ctx.clearRect(this.actual_size * -0.5, this.actual_size * -0.5, this.actual_size, this.actual_size);
// first circle
this.ctx.strokeStyle = "rgba(128,128,128,0.2)";
this.ctx.beginPath();
this.ctx.arc(0, 0, this.r_size / 2, 0, 2 * Math.PI, 2);
this.ctx.lineWidth = this.r_thickness;
this.ctx.stroke();
// second circle
this.ctx.strokeStyle = "rgba(253, 128, 1, 0.9)";
this.ctx.beginPath();
this.ctx.arc(0, 0, this.r_size / 2, 0, endAngle, 1);
this.ctx.lineWidth = this.r_thickness;
this.ctx.stroke();
// label
this.ctx.fillStyle = "#ffffff";
this.ctx.font = '12px Helvetica';
this.ctx.fillText(label, 0, 23);
this.ctx.fillText(label, 0, 23);
this.ctx.font = 'bold 40px Helvetica';
this.ctx.fillText(Math.floor(value), 0, 10);
this.ctx.restore();
}
}
jQuery(document).ready(function($) {
(new Ringer()).init('#cd-container-1', '12/24/2020'); // <-- init new instance with new Ringer() and call init() on that instance
(new Ringer()).init('#cd-container-2', '10/31/2020');
});
body,
html {
width: 100%;
height: 100%;
margin: 0;
}
html {
display: table;
}
canvas {
width: 900px;
height: 200px;
display: block;
position: relative;
background: transparent;
margin: 40px auto;
}
body {
background: #000000;
background-image: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/12399/free-pumpkin-wallpaper-25771-26455-hd-wallpapers.jpg");
background-position: top center;
background-size: cover;
color: #fff;
margin: 0;
padding: 0;
overflow: hidden;
display: table-cell;
vertical-align: middle;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="cd-container-1"></div>
<div id="cd-container-2"></div>
If you want to read more into classes and how they work, I found a cool little summary here: https://www.digitalocean.com/community/tutorials/js-objects-prototypes-classes
Simple: wrap it in a function.
function createRinger() {
const ringer = { ... }
return ringer
}
const ringer1 = createRinger()
const ringer2 = createRinger()
ringer1.init(target1, targetDateTime1)
ringer2.init(target2, targetDateTime2)
There are other ways that improve efficiency of memory use (eg use the prototype chain), but unless you have lots of timers on a page, these approaches are moot.

How do i add a link to this animated button

Essentially i found this button that i wanted to add to my wixsite that links to a store. I have gotten the button animation to work and this button exists as a html element on wix. But all the button does currently is do the animation and dosent link. Could someone edit this code so after the animation plays the user will be redirected to a certain link.
I've tried looking up link code and inserting it in logical places to determine where it might work but obviously i dident find anything. And even if it did it likely would have redirected before the animation finished.
Here is the code without any of my attempts to try and fix this problem.
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
Math.randMinMax = function (min, max, round) {
var val = min + (Math.random() * (max - min));
if (round) val = Math.round(val);
return val;
};
Math.TO_RAD = Math.PI / 180;
Math.getAngle = function (x1, y1, x2, y2) {
var dx = x1 - x2,
dy = y1 - y2;
return Math.atan2(dy, dx);
};
Math.getDistance = function (x1, y1, x2, y2) {
var xs = x2 - x1,
ys = y2 - y1;
xs *= xs;
ys *= ys;
return Math.sqrt(xs + ys);
};
var FX = {};
(function () {
var canvas = document.getElementById('myCanvas'),
ctx = canvas.getContext('2d'),
lastUpdate = new Date(),
mouseUpdate = new Date(),
lastMouse = [],
width, height;
FX.particles = [];
setFullscreen();
document.getElementById('button').addEventListener('mousedown', buttonEffect);
function buttonEffect() {
var button = document.getElementById('button'),
height = button.offsetHeight,
left = button.offsetLeft,
top = button.offsetTop,
width = button.offsetWidth,
x, y, degree;
for (var i = 0; i < 40; i = i + 1) {
if (Math.random() < 0.5) {
y = Math.randMinMax(top, top + height);
if (Math.random() < 0.5) {
x = left;
degree = Math.randMinMax(-45, 45);
} else {
x = left + width;
degree = Math.randMinMax(135, 225);
}
} else {
x = Math.randMinMax(left, left + width);
if (Math.random() < 0.5) {
y = top;
degree = Math.randMinMax(45, 135);
} else {
y = top + height;
degree = Math.randMinMax(-135, -45);
}
}
createParticle({
x: x,
y: y,
degree: degree,
speed: Math.randMinMax(100, 150),
vs: Math.randMinMax(-4, -1)
});
}
}
window.setTimeout(buttonEffect, 100);
loop();
window.addEventListener('resize', setFullscreen);
function createParticle(args) {
var options = {
x: width / 2,
y: height / 2,
color: 'hsla(' + Math.randMinMax(160, 290) + ', 100%, 50%, ' + (Math.random().toFixed(2)) + ')',
degree: Math.randMinMax(0, 360),
speed: Math.randMinMax(300, 350),
vd: Math.randMinMax(-90, 90),
vs: Math.randMinMax(-8, -5)
};
for (key in args) {
options[key] = args[key];
}
FX.particles.push(options);
}
function loop() {
var thisUpdate = new Date(),
delta = (lastUpdate - thisUpdate) / 1000,
amount = FX.particles.length,
size = 2,
i = 0,
p;
ctx.fillStyle = 'rgba(15,15,15,0.25)';
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeStyle = 'lighter';
for (; i < amount; i = i + 1) {
p = FX.particles[i];
p.degree += (p.vd * delta);
p.speed += (p.vs);// * delta);
if (p.speed < 0) continue;
p.x += Math.cos(p.degree * Math.TO_RAD) * (p.speed * delta);
p.y += Math.sin(p.degree * Math.TO_RAD) * (p.speed * delta);
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate(p.degree * Math.TO_RAD);
ctx.fillStyle = p.color;
ctx.fillRect(-size, -size, size * 2, size * 2);
ctx.restore();
}
lastUpdate = thisUpdate;
requestAnimFrame(loop);
}
function setFullscreen() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
};
})();
body {
margin: 0;
overflow: hidden;
}
#myCanvas {
display: block;
}
#button {
font-family: "Gill Sans", "Gill Sans MT", Calibri, sans-serif;
position: absolute;
font-size: 1.5em;
text-transform: uppercase;
padding: 7px 20px;
left: 50%;
width: 200px;
margin-left: -100px;
top: 50%;
border-radius: 10px;
color: white;
text-shadow: -1px -1px 1px rgba(0,0,0,0.8);
border: 5px solid transparent;
border-bottom-color: rgba(0,0,0,0.35);
background: hsla(260, 100%, 50%, 1);
cursor: pointer;
outline: 0 !important;
animation: pulse 1s infinite alternate;
transition: background 0.4s, border 0.2s, margin 0.2s;
}
#button:hover {
background: hsla(220, 100%, 60%, 1);
margin-top: -1px;
animation: none;
}
#button:active {
border-bottom-width: 0;
margin-top: 5px;
}
#keyframes pulse {
0% {
margin-top: 0px;
}
100% {
margin-top: 6px;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
<button id="button">Donate</button>
<canvas id="myCanvas" width="500" height="500"></canvas>
</body>
</html>
So again the expected result is to play the animation for the button then redirect to another page and the current result is the button simply playing the animation when clicked. If anyone could please write this code it would be super helpful.
Try changing your button to a href link. You may have to add some extra styling to the id class, but this should work.
<a id="button" href="https://www.linktosite.com">Link Button</a>
Put this on the button html tag onclick="location.href='http://www.link.com'"
You should add an activity when click on button. For example:
<button id="button" onclick="window.location.href = 'https://www.google.com';">Donate</button>
Demo:
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
Math.randMinMax = function (min, max, round) {
var val = min + (Math.random() * (max - min));
if (round) val = Math.round(val);
return val;
};
Math.TO_RAD = Math.PI / 180;
Math.getAngle = function (x1, y1, x2, y2) {
var dx = x1 - x2,
dy = y1 - y2;
return Math.atan2(dy, dx);
};
Math.getDistance = function (x1, y1, x2, y2) {
var xs = x2 - x1,
ys = y2 - y1;
xs *= xs;
ys *= ys;
return Math.sqrt(xs + ys);
};
var FX = {};
(function () {
var canvas = document.getElementById('myCanvas'),
ctx = canvas.getContext('2d'),
lastUpdate = new Date(),
mouseUpdate = new Date(),
lastMouse = [],
width, height;
FX.particles = [];
setFullscreen();
document.getElementById('button').addEventListener('mousedown', buttonEffect);
function buttonEffect() {
var button = document.getElementById('button'),
height = button.offsetHeight,
left = button.offsetLeft,
top = button.offsetTop,
width = button.offsetWidth,
x, y, degree;
for (var i = 0; i < 40; i = i + 1) {
if (Math.random() < 0.5) {
y = Math.randMinMax(top, top + height);
if (Math.random() < 0.5) {
x = left;
degree = Math.randMinMax(-45, 45);
} else {
x = left + width;
degree = Math.randMinMax(135, 225);
}
} else {
x = Math.randMinMax(left, left + width);
if (Math.random() < 0.5) {
y = top;
degree = Math.randMinMax(45, 135);
} else {
y = top + height;
degree = Math.randMinMax(-135, -45);
}
}
createParticle({
x: x,
y: y,
degree: degree,
speed: Math.randMinMax(100, 150),
vs: Math.randMinMax(-4, -1)
});
}
}
window.setTimeout(buttonEffect, 100);
loop();
window.addEventListener('resize', setFullscreen);
function createParticle(args) {
var options = {
x: width / 2,
y: height / 2,
color: 'hsla(' + Math.randMinMax(160, 290) + ', 100%, 50%, ' + (Math.random().toFixed(2)) + ')',
degree: Math.randMinMax(0, 360),
speed: Math.randMinMax(300, 350),
vd: Math.randMinMax(-90, 90),
vs: Math.randMinMax(-8, -5)
};
for (key in args) {
options[key] = args[key];
}
FX.particles.push(options);
}
function loop() {
var thisUpdate = new Date(),
delta = (lastUpdate - thisUpdate) / 1000,
amount = FX.particles.length,
size = 2,
i = 0,
p;
ctx.fillStyle = 'rgba(15,15,15,0.25)';
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeStyle = 'lighter';
for (; i < amount; i = i + 1) {
p = FX.particles[i];
p.degree += (p.vd * delta);
p.speed += (p.vs);// * delta);
if (p.speed < 0) continue;
p.x += Math.cos(p.degree * Math.TO_RAD) * (p.speed * delta);
p.y += Math.sin(p.degree * Math.TO_RAD) * (p.speed * delta);
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate(p.degree * Math.TO_RAD);
ctx.fillStyle = p.color;
ctx.fillRect(-size, -size, size * 2, size * 2);
ctx.restore();
}
lastUpdate = thisUpdate;
requestAnimFrame(loop);
}
function setFullscreen() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
};
})();
body {
margin: 0;
overflow: hidden;
}
#myCanvas {
display: block;
}
#button {
font-family: "Gill Sans", "Gill Sans MT", Calibri, sans-serif;
position: absolute;
font-size: 1.5em;
text-transform: uppercase;
padding: 7px 20px;
left: 50%;
width: 200px;
margin-left: -100px;
top: 50%;
border-radius: 10px;
color: white;
text-shadow: -1px -1px 1px rgba(0,0,0,0.8);
border: 5px solid transparent;
border-bottom-color: rgba(0,0,0,0.35);
background: hsla(260, 100%, 50%, 1);
cursor: pointer;
outline: 0 !important;
animation: pulse 1s infinite alternate;
transition: background 0.4s, border 0.2s, margin 0.2s;
}
#button:hover {
background: hsla(220, 100%, 60%, 1);
margin-top: -1px;
animation: none;
}
#button:active {
border-bottom-width: 0;
margin-top: 5px;
}
#keyframes pulse {
0% {
margin-top: 0px;
}
100% {
margin-top: 6px;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
<button id="button" onclick="window.location.href = 'https://www.google.com';">Donate</button>
<canvas id="myCanvas" width="500" height="500"></canvas>
</body>
</html>

How to set a script as Background of a concrete <section>?

I am using Bootstrap for a one-page website project. This website is divided in 4 parts, each one inside a 'section' with its own background color/image and content.
For one of these sections (the first one), which occupies the whole screen and has a 'Navbar' at the top and some headers inside, I would like to set a script as the background and not a simple image or solid color as in the other sections.
For this, I would like to know how to set a JavaScript code as the background, without affecting any other item inside the same section and being able to see the text over this background element.
This is the concrete script that I would like to set as background (got it from codePen.io): http://jsfiddle.net/oleg_korol/a1bxr5ua/1/
Here is the code:
var w = c.width = window.innerWidth,
h = c.height = window.innerHeight,
ctx = c.getContext('2d'),
opts = {
len: 20,
count: 50,
baseTime: 10,
addedTime: 10,
dieChance: .05,
spawnChance: 1,
sparkChance: .1,
sparkDist: 10,
sparkSize: 2,
color: 'hsl(hue,100%,light%)',
baseLight: 50,
addedLight: 10, // [50-10,50+10]
shadowToTimePropMult: 6,
baseLightInputMultiplier: .01,
addedLightInputMultiplier: .02,
cx: w / 2,
cy: h / 2,
repaintAlpha: .04,
hueChange: .1
},
tick = 0,
lines = [],
dieX = w / 2 / opts.len,
dieY = h / 2 / opts.len,
baseRad = Math.PI * 2 / 6;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
function loop() {
window.requestAnimationFrame(loop);
++tick;
ctx.globalCompositeOperation = 'source-over';
ctx.shadowBlur = 0;
ctx.fillStyle = 'rgba(0,0,0,alp)'.replace('alp', opts.repaintAlpha);
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'lighter';
if (lines.length < opts.count && Math.random() < opts.spawnChance) lines.push(new Line);
lines.map(function (line) {
line.step();
});
}
function Line() {
this.reset();
}
Line.prototype.reset = function () {
this.x = 0;
this.y = 0;
this.addedX = 0;
this.addedY = 0;
this.rad = 0;
this.lightInputMultiplier = opts.baseLightInputMultiplier + opts.addedLightInputMultiplier * Math.random();
this.color = opts.color.replace('hue', tick * opts.hueChange);
this.cumulativeTime = 0;
this.beginPhase();
}
Line.prototype.beginPhase = function () {
this.x += this.addedX;
this.y += this.addedY;
this.time = 0;
this.targetTime = (opts.baseTime + opts.addedTime * Math.random()) | 0;
this.rad += baseRad * (Math.random() < .5 ? 1 : -1);
this.addedX = Math.cos(this.rad);
this.addedY = Math.sin(this.rad);
if (Math.random() < opts.dieChance || this.x > dieX || this.x < -dieX || this.y > dieY || this.y < -dieY) this.reset();
}
Line.prototype.step = function () {
++this.time;
++this.cumulativeTime;
if (this.time >= this.targetTime) this.beginPhase();
var prop = this.time / this.targetTime,
wave = Math.sin(prop * Math.PI / 2),
x = this.addedX * wave,
y = this.addedY * wave;
ctx.shadowBlur = prop * opts.shadowToTimePropMult;
ctx.fillStyle = ctx.shadowColor = this.color.replace('light', opts.baseLight + opts.addedLight * Math.sin(this.cumulativeTime * this.lightInputMultiplier));
ctx.fillRect(opts.cx + (this.x + x) * opts.len, opts.cy + (this.y + y) * opts.len, 2, 2);
if (Math.random() < opts.sparkChance) ctx.fillRect(opts.cx + (this.x + x) * opts.len + Math.random() * opts.sparkDist * (Math.random() < .5 ? 1 : -1) - opts.sparkSize / 2, opts.cy + (this.y + y) * opts.len + Math.random() * opts.sparkDist * (Math.random() < .5 ? 1 : -1) - opts.sparkSize / 2, opts.sparkSize, opts.sparkSize)
}
loop();
window.addEventListener('resize', function () {
w = c.width = window.innerWidth;
h = c.height = window.innerHeight;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
opts.cx = w / 2;
opts.cy = h / 2;
dieX = w / 2 / opts.len;
dieY = h / 2 / opts.len;
});
//# sourceURL=pen.js
Thanks a lot!
Original piece of code from: http://codepen.io/towc/pen/mJzOWJ
by Matei Copot
You can do this using an iframe and placing it as an absolute element with full body width and height like this:
HTML:
<body>
<div id="iframe"><iframe name="result" sandbox="allow-forms allow-popups allow-scripts allow-same-origin" frameborder="0" src="//fiddle.jshell.net/oleg_korol/a1bxr5ua/1/show/"></iframe></div>
<!--Your page content-->
</body>
CSS:
html, body {
margin:0px;
width:100%;
height:100%;
overflow:hidden;
}
iframe {
width:100%;
height:100%;
}
#iframe {
position: absolute;
float: left;
clear: both;
width: 100%;
height: 100%;
z-index: 0;
}
Here is a jsfiddle with above codes: https://jsfiddle.net/AndrewL32/kkLLxkxk/2/
And here is a StackOverflow snippet with above codes:
html, body {
margin:0px;
width:100%;
height:100%;
overflow:hidden;
}
iframe {
width:100%;
height:100%;
}
#iframe {
position: absolute;
float: left;
clear: both;
width: 100%;
height: 100%;
z-index: 0;
}
<div id="iframe"><iframe name="result" sandbox="allow-forms allow-popups allow-scripts allow-same-origin" frameborder="0" src="//fiddle.jshell.net/oleg_korol/a1bxr5ua/1/show/"></iframe></div>
WITHOUT IFRAME:
If you want to use the canvas as the background-image for the whole page, then the current code that you are using should work fine with width and height defined:
canvas {
position:absolute;
width:100%;
height:100%;
top:0px;
left:0px;
}
However if you want it as the background-image only for a certain div, then you can just add a position:relative; property to the parent div like this:
HTML:
<div id="canvas-box">
<canvas id=c></canvas>
</div>
CSS:
#canvas-box {
position:relative;
width:500px;
height:500px;
}
canvas {
position: absolute;
top: 0;
left: 0;
width:100%;
height:100%;
}
Here is a jsfiddle for adding canvas as background for a specific div: http://jsfiddle.net/AndrewL32/a1bxr5ua/5/

Positioning divs in a circle using JavaScript

I am trying to position 15 div elements evenly in a circle with a radius of 150px. I'm using the following code, which seems to give an oddly eccentric ellipse that overlaps.
Fiddle
// Hold a global reference to the div#main element. Initially assign it ... somewhere useful :)
var main = document.getElementById('main');
var circleArray = [];
// Move a circle based on the distance of the approaching mouse
var moveCircle = function(circle, dx, dy) {
};
// Look at all the circle elements, and figure out if any of them have to move.
var checkMove = function() {
};
var setup = function() {
for (var i = 0; i < 15; i++) {
//create element, add it to the array, and assign it's coordinates trigonometrically.
//Then add it to the "main" div
var circle = document.createElement('div');
circle.className = 'circle number' + i;
circleArray.push(circle);
circleArray[i].posx = Math.round((150 * Math.cos(i * (2 * Math.PI / 15)))) + 'px';
circleArray[i].posy = Math.round((150 * Math.sin(i * (2 * Math.PI / 15)))) + 'px';
circleArray[i].style.position = "relative";
circleArray[i].style.top = circleArray[i].posy;
circleArray[i].style.left = circleArray[i].posx;
main.appendChild(circleArray[i]);
}
};
setup();
window.addEventListener('load', function() {
});
div {
box-sizing: border-box;
}
div#main {
position: absolute;
left: 50%;
top: 50%;
}
div.circle {
position: absolute;
width: 20px;
height: 20px;
border: 2px solid black;
border-radius: 10px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
}
<div id="main"></div>
Any suggestions as to what I might be doing wrong?
First of all, the equation for a co-ordinate on a circle is simply:
(x, y) = (r * cos(θ), r * sin(θ))
where, r is the radius of a circle and θ is the angle in radians.
The reason why your code is creating an eccentric ellipse is because when you assign the .top and .left CSS values, you are not considering that it will actually take the top-left corner as its reference. I've fixed your code and now it creates a perfect circle.
Changes made to your code:
Added an array theta that holds all the angles.
var theta = [0, Math.PI / 6, Math.PI / 4, Math.PI / 3, Math.PI / 2, 2 * (Math.PI / 3), 3 * (Math.PI / 4), 5 * (Math.PI / 6), Math.PI, 7 * (Math.PI / 6), 5 * (Math.PI / 4), 4 * (Math.PI / 3), 3 * (Math.PI / 2), 5 * (Math.PI / 3), 7 * (Math.PI / 4), 11 * (Math.PI / 6)];
The image below shows all the angles I've used.
Added an array colors that holds different colors.
var colors = ['red', 'green', 'purple', 'black', 'orange', 'yellow', 'maroon', 'grey', 'lightblue', 'tomato', 'pink', 'maroon', 'cyan', 'magenta', 'blue', 'chocolate', 'DarkSlateBlue'];
Made changes to your trigonometric equations.
circleArray[i].posx = Math.round(radius * (Math.cos(theta[i]))) + 'px';
circleArray[i].posy = Math.round(radius * (Math.sin(theta[i]))) + 'px';
Changed the way .top and .left are assigned.
circleArray[i].style.top = ((mainHeight / 2) - parseInt(circleArray[i].posy.slice(0, -2))) + 'px';
circleArray[i].style.left = ((mainHeight / 2) + parseInt(circleArray[i].posx.slice(0, -2))) + 'px';
where mainHeight is the height of the #main div.
[1] 16 divs
Demo on Fiddle
var setup = function() {
var radius = 150;
var main = document.getElementById('main');
var mainHeight = parseInt(window.getComputedStyle(main).height.slice(0, -2));
var theta = [0, Math.PI / 6, Math.PI / 4, Math.PI / 3, Math.PI / 2, 2 * (Math.PI / 3), 3 * (Math.PI / 4), 5 * (Math.PI / 6), Math.PI, 7 * (Math.PI / 6), 5 * (Math.PI / 4), 4 * (Math.PI / 3), 3 * (Math.PI / 2), 5 * (Math.PI / 3), 7 * (Math.PI / 4), 11 * (Math.PI / 6)];
var circleArray = [];
var colors = ['red', 'green', 'purple', 'black', 'orange', 'yellow', 'maroon', 'grey', 'lightblue', 'tomato', 'pink', 'maroon', 'cyan', 'magenta', 'blue', 'chocolate', 'DarkSlateBlue'];
for (var i = 0; i < 16; i++) {
var circle = document.createElement('div');
circle.className = 'circle number' + i;
circleArray.push(circle);
circleArray[i].posx = Math.round(radius * (Math.cos(theta[i]))) + 'px';
circleArray[i].posy = Math.round(radius * (Math.sin(theta[i]))) + 'px';
circleArray[i].style.position = "absolute";
circleArray[i].style.backgroundColor = colors[i];
circleArray[i].style.top = ((mainHeight / 2) - parseInt(circleArray[i].posy.slice(0, -2))) + 'px';
circleArray[i].style.left = ((mainHeight / 2) + parseInt(circleArray[i].posx.slice(0, -2))) + 'px';
main.appendChild(circleArray[i]);
}
};
setup();
div#main {
height: 300px;
width: 300px;
position: absolute;
margin: 0 auto;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
}
div.circle {
position: absolute;
width: 20px;
height: 20px;
border: 2px solid black;
border-radius: 50%;
}
body {
margin: 0 auto;
background: papayawhip;
}
<div id="main"></div>
[2] 15 divs Positioned Evenly
Demo on Fiddle
var setup = function() {
var radius = 150;
var main = document.getElementById('main');
var mainHeight = parseInt(window.getComputedStyle(main).height.slice(0, -2));
var theta = [0, (2 * (Math.PI / 15)), (4 * (Math.PI / 15)), (2 * (Math.PI / 5)), (8 * (Math.PI / 15)), (2 * (Math.PI / 3)), (4 * (Math.PI / 5)), (14 * (Math.PI / 15)), (16 * (Math.PI / 15)), (6 * (Math.PI / 5)), (4 * (Math.PI / 3)), (22 * (Math.PI / 15)), (8 * (Math.PI / 5)), (26 * (Math.PI / 15)), (28 * (Math.PI / 15))];
var circleArray = [];
var colors = ['red', 'green', 'purple', 'black', 'orange', 'yellow', 'maroon', 'grey', 'lightblue', 'tomato', 'pink', 'maroon', 'cyan', 'magenta', 'blue', 'chocolate', 'DarkSlateBlue'];
for (var i = 0; i < 15; i++) {
var circle = document.createElement('div');
circle.className = 'circle number' + i;
circleArray.push(circle);
circleArray[i].posx = Math.round(radius * (Math.cos(theta[i]))) + 'px';
circleArray[i].posy = Math.round(radius * (Math.sin(theta[i]))) + 'px';
circleArray[i].style.position = "absolute";
circleArray[i].style.backgroundColor = colors[i];
circleArray[i].style.top = ((mainHeight / 2) - parseInt(circleArray[i].posy.slice(0, -2))) + 'px';
circleArray[i].style.left = ((mainHeight / 2) + parseInt(circleArray[i].posx.slice(0, -2))) + 'px';
main.appendChild(circleArray[i]);
}
};
setup();
div#main {
height: 300px;
width: 300px;
position: absolute;
margin: 0 auto;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
}
div.circle {
position: absolute;
width: 20px;
height: 20px;
border: 2px solid black;
border-radius: 50%;
}
body {
margin: 0 auto;
background: papayawhip;
}
<div id="main"></div>
[3] Dynamically Position any number of divs on an Ellipse/Circle
The equation for a co-ordinate on an ellipse is:
(x, y) = (rx * cos(θ), ry * sin(θ))
where, rx is the radius along X-axis and ry is the radius along Y-axis.
In this case, the function generate(n, rx, ry, id) takes four arguments, where n is the number of divs, rx and ry are the radii along the X and Y-axis respectively and finally id is the id of the div that you want to append your elliptically arranged divs in.
Demo on Fiddle
var theta = [];
var setup = function(n, rx, ry, id) {
var main = document.getElementById(id);
var mainHeight = parseInt(window.getComputedStyle(main).height.slice(0, -2));
var circleArray = [];
var colors = ['red', 'green', 'purple', 'black', 'orange', 'yellow', 'maroon', 'grey', 'lightblue', 'tomato', 'pink', 'maroon', 'cyan', 'magenta', 'blue', 'chocolate', 'darkslateblue', 'coral', 'blueviolet', 'burlywood', 'cornflowerblue', 'crimson', 'darkgoldenrod', 'olive', 'sienna', 'red', 'green', 'purple', 'black', 'orange', 'yellow', 'maroon', 'grey', 'lightblue', 'tomato', 'pink', 'maroon', 'cyan', 'magenta', 'blue', 'chocolate', 'darkslateblue', 'coral', 'blueviolet', 'burlywood', 'cornflowerblue', 'crimson', 'darkgoldenrod', 'olive', 'sienna'];
for (var i = 0; i < n; i++) {
var circle = document.createElement('div');
circle.className = 'circle number' + i;
circleArray.push(circle);
circleArray[i].posx = Math.round(rx * (Math.cos(theta[i]))) + 'px';
circleArray[i].posy = Math.round(ry * (Math.sin(theta[i]))) + 'px';
circleArray[i].style.position = "absolute";
circleArray[i].style.backgroundColor = colors[i];
circleArray[i].style.top = ((mainHeight / 2) - parseInt(circleArray[i].posy.slice(0, -2))) + 'px';
circleArray[i].style.left = ((mainHeight / 2) + parseInt(circleArray[i].posx.slice(0, -2))) + 'px';
main.appendChild(circleArray[i]);
}
};
var generate = function(n, rx, ry, id) {
var frags = 360 / n;
for (var i = 0; i <= n; i++) {
theta.push((frags / 180) * i * Math.PI);
}
setup(n, rx, ry, id)
}
generate(16, 150, 75, 'main');
div#main {
height: 300px;
width: 300px;
position: absolute;
margin: 0 auto;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
}
div.circle {
position: absolute;
width: 20px;
height: 20px;
border: 2px solid black;
border-radius: 50%;
}
body {
margin: 0 auto;
background: papayawhip;
}
<div id="main"></div>
Edit[9th December 2015]:
Here's a more flexible version with start offset, clock wise and anti-clock wise functionality.
/*
Usage: Position.ellipse(n, rx, ry, so, wh, idd, cls, cw);
where n = number of divs,
rx = radius along X-axis,
ry = radius along Y-axis,
so = startOffset,
wh = width/height of divs,
idd = id of main div(ellipse),
cls = className of divs;
cw = clockwise(true/false)
*/
var Position = {
ellipse: function(n, rx, ry, so, wh, idd, cls, cw) {
var m = document.createElement('div'),
ss = document.styleSheets;
ss[0].insertRule('#' + idd + ' { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); border-radius: 50%; box-shadow: inset 0 0 ' + wh + 'px ' + wh / 4 + 'px black; background: rgba(0, 0, 0, 0.2); width: ' + String((rx * 2) + wh) + 'px; height: ' + String((ry * 2) + wh) + 'px; }', 1);
ss[0].insertRule('.' + cls + '{ position: absolute; background: black; color: papayawhip; text-align: center; font-family: "Open Sans Condensed", sans-serif; border-radius: 50%; transition: transform 0.2s ease; width: ' + wh + 'px; height: ' + wh + 'px; line-height: ' + wh + 'px;}', 1);
ss[0].insertRule('.' + cls + ':hover { transform: scale(1.2); cursor: pointer; background: rgba(0, 0, 0, 0.8); }', 1);
m.id = idd;
for (var i = 0; i < n; i++) {
var c = document.createElement('div');
c.className = cls;
c.innerHTML = i + 1;
c.style.top = String(ry + -ry * Math.cos((360 / n / 180) * (i + so) * Math.PI)) + 'px';
c.style.left = String(rx + rx * (cw ? Math.sin((360 / n / 180) * (i + so) * Math.PI) : -Math.sin((360 / n / 180) * (i + so) * Math.PI))) + 'px';
m.appendChild(c);
}
document.body.appendChild(m);
}
}
Position.ellipse(20, 150, 150, 0, 42, 'main', 'circle', true);
#import url(http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300);
body {
margin: 0 auto;
background: rgb(198, 193, 173);
}
Other approach without JS
chipChocolate.py's anser is pretty complete but there is an other way to achieve your aim. It is simpler and doesn't require JS.
The point is to think "circle" and rotation rather than rely on [x,y] coordinates :
You need to nest all the elements and apply a rotation to them. As they are nested the n + 1 element will rotate according to it's direct parent's rotation. Here is a DEMO :
.circle, .circle div {
width:24px; height:300px;
position:absolute;
left:50%; top:50px;
}
.circle:before, .circle div:before {
content:'';
display:block;
width:20px; height:20px;
border: 2px solid black;
border-radius: 100%;
}
.circle div {
top:0; left:0;
-webkit-transform : rotate(24deg);
-ms-transform : rotate(24deg);
transform : rotate(24deg);
}
<div class="circle">
<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>
</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
</div>
The diameter of the circle is controled by the height of the elements (in the demo height:300px) you can make that a percentage to make the circle responsive (see below).
The rotation must be set according to the number of elements you want around the circle. In the demo 15 elements so rotation = 360 / 15 = 24deg.
If you have a dynamic number of elements, you may use JS to add them and to calculate the rotation angle needed.
Responsive example
DEMO
.circle{
position:relative;
width:5%;padding-bottom:50%;
margin-left:47.5%;
}
.circle div {
position:absolute;
top:0; left:0;
width:100%; height:100%;
-webkit-transform : rotate(24deg);
-ms-transform : rotate(24deg);
transform : rotate(24deg);
}
.circle:before, .circle div:before {
content:'';
position:absolute;
top:0; left:0;
width:100%; padding-bottom:100%;
border-radius: 100%;
border: 2px solid teal;
background:gold;
}
<div class="circle">
<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>
</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
</div>
Yet another solution, based on ideas from other solutions I've seen
http://jsfiddle.net/0hr1n7a2/6/
(function() {
var radians, radius;
radius = 150;
var totalItems = 48
var item = 0;
function positionTarget()
{
var x, y, angle = 0, step = (2*Math.PI) / totalItems;
var width = $('#container').width()/2;
var height = $('#container').height()/2;
var itemW = 20, itemH = 2;
var deg = 0;
while(item <= totalItems)
{
x = Math.round(width + radius * Math.cos(angle) - itemW/2);
y = Math.round(height + radius * Math.sin(angle) - itemH/2);
//console.log(x + "," + y);
$('#container').append('<div id="'+ item +'"/>')
$('div#'+item).css('position', 'absolute')
.css('width', itemW+'px').css('height', itemH+'px')
.css('left', x+'px').css('top', y+'px')
.css('background-color', 'blue')
.css('transform-origin', x+'px' -y+'px')
.css('transform', 'rotate('+ deg +'deg)')
.css('border', 'solid 1px #000');
angle += step;
++item;
deg += 360/totalItems;
//console.log(deg)
}
}
$('#theButton').on('click', function()
{
positionTarget();
})
})();
#container { width: 600px; height: 600px; border: 1px solid #000; position: relative; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" id="theButton" value="Draw">
<div id="container">
</div>
Set the position to "absolute". This will allow "top" and "left" to position the divs from (0, 0). Using "relative" will position the divs from where they would normally be laid out.
Change the center point of your circle from (0, 0) to something else, like (250, 250).
circleArray[i].posx = 250 + Math.round((150*Math.cos(i*(2*Math.PI/15)))) + 'px';
circleArray[i].posy = 250 + Math.round((150*Math.sin(i*(2*Math.PI/15)))) + 'px';
circleArray[i].style.position = "absolute";

Categories

Resources