Animated SVG circles (timer and progress) - javascript

How can I change this:
var el = document.getElementById('graph'); // get canvas
var options = {
percent: el.getAttribute('data-percent') || 25,
size: el.getAttribute('data-size') || 220,
lineWidth: el.getAttribute('data-line') || 15,
rotate: el.getAttribute('data-rotate') || 0
}
var canvas = document.createElement('canvas');
var span = document.createElement('span');
span.textContent = options.percent + '%';
if (typeof(G_vmlCanvasManager) !== 'undefined') {
G_vmlCanvasManager.initElement(canvas);
}
var ctx = canvas.getContext('2d');
canvas.width = canvas.height = options.size;
el.appendChild(span);
el.appendChild(canvas);
ctx.translate(options.size / 2, options.size / 2); // change center
ctx.rotate((-1 / 2 + options.rotate / 180) * Math.PI); // rotate -90 deg
//imd = ctx.getImageData(0, 0, 240, 240);
var radius = (options.size - options.lineWidth) / 2;
var drawCircle = function(color, lineWidth, percent) {
percent = Math.min(Math.max(0, percent || 1), 1);
ctx.beginPath();
ctx.arc(0, 0, radius, 0, Math.PI * 2 * percent, false);
ctx.strokeStyle = color;
ctx.lineCap = 'round'; // butt, round or square
ctx.lineWidth = lineWidth
ctx.stroke();
};
drawCircle('#efefef', options.lineWidth, 100 / 100);
drawCircle('#555555', options.lineWidth, options.percent / 100);
div {
position:relative;
margin:80px;
width:220px; height:220px;
}
canvas {
display: block;
position:absolute;
top:0;
left:0;
}
span {
color:#555;
display:block;
line-height:220px;
text-align:center;
width:220px;
font-family:sans-serif;
font-size:40px;
font-weight:100;
margin-left:5px;
}
input {
width: 200px;
}
<div class="chart" id="graph" data-percent="10"></div>
To get this:
outer circle is just a progress from 0 to 100%
inner - timer
I did this
but through the ass
http://codepen.io/di3orlive/pen/wKjBzY
I would like to do it more nicer
and with an arrow

var el = document.getElementById('graph'); // get canvas
var options = {
percent: el.getAttribute('data-percent') || 25,
size: el.getAttribute('data-size') || 220,
lineWidth: el.getAttribute('data-line') || 4,
rotate: el.getAttribute('data-rotate') || 0
}
function Gauge() {
this.to_rad = to_rad = Math.PI / 180;
this.percentBg = "#efefef";
this.percentFg = "#555555";
this.tickFg = "#cccccc";
this.tickBg = "transparent";
this.tickFill = true;
this.tickDirection = -1; // 1=clockwise, -1=counterclockewise
this.percent = 0;
this.size = 220;
this.lineWidth = 14;
this.rotate = 0;
this.ticks = 40;
this.tick = 0;
this.can = document.createElement('canvas');
this.ctx = this.can.getContext('2d');
}
Gauge.prototype.drawCircle = function(color, lineWidth, percent, drawArrow) {
var ctx = this.ctx;
var circleMargin = 10; // need room for arrowhead
var radius = (this.size - this.lineWidth - circleMargin) / 2;
ctx.save();
var mid = this.size / 2;
ctx.translate(mid, mid);
ctx.rotate(-90 * to_rad);
percent = Math.min(Math.max(0, percent || 1), 1);
ctx.beginPath();
var endRadians = 360 * percent * this.to_rad;
ctx.arc(0, 0, radius, 0, endRadians, false);
ctx.strokeStyle = ctx.filStyle = color;
ctx.lineCap = 'round'; // butt, round or square
ctx.lineWidth = lineWidth
ctx.stroke();
if (drawArrow === true && percent !== 1) {
ctx.beginPath();
ctx.rotate(endRadians);
var arrowWidth = this.lineWidth + 12;
var arrowHeight = 10;
ctx.moveTo(radius - (arrowWidth / 2), 0);
ctx.lineTo(radius + (arrowWidth / 2), 0);
ctx.lineTo(radius, arrowHeight);
ctx.fill();
}
ctx.restore();
};
Gauge.prototype.drawTicks = function() {
var circleMargin = 10; // need room for arrowhead
var radius = (this.size - this.lineWidth - circleMargin) / 2;
var ctx = this.ctx;
ctx.save();
var mid = this.size / 2;
ctx.translate(mid, mid);
ctx.rotate(-90 * to_rad);
ctx.lineWidth = 3;
var angle = 360 / this.ticks;
var tickSize = 20;
var tickMargin = 10;
for (var i = 0; i < this.ticks; i++) {
if ((i < this.tick && this.tickFill == true) || i == this.tick) {
ctx.strokeStyle = this.tickFg;
} else {
ctx.strokeStyle = this.tickBg;
}
ctx.save();
if (this.tickDirection === -1) {
ctx.rotate((360 - (i * angle)) * this.to_rad);
} else {
ctx.rotate(i * angle * this.to_rad);
}
ctx.beginPath();
ctx.moveTo(radius - tickSize - tickMargin, 0);
ctx.lineTo(radius - tickMargin, 0);
ctx.stroke();
ctx.restore();
}
ctx.restore();
};
Gauge.prototype.render = function(el) {
this.can.width = this.can.height = this.size;
this.span = document.createElement('span');
el.innerHTML = "";
el.appendChild(this.can);
el.appendChild(this.span);
var self = this;
var ctx = self.ctx;
function loop() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
self.drawCircle(self.percentBg, self.lineWidth, 100 / 100);
self.drawCircle(self.percentFg, self.lineWidth, self.percent / 100, true);
self.drawTicks();
self.timeout = setTimeout(function() {
loop()
}, 1000 / 30);
}
loop();
}
var myGauge = new Gauge();
myGauge.size = options.size;
myGauge.percent = options.percent;
myGauge.lineWidth = options.lineWidth;
myGauge.percent = options.percent;
myGauge.render(el)
var myGauge2 = new Gauge();
myGauge2.size = options.size;
myGauge2.percent = options.percent;
myGauge2.lineWidth = options.lineWidth;
myGauge2.percent = options.percent;
myGauge2.tickFg = "#FF8800";
myGauge2.tickBg = "#EEEEEE";
myGauge2.tickFill = false;
myGauge2.ticks = 60;
myGauge2.tickDirection = 1;
myGauge2.render(document.getElementById('gauge'));
var startTime = (new Date()).getTime();
var countDown = 99;
function dataLoop() {
myGauge.percent = myGauge.percent > 100 ? 100 : (myGauge.percent * 1) + .1;
var elapsedMs = (new Date().getTime()) - startTime; // milliseconds;
var elapsedSec = elapsedMs / 1000;
var remainSec = Math.floor(countDown - elapsedSec);
var progress = remainSec <=0 ? 1 : elapsedSec / countDown;
myGauge.tick = Math.floor(progress * myGauge.ticks);
myGauge.span.innerHTML = remainSec > 0 ? remainSec + " sec" : "---";
var d = new Date();
myGauge2.percent = (d.getMinutes() / 60) * 100;
if (myGauge2.percent > 100) myGauge2.percent = 100;
myGauge2.tick = d.getSeconds();
myGauge2.span.innerHTML = d.getSeconds() + " sec";
setTimeout(dataLoop,1000/30);
}
dataLoop();
div {
position: relative;
margin: 80px;
width: 220px;
height: 220px;
}
canvas {
display: block;
position: absolute;
top: 0;
left: 0;
}
span {
color: #555;
display: block;
line-height: 220px;
text-align: center;
width: 100%;
font-family: sans-serif;
font-size: 40px;
font-weight: 100;
margin-left: 5px;
}
input {
width: 200px;
}
<table>
<tr>
<td>
<div class="chart" id="graph" data-percent="10"></div>
</td>
<td>
<div id="gauge"></div>
</td>
</tr>
</table>

Related

Canvas particle animation mobile touch event with elements positioned on top

I have this canvas particle animation that follows the mouse event correctly but it does not follow a touch event on mobile. I realized that it's because I have this animation beneath the rest of the content which is positioned on top. When there is something on top of the canvas that it's hitting, it does not trigger the touch event. I'm hoping someone can help me figure out how to avoid this issue so that the animation will still follow the user underneath the page content.
JS
var PI2 = Math.PI * 2;
var HALF_PI = Math.PI / 2;
var isTouch = 'ontouchstart' in window;
var isSafari = !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/);
function Canvas(options) {
options = _.clone(options || {});
this.options = _.defaults(options, this.options);
this.el = this.options.el;
this.ctx = this.el.getContext('2d');
this.dpr = window.devicePixelRatio || 1;
this.updateDimensions();
window.addEventListener('resize', this.updateDimensions.bind(this), false);
this.resetTarget();
if(isTouch){
// touch
this.el.addEventListener('touchstart', this.touchMove.bind(this), false);
this.el.addEventListener('touchmove', this.touchMove.bind(this), false);
// this.el.addEventListener('touchend', this.resetTarget.bind(this), false);
} else {
// Mouse
window.addEventListener('mousemove', this.mouseMove.bind(this), false);
window.addEventListener('mouseout', this.resetTarget.bind(this), false);
}
this.setupParticles();
this.loop();
}
Canvas.prototype.updateDimensions = function() {
this.width = this.el.width = _.result(this.options, 'width') * this.dpr;
this.height = this.el.height = _.result(this.options, 'height') * this.dpr;
this.el.style.width = _.result(this.options, 'width') + 'px';
this.el.style.height = _.result(this.options, 'height') + 'px';
}
// Update the orb target
Canvas.prototype.mouseMove = function(event) {
this.target = new Vector(event.clientX * this.dpr, event.clientY* this.dpr);
}
// Reset to center when we mouse out
Canvas.prototype.resetTarget = function() {
this.target = new Vector(this.width / 2, this.height /2);
}
// Touch Eent
Canvas.prototype.touchMove = function(event) {
if(event.touches.length === 1) { event.preventDefault(); }
this.target = new Vector(event.touches[0].pageX * this.dpr, event.touches[0].pageY * this.dpr);
}
// Defaults
Canvas.prototype.options = {
count: 11,
speed: 0.001,
width: 400,
height: 400,
size: 5,
radius: 1,
background: '240, 240, 240, 0.6',
maxDistance: 100
}
Canvas.prototype.setupParticles = function() {
this.particles = [];
var index = -1;
var between = PI2 / this.options.count;
while(++index < this.options.count) {
var x;
var y;
var angle;
var max = Math.max(this.width, this.height);
angle = (index + 1) * between;
x = Math.cos(angle) * max;
x += this.width / 2;
y = Math.sin(angle) * max;
y += this.height / 2;
var particle = new Particle({
x: x,
y: y,
radius: this.options.radius,
size: this.options.size,
angle: angle,
color: this.options.color
});
this.particles.push(particle);
}
}
Canvas.prototype.findClosest = function() {
var index = -1;
var pointsLength = this.particles.length;
while(++index < pointsLength) {
var closestIndex = -1;
this.particles[index].closest = [];
while(++closestIndex < pointsLength) {
var closest = this.particles[closestIndex];
var distance = this.particles[index].position.distanceTo(closest.position);
if(distance < this.options.maxDistance) {
var vector = new Vector(closest.position.x, closest.position.y);
vector.opacity = 1 - (distance / this.options.maxDistance);
vector.distance = distance;
this.particles[index].closest.push(vector);
}
}
}
}
Canvas.prototype.loop = function() {
// this.clear();
if(isTouch || isSafari) {
this.ghost();
} else {
this.ghostGradient();
}
if(this.options.maxDistance > 0) {
this.findClosest();
}
this.draw();
window.requestAnimationFrame(_.bind(this.loop, this));
}
Canvas.prototype.clear = function() {
this.ctx.clearRect(0, 0 , this.width, this.height);
}
Canvas.prototype.ghost = function() {
this.ctx.globalCompositeOperation = "source-over";
this.ctx.rect(0, 0 , this.width, this.height);
if(typeof this.options.background === 'string') {
this.ctx.fillStyle = "rgba(" + this.options.background + ")";
} else {
this.ctx.fillStyle = "rgba(" + this.options.background[0] + ")";
}
this.ctx.fill();
}
Canvas.prototype.ghostGradient = function() {
var gradient;
if(typeof this.options.background === 'string') {
this.ctx.fillStyle = 'rgba(' + this.options.background + ')';
} else {
var gradient = this.ctx.createLinearGradient(0, 0, 0, this.height);
var length = this.options.background.length;
for(var i = 0; i < length; i++){
gradient.addColorStop((i+1) / length, 'rgba(' + this.options.background[i] + ')');
}
this.ctx.fillStyle = gradient;
}
this.ctx.globalOpacity = 0.1;
this.ctx.globalCompositeOperation = "darken";
this.ctx.fillRect(0, 0 , this.width, this.height);
}
// Draw
Canvas.prototype.draw = function() {
var index = -1;
var length = this.particles.length;
while(++index < length) {
var point = this.particles[index];
var color = point.color || this.options.color;
point.update(this.target, index);
this.ctx.globalAlpha = 0.3;
this.ctx.globalCompositeOperation = "lighten";
this.ctx.fillStyle = 'rgb(' + color + ')';
this.ctx.beginPath();
this.ctx.arc(point.position.x, point.position.y, point.size, 0, PI2, false);
this.ctx.closePath();
this.ctx.fill();
if(this.options.maxDistance > 0) {
this.drawLines(point, color);
}
}
}
// Draw connecting lines
Canvas.prototype.drawLines = function (point, color) {
color = color || this.options.color;
var index = -1;
var length = point.closest.length;
this.ctx.globalAlpha = 0.2;
this.ctx.globalCompositeOperation = "screen";
this.ctx.lineCap = 'round';
while(++index < length) {
this.ctx.lineWidth = (point.size * 2) * point.closest[index].opacity;
this.ctx.strokeStyle = 'rgba(250,250,250, ' + point.closest[index].opacity + ')';
this.ctx.beginPath();
this.ctx.moveTo(point.position.x, point.position.y);
this.ctx.lineTo(point.closest[index].x, point.closest[index].y);
this.ctx.stroke();
}
}
function Particle(options) {
options = _.clone(options || {});
this.options = _.defaults(options, this.options);
this.position = this.shift = new Vector(this.options.x, this.options.y);
this.speed = this.options.speed || 0.01 + Math.random() * 0.04;
this.angle = this.options.angle || 0;
if(this.options.color) {
var color = this.options.color.split(',');
var colorIndex = -1;
while(++colorIndex < 3) {
color[colorIndex] = Math.round(parseInt(color[colorIndex], 10) + (Math.random()*100)-50);
// Clamp
color[colorIndex] = Math.min(color[colorIndex], 255);
color[colorIndex] = Math.max(color[colorIndex], 0);
}
this.color = color.join(', ');
}
// Size
this.options.size = this.options.size || 7;
this.size = 1 + Math.random() * this.options.size;
this.targetSize = this.options.targetSize || this.options.size;
this.orbit = this.options.radius * 0.5 + (this.options.radius * 0.5 * Math.random());
}
Particle.prototype.update = function(target, index) {
this.angle += this.speed;
this.shift.x += (target.x - this.shift.x) * this.speed;
this.shift.y += (target.y - this.shift.y) * this.speed;
this.position.x = this.shift.x + Math.cos(index + this.angle) * this.orbit;
this.position.y = this.shift.y + Math.sin(index + this.angle) * this.orbit;
if(!isSafari) {
this.size += (this.targetSize - this.size) * 0.03;
if(Math.round(this.size) === Math.round(this.targetSize)) {
this.targetSize = 1 + Math.random() * this.options.size;
}
}
}
function Vector(x, y) {
this.x = x || 0;
this.y = y || 0;
}
Vector.prototype.distanceTo = function(vector, abs) {
var distance = Math.sqrt(Math.pow(this.x - vector.x, 2) + Math.pow(this.y - vector.y, 2));
return abs || false ? Math.abs(distance) : distance;
};
new Canvas({
el: document.getElementById('canvas'),
count: 25,
speed: 0.3,
radius: 6,
width: function() { return window.innerWidth; },
height: function() { return window.innerHeight; },
size: 15,
color: '30, 180, 1',
maxDistance: 100,
background: ['250,250,250,1', '215,216,215,0.8']
})
CSS
html, body {
min-height: 100%;
height: 100%;
}
#canvas {
width: 100%;
height: 100%;
position: fixed;
z-index: 100;
}
.page-content {
position: relative;
z-index: 900;
display: flex;
flex-direction: column;
}
HTML
<body>
<div id="container">
<canvas id="canvas"></canvas>
<div class="page-content">
</div>
</div>
</body>

Moving an Object according to mouse coordinates

How do I get the following script to work? I wanted to be able to move the ship to the mouse coordinates. The space itself should move and the ship should stay in the centerview.
Would be great if you can modify the script as everybody writes his functions different.
Finally if the button is clicked I want the ship to move to that destination
I tried it when it worked and the ship never moved and I don't know if it has to do with the prototype.update function or not.
Is prototype.update or .render the same as if I would write this.update or this.render?
<script>
function domanDown(evt)
{
switch (evt)
{
case 38: /* Up arrow was pressed or button pressed*/
offsetY+=100 ;
break;
case 40: /* Down arrow was pressed or button pressed*/
offsetY-=100;
break;
case 37: /* Left arrow was pressedor button pressed*/
offsetX+=100;
break;
case 39: /* Right arrow was pressed or button pressed*/
offsetX-=100;
break;
}
}
window.requestAnimationFrame = function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function(f) {
window.setTimeout(f,1e3/60);
}
}();
var Obj = function (x,y,sp,size){
this.size = size;
this.x = x;
this.y = y;
this.color = sp;
this.selected = 0;
}
var Planet_Class = function (x,y,sp){
this.type = 'Planet_Class';
this.depot = 11;
this.xtype = new Obj(x,y,sp,10);
w.objects.push(this);
Planet_Class.prototype.update =function () {
}
Planet_Class.prototype.render =function () {
ctx.save();
ctx.beginPath();
ctx.fillStyle = this.xtype.color;
ctx.fillRect(this.xtype.x, this.xtype.y,this.xtype.size,this.xtype.size);
ctx.fill();
ctx.restore();
}
}
var Usership = function(x,y,sp){
this.depot = 10;
this.type = 'Usership';
this.xtype = new Obj(x,y,sp,10);
w.objects.push(this);
Usership.prototype.update =function (x,y) {
this.xtype.x = x || 20;
this.xtype.y = y || 20;
}
Usership.prototype.render =function () {
ctx.save();
ctx.beginPath();
ctx.fillStyle = this.xtype.color;
ctx.fillRect(this.xtype.x, this.xtype.y,this.xtype.size,this.xtype.size);
ctx.fill();
ctx.restore();
}
}
var World = function(){
this.objects = new Array();
this.count = function(type,sp){
var cnt = 0;
for(var k = 0;k<this.objects.length;k++)
cnt++;
return cnt;
}
}
renderWorld = function(){
requestAnimationFrame(renderWorld);
var spliceArray = Array();
ctx.beginPath();
objcnt = w.objects.length;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "white";
ctx.fill();
i = 0;
while(i < objcnt){
w.objects[i].update();
w.objects[i].render();
if(w.objects[i].depot < 1)
spliceArray.push(i);
i++;
}
for(var k = 0;k<spliceArray.length;k++)
{
w.objects.splice(spliceArray[k],1); }
}
function r1(max,min){
return (Math.floor(Math.random() * (max - min)) + 1);
}
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d"),
width = 1024,
height = 768;
offsetX = (canvas.width/2)-300; /Startpoint x for the Ship
offsetY = (canvas.height/2)-300; /Startpoint y for the Ship
generateshipx = -offsetX + (canvas.width/2);
generateshipy = -offsetY + (canvas.height/2);
mX = generateshipx;
mY = generateshipy;
w = new World();
new Usership(generateshipx,generateshipy,'green');
for(i=1;i<4;i++){
new Planet_Class(r1(600,2),r1(600,2),'red');
}
canvas.addEventListener("click", function (e) {
mX = e.pageX- canvas.offsetLeft -offsetX) ;
mY = e.pageY - canvas.offsetTop -offsetY) ;
});
renderWorld();
</script>
<html>
<head>
</head>
<body style="background-color:black;">
<canvas style="z-index:1" width="1024" height="768" id="canvas"></canvas>
<input type="button" style="z-index:2; position:absolute; top:300; left:10" value="uo" onCLick="domanDown(38)()">
<input type="button" style="z-index:2; position:absolute; top:340; left:10" value="down" onCLick="domanDown(40)">
<input type="button" style="z-index:2; position:absolute; top:380; left:10" value="left" onCLick="domanDown(37)">
<input type="button" style="z-index:2; position:absolute; top:420; left:10" value="right" onCLick="domanDown(39)">
<input type="button" style="z-index:2; position:absolute; top:460; left:10" value="move" onCLick="moveObj()">
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
position: absolute;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
cursor: crosshair;
border: solid 1px white;
border-radius: 10px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
var canvasWidth = 180;
var canvasHeight = 160;
var canvas = null;
var ctx = null;
var bounds = null;
var isRotating = false;
var isMoving = false;
var pan = {
x: 0.0,
y: 0.0
};
var target = {
x: 0.0,
y: 0.0,
dist: 0.0
};
var ship = {
x: (canvasWidth * 0.5)|0,
y: (canvasHeight * 0.5)|0,
width: 10.0,
height: 10.0,
rotation: 0.0,
deltaRotation: 0.0
};
var stars = [];
stars.length = 100;
window.onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx = canvas.getContext("2d");
bounds = canvas.getBoundingClientRect();
for (var i = 0; i < stars.length; ++i) {
stars[i] = {
x: (Math.random() * 4 * canvasWidth - 2 * canvasWidth)|0,
y: (Math.random() * 4 * canvasHeight - 2 * canvasHeight)|0
};
}
loop();
}
window.onmousedown = function(e) {
isRotating = true;
isMoving = false;
target.x = pan.x + e.clientX - bounds.left;
target.y = pan.y + e.clientY - bounds.top;
}
function loop() {
// Tick
// Rotate to face target
if (isRotating) {
// Vector creation
var tX = ship.x - target.x;
var tY = ship.y - target.y;
var tL = Math.sqrt(tX**2 + tY**2);
var fX = Math.sin(ship.rotation);
var fY = -Math.cos(ship.rotation);
var sX = Math.sin(ship.rotation + Math.PI * 0.5);
var sY = -Math.cos(ship.rotation + Math.PI * 0.5);
// Normalization
tX = tX / tL;
tY = tY / tL;
// Left or right?
var a = 1.0 - Math.abs(tX * fX + tY * fY);
ship.rotation += 0.075 * (sX * tX + sY * tY < 0.0 ? 1.0 : -1.0);
if (a < 0.01) {
target.dist = tL;
isRotating = false;
isMoving = true;
}
}
// Accelerate to target
if (isMoving) {
ship.x += Math.sin(ship.rotation) * 1.0;
ship.y -= Math.cos(ship.rotation) * 1.0;
if (--target.dist < 0.0) {
isMoving = false;
}
}
// Render
// Update pan coordinates
pan.x = ship.x - canvasWidth * 0.5;
pan.y = ship.y - canvasHeight * 0.5;
// Draw background
ctx.fillStyle = "#222222";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
ctx.fillStyle = "white";
for (var i = 0; i < stars.length; ++i) {
ctx.fillRect(
stars[i].x - pan.x,
stars[i].y - pan.y,
2,
2
);
}
// Draw ship
ctx.strokeStyle = "white";
ctx.fillStyle = "darkred";
ctx.translate(canvasWidth * 0.5,canvasHeight * 0.5);
ctx.rotate(ship.rotation);
ctx.beginPath();
ctx.moveTo(-ship.width * 0.5,ship.height * 0.5);
ctx.lineTo(ship.width * 0.5,ship.height * 0.5);
ctx.lineTo(0.0,-ship.height * 0.5);
ctx.lineTo(-ship.width * 0.5,ship.height * 0.5);
ctx.fill();
ctx.stroke();
ctx.rotate(-ship.rotation);
ctx.translate(-canvasWidth * 0.5,-canvasHeight * 0.5);
requestAnimationFrame(loop);
}
</script>
</body>
</html>

Resizing a <canvas> Element

I have the following canvas. I'm trying to make it smaller using the width and height attributes, but it doesn't work. This is my code:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="canvas" width="200" height="50"></canvas>
<script>
//set the variables
var a = document.getElementById('canvas'),
c = a.getContext('2d'),
w = a.width = window.innerWidth,
h = a.height = window.innerHeight,
area = w * h,
particleNum = 300,
ANIMATION;
var particles = [];
//create the particles
function Particle(i) {
this.id = i;
this.hue = rand(50, 0, 1);
this.active = false;
}
Particle.prototype.build = function() {
this.x = w / 2;
this.y = h / 2;
this.r = rand(7, 2, 1);
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
this.gravity = .01;
this.opacity = Math.random() + .5;
this.active = true;
c.beginPath();
c.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
c.fillStyle = "hsla(" + this.hue + ",100%,50%,1)";
c.fill();
};
Particle.prototype.draw = function() {
this.active = true;
this.x += this.vx;
this.y += this.vy;
this.vy += this.gravity;
this.hue -= 0.5;
this.r = Math.abs(this.r - .05);
c.beginPath();
c.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
c.fillStyle = "hsla(" + this.hue + ",100%,50%,1)";
c.fill();
// reset particle
if(this.r <= .05) {
this.active = false;
}
};
//functionality
function drawScene() {
c.fillStyle = "black";
c.fillRect(0,0,w,h);
for(var i = 0; i < particles.length; i++) {
if(particles[i].active === true) {
particles[i].draw();
} else {
particles[i].build();
}
}
ANIMATION = requestAnimationFrame(drawScene);
}
function initCanvas() {
var s = getComputedStyle(a);
if(particles.length) {
particles = [];
cancelAnimationFrame(ANIMATION);
ANIMATION;
console.log(ANIMATION);
}
w = a.width = window.innerWidth;
h = a.height = window.innerHeight;
for(var i = 0; i < particleNum; i++) {
particles.push(new Particle(i));
}
drawScene();
console.log(ANIMATION);
}
//init
(function() {
initCanvas();
addEventListener('resize', initCanvas, false);
})();
//helper functions
function rand(max, min, _int) {
var max = (max === 0 || max)?max:1,
min = min || 0,
gen = min + (max - min) * Math.random();
return (_int) ? Math.round(gen) : gen;
};
</script>
</body>
</html>
Pay more attention here:
var a = document.getElementById('canvas'),
c = a.getContext('2d'),
w = a.width = window.innerWidth,
h = a.height = window.innerHeight,
area = w * h,
As you can see, w is the width of the window and h is the height of the window. Try to edit w and h variable with custom values and tell me if it worked ;)
EDIT:
Simply replace h and w with this in your script tag:
a.style.width = '500px'; //random value, insert custom here
a.style.height = '500px';

Canvas arc on Retina is too sharp at some points

I am trying to create animated arc that doesn't look blurry on HiDPI devices.
This is how my arc looks on iPhone 5s:
you can see that near 0, 90, 180 deg arc becomes too sharp. How can I prevent this?
This is my code:
// Canvas arc progress
const can = document.getElementById('canvas');
const ctx = can.getContext('2d');
const circ = Math.PI * 2;
const quart = Math.PI / 2;
const canvasSize = can.offsetWidth;
const halfCanvasSize = canvasSize / 2;
let start = 0,
finish = 70,
animRequestId = null;
// Get pixel ratio
const ratio = (function() {
const dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
return dpr / bsr;
})();
// Set canvas h & w
can.width = can.height = canvasSize * ratio;
can.style.width = can.style.height = canvasSize + 'px';
ctx.scale(ratio, ratio)
ctx.beginPath();
ctx.strokeStyle = 'rgb(120,159,194)';
ctx.lineCap = 'square';
ctx.lineWidth = 8.0;
ctx.arc(halfCanvasSize, halfCanvasSize, halfCanvasSize - 4, 0, circ, false);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.strokeStyle = 'rgb(244,247,255)';
ctx.lineCap = 'round';
ctx.lineWidth = 8.0;
ctx.closePath();
let imd = ctx.getImageData(0, 0, canvasSize, canvasSize);
const draw = (current) => {
ctx.putImageData(imd, 0, 0);
ctx.beginPath();
ctx.arc(halfCanvasSize, halfCanvasSize, halfCanvasSize - 4, -(quart), ((circ) * current) - quart, false);
ctx.stroke();
};
(function animateArcProgress() {
animRequestId = requestAnimationFrame(animateArcProgress);
if (start <= finish) {
draw(start / 100);
start += 2;
} else {
cancelAnimationFrame(animRequestId);
}
})();
body {
margin: 0;
}
div {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
widht: 300px;
background: #85b1d7;
}
canvas {
height: 250px;
width: 250px;
}
<div>
<canvas id='canvas'></canvas>
</div>
You can soften the edge by drawing 1/2 pixel inside as done in your code below.
I rendered the arc 3 times at 8, 7.5 and 7 pixel width width alpha colour values 0.25, 0.5 and 1 respectively.
You can make it as soft as you want.
BTW using putImageData is very slow, why not just render the background to another canvas and draw that via ctx.drawImage(offScreencanvas,0,0) that way you will use the GPU to render the background rather than the CPU via the graphics port bus.
I added a bit more code to show the different softening FX you can get and added a mouse zoom so you can see the pixels a little better.
const can = document.getElementById('canvas');
const can2 = document.createElement("canvas"); // off screen canvas
const can3 = document.createElement("canvas"); // off screen canvas
const ctx = can.getContext('2d');
const ctx2 = can2.getContext('2d');
const ctx3 = can3.getContext('2d');
const circ = Math.PI * 2;
const quart = Math.PI / 2;
const canvasSize = can.offsetWidth;
const halfCanvasSize = canvasSize / 2;
const mouse = {x : null, y : null};
can.addEventListener("mousemove",function(e){
var bounds = can.getBoundingClientRect();
mouse.x = e.clientX - bounds.left;
mouse.y = e.clientY - bounds.top;
});
let start = 0,
finish = 70,
animRequestId = null;
// Get pixel ratio
const ratio = (function() {
const dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
return dpr / bsr;
})();
// Set canvas h & w
can2.height = can3.height = can2.width = can3.width = can.width = can.height = canvasSize * ratio;
can.style.width = can.style.height = canvasSize + 'px';
ctx.scale(ratio, ratio)
ctx2.scale(ratio, ratio)
ctx3.scale(ratio, ratio)
ctx2.beginPath();
ctx2.strokeStyle = 'rgb(120,159,194)';
ctx2.lineCap = 'square';
ctx2.lineWidth = 8.0;
ctx2.arc(halfCanvasSize, halfCanvasSize, halfCanvasSize - 4, 0, circ, false);
ctx2.stroke();
ctx2.closePath();
ctx2.beginPath();
ctx2.strokeStyle = 'rgb(244,247,255)';
ctx2.lineCap = 'round';
ctx2.lineWidth = 8.0;
ctx2.closePath();
const draw = (current) => {
ctx3.clearRect(0,0,canvas.width,canvas.height);
ctx3.drawImage(can2,0,0);
var rad = halfCanvasSize - 4;
const drawArc = () => {
ctx3.beginPath();
ctx3.arc(halfCanvasSize, halfCanvasSize, rad, -(quart), ((circ) * current) - quart, false);
ctx3.stroke();
}
// draw soft
ctx3.strokeStyle = 'rgb(244,247,255)';
ctx3.lineWidth = 8.5;
ctx3.globalAlpha = 0.25;
drawArc();;
ctx3.lineWidth = 7.0;
ctx3.globalAlpha = 0.5;
drawArc();;
ctx3.lineWidth = 6.5;
ctx3.globalAlpha = 1;
drawArc();
// draw normal
rad -= 12;
ctx3.lineWidth = 8.0;
ctx3.globalAlpha = 1;
drawArc();;
// draw ultra soft
rad -= 12;
ctx3.strokeStyle = 'rgb(244,247,255)';
ctx3.lineWidth = 9.0;
ctx3.globalAlpha = 0.1;
drawArc();
ctx3.lineWidth = 8.0;
ctx3.globalAlpha = 0.2;
drawArc();;
ctx3.lineWidth = 7.5;
ctx3.globalAlpha = 0.5;
drawArc();
ctx3.lineWidth = 6;
ctx3.globalAlpha = 1;
drawArc();
};
const zoomW = 30;
const zoomAmount = 5;
const drawZoom = () => {
ctx.drawImage(can3,0,0);
var width = zoomW * zoomAmount;
var cx = mouse.x - width / 2;
var cy = mouse.y - width / 2;
var c1x = mouse.x - zoomW / 2;
var c1y = mouse.y - zoomW / 2;
ctx.strokeStyle = 'rgb(244,247,255)';
ctx.lineWidth = 4;
ctx.strokeRect(cx,cy,width,width);
ctx.clearRect(cx,cy,width,width);
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.drawImage(can3,c1x,c1y,zoomW,zoomW,cx,cy,width,width);
ctx.imageSmoothingEnabled = true;
ctx.mozImageSmoothingEnabled = true;
}
function keepUpdating(){
ctx.clearRect(0,0,can.width,can.height);
drawZoom();
requestAnimationFrame(keepUpdating);
}
(function animateArcProgress() {
ctx.clearRect(0,0,can.width,can.height);
draw(start / 100);
drawZoom();
if (start <= finish) {
start += 0.5;
requestAnimationFrame(animateArcProgress);
} else {
requestAnimationFrame(keepUpdating);
}
})();
body {
margin: 0;
}
div {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
widht: 300px;
background: #85b1d7;
}
canvas {
height: 250px;
width: 250px;
}
<div>
<canvas id='canvas'></canvas>
</div>

Javascript glitch effect, text is too big, how do I resize?

I've used some code from a Pen I found online to produce a glitch text effect. However, the text is too big for the page, and I don't understand how I can change it.
Here is how the text displays:
too big
Here is how I want it to display:
how I'd like it
Here is the html:
<div class="glitch">
<div id="wrapper">
<canvas id="stage"></canvas>
Css:
.glitch {
background: black;
overflow: hidden;
margin: 0;
padding: 0;
width: 100%;
}
#wrapper {
width: 100%;
padding: 0;
margin: 0 auto;
max-width: 100%;
/*overflow: hidden;*/
text-align: center;
}
img {
display: none;
}
canvas {
box-sizing: border-box;
padding: 0 40px 0 0;
margin: 20px auto 0;
text-align: center;
max-width: 100%;
}
JS:
(function (){
"use strict";
var textSize = 10;
var glitcher = {
init: function () {
setTimeout((function () {
this.canvas = document.getElementById('stage');
this.context = this.canvas.getContext('2d');
this.initOptions();
this.resize();
this.tick();
}).bind(this), 100);
},
initOptions: function () {
var gui = new dat.GUI(),
current = gui.addFolder('Current'),
controls = gui.addFolder('Controls');
this.width = document.documentElement.offsetWidth;
this.height = document.documentElement.offsetHeight;
this.textSize = Math.floor(this.width / 7);
// sets text size based on window size
if (this.textSize > this.height) {
this.textSize = Math.floor(this.height/1.5); }
// tries to make text fit if window is
// very wide, but not very tall
this.font = '900 ' + this.textSize + 'px "Josefin Sans"';
this.context.font = this.font;
this.text = "ROYAL ARCADE";
this.textWidth = (this.context.measureText(this.text)).width;
this.fps = 60;
this.channel = 0; // 0 = red, 1 = green, 2 = blue
this.compOp = 'lighter'; // CompositeOperation = lighter || darker || xor
this.phase = 0.0;
this.phaseStep = 0.05; //determines how often we will change channel and amplitude
this.amplitude = 0.0;
this.amplitudeBase = 2.0;
this.amplitudeRange = 2.0;
this.alphaMin = 0.8;
this.glitchAmplitude = 20.0;
this.glitchThreshold = 0.9;
this.scanlineBase = 40;
this.scanlineRange = 40;
this.scanlineShift = 15;
current.add(this, 'channel', 0, 2).listen();
current.add(this, 'phase', 0, 1).listen();
current.add(this, 'amplitude', 0, 5).listen();
// comment out below to hide ability to change text string
var text = controls.add(this, 'text');
text.onChange((function (){
this.textWidth = (this.context.measureText(this.text)).width;
}).bind(this));
// comment out above to hide ability to change text string
controls.add(this, 'fps', 1, 60);
controls.add(this, 'phaseStep', 0, 1);
controls.add(this, 'alphaMin', 0, 1);
controls.add(this, 'amplitudeBase', 0, 5);
controls.add(this, 'amplitudeRange', 0, 5);
controls.add(this, 'glitchAmplitude', 0, 100);
controls.add(this, 'glitchThreshold', 0, 1);
controls.open();
gui.close(); // start the control panel cloased
},
tick: function () {
setTimeout((function () {
this.phase += this.phaseStep;
if (this.phase > 1) {
this.phase = 0.0;
this.channel = (this.channel === 2) ? 0 : this.channel + 1;
this.amplitude = this.amplitudeBase + (this.amplitudeRange * Math.random());
}
this.render();
this.tick();
}).bind(this), 1000 / this.fps);
},
render: function () {
var x0 = this.amplitude * Math.sin((Math.PI * 2) * this.phase) >> 0,
x1, x2, x3;
if (Math.random() >= this.glitchThreshold) {
x0 *= this.glitchAmplitude;
}
x1 = this.width - this.textWidth >> 1;
x2 = x1 + x0;
x3 = x1 - x0;
this.context.clearRect(0, 0, this.width, this.height);
this.context.globalAlpha = this.alphaMin + ((1 - this.alphaMin) * Math.random());
switch (this.channel) {
case 0:
this.renderChannels(x1, x2, x3);
break;
case 1:
this.renderChannels(x2, x3, x1);
break;
case 2:
this.renderChannels(x3, x1, x2);
break;
}
this.renderScanline();
if (Math.floor(Math.random() * 2) > 1) {
this.renderScanline();
// renders a second scanline 50% of the time
}
},
renderChannels: function (x1, x2, x3) {
this.context.font = this.font;
this.context.fillStyle = "rgb(255,0,0)";
this.context.fillText(this.text, x1, this.height / 2);
this.context.globalCompositeOperation = this.compOp;
this.context.fillStyle = "rgb(0,255,0)";
this.context.fillText(this.text, x2, this.height / 2);
this.context.fillStyle = "rgb(0,0,255)";
this.context.fillText(this.text, x3, this.height / 2);
},
renderScanline: function () {
var y = this.height * Math.random() >> 0,
o = this.context.getImageData(0, y, this.width, 1),
d = o.data,
i = d.length,
s = this.scanlineBase + this.scanlineRange * Math.random() >> 0,
x = -this.scanlineShift + this.scanlineShift * 2 * Math.random() >> 0;
while (i-- > 0) {
d[i] += s;
}
this.context.putImageData(o, x, y);
},
resize: function () {
this.width = document.documentElement.offsetWidth;
//this.height = window.innerHeight;
this.height = document.documentElement.offsetHeight;
if (this.canvas) {
this.canvas.height = this.height;
//document.documentElement.offsetHeight;
this.canvas.width = this.width;
//document.documentElement.offsetWidth;
this.textSize = Math.floor(this.canvas.width / 7);
// RE-sets text size based on window size
if (this.textSize > this.height) {
this.textSize = Math.floor(this.canvas.height/1.5); }
// tries to make text fit if window is
// very wide, but not very tall
this.font = '900 ' + this.textSize + 'px "Josefin Sans"';
this.context.font = this.font;
}
}
};
document.onload = glitcher.init();
window.onresize = glitcher.resize();
// return;
// executes anonymous function onload
})();
Thanks to anyone who can help me
I couldn't figure it out but here's an example of the glitch effect and the problem. Maybe someone can see what's going on here.
(function() {
"use strict";
var textSize = 10;
var glitcher = {
init: function() {
setTimeout((function() {
this.canvas = document.getElementById('stage');
this.context = this.canvas.getContext('2d');
this.initOptions();
this.resize();
this.tick();
}).bind(this), 100);
},
initOptions: function() {
var gui = new dat.GUI(),
current = gui.addFolder('Current'),
controls = gui.addFolder('Controls');
this.width = document.documentElement.offsetWidth;
this.height = document.documentElement.offsetHeight;
this.textSize = Math.floor(this.width / 7);
// sets text size based on window size
if (this.textSize > this.height) {
this.textSize = Math.floor(this.height / 1.5);
}
// tries to make text fit if window is
// very wide, but not very tall
this.font = '900 ' + this.textSize + 'px "Josefin Sans"';
this.context.font = this.font;
this.text = "ROYAL ARCADE";
this.textWidth = (this.context.measureText(this.text)).width;
this.fps = 60;
this.channel = 0; // 0 = red, 1 = green, 2 = blue
this.compOp = 'lighter'; // CompositeOperation = lighter || darker || xor
this.phase = 0.0;
this.phaseStep = 0.05; //determines how often we will change channel and amplitude
this.amplitude = 0.0;
this.amplitudeBase = 2.0;
this.amplitudeRange = 2.0;
this.alphaMin = 0.8;
this.glitchAmplitude = 20.0;
this.glitchThreshold = 0.9;
this.scanlineBase = 40;
this.scanlineRange = 40;
this.scanlineShift = 15;
current.add(this, 'channel', 0, 2).listen();
current.add(this, 'phase', 0, 1).listen();
current.add(this, 'amplitude', 0, 5).listen();
// comment out below to hide ability to change text string
var text = controls.add(this, 'text');
text.onChange((function() {
this.textWidth = (this.context.measureText(this.text)).width;
}).bind(this));
// comment out above to hide ability to change text string
controls.add(this, 'fps', 1, 60);
controls.add(this, 'phaseStep', 0, 1);
controls.add(this, 'alphaMin', 0, 1);
controls.add(this, 'amplitudeBase', 0, 5);
controls.add(this, 'amplitudeRange', 0, 5);
controls.add(this, 'glitchAmplitude', 0, 100);
controls.add(this, 'glitchThreshold', 0, 1);
controls.open();
gui.close(); // start the control panel cloased
},
tick: function() {
setTimeout((function() {
this.phase += this.phaseStep;
if (this.phase > 1) {
this.phase = 0.0;
this.channel = (this.channel === 2) ? 0 : this.channel + 1;
this.amplitude = this.amplitudeBase + (this.amplitudeRange * Math.random());
}
this.render();
this.tick();
}).bind(this), 1000 / this.fps);
},
render: function() {
var x0 = this.amplitude * Math.sin((Math.PI * 2) * this.phase) >> 0,
x1, x2, x3;
if (Math.random() >= this.glitchThreshold) {
x0 *= this.glitchAmplitude;
}
x1 = this.width - this.textWidth >> 1;
x2 = x1 + x0;
x3 = x1 - x0;
this.context.clearRect(0, 0, this.width, this.height);
this.context.globalAlpha = this.alphaMin + ((1 - this.alphaMin) * Math.random());
switch (this.channel) {
case 0:
this.renderChannels(x1, x2, x3);
break;
case 1:
this.renderChannels(x2, x3, x1);
break;
case 2:
this.renderChannels(x3, x1, x2);
break;
}
this.renderScanline();
if (Math.floor(Math.random() * 2) > 1) {
this.renderScanline();
// renders a second scanline 50% of the time
}
},
renderChannels: function(x1, x2, x3) {
this.context.font = this.font;
this.context.fillStyle = "rgb(255,0,0)";
this.context.fillText(this.text, x1, this.height / 2);
this.context.globalCompositeOperation = this.compOp;
this.context.fillStyle = "rgb(0,255,0)";
this.context.fillText(this.text, x2, this.height / 2);
this.context.fillStyle = "rgb(0,0,255)";
this.context.fillText(this.text, x3, this.height / 2);
},
renderScanline: function() {
var y = this.height * Math.random() >> 0,
o = this.context.getImageData(0, y, this.width, 1),
d = o.data,
i = d.length,
s = this.scanlineBase + this.scanlineRange * Math.random() >> 0,
x = -this.scanlineShift + this.scanlineShift * 2 * Math.random() >> 0;
while (i-- > 0) {
d[i] += s;
}
this.context.putImageData(o, x, y);
},
resize: function() {
this.width = document.documentElement.offsetWidth;
//this.height = window.innerHeight;
this.height = document.documentElement.offsetHeight;
if (this.canvas) {
this.canvas.height = this.height;
//document.documentElement.offsetHeight;
this.canvas.width = this.width;
//document.documentElement.offsetWidth;
this.textSize = Math.floor(this.canvas.width / 7);
// RE-sets text size based on window size
if (this.textSize > this.height) {
this.textSize = Math.floor(this.canvas.height / 1.5);
}
// tries to make text fit if window is
// very wide, but not very tall
this.font = '900 ' + this.textSize + 'px "Josefin Sans"';
this.context.font = this.font;
}
}
};
//document.onload = glitcher.init;
glitcher.init();
window.onresize = glitcher.resize;
// return;
// executes anonymous function onload
})();
.glitch {
background: black;
overflow: hidden;
margin: 0;
padding: 0;
width: 100%;
}
#wrapper {
width: 100%;
padding: 0;
margin: 0 auto;
max-width: 100%;
/*overflow: hidden;*/
text-align: center;
}
img {
display: none;
}
canvas {
box-sizing: border-box;
padding: 0 40px 0 0;
margin: 20px auto 0;
text-align: center;
max-width: 100%;
}
<script type="text/javascript" src="https://raw.githubusercontent.com/dataarts/dat.gui/master/build/dat.gui.js"></script>
<div class="glitch">
<div id="wrapper">
<canvas id="stage"></canvas>
</div>
</div>

Categories

Resources