I am trying to generate a site with interactive elements and animations. I have came across this Fiddle which is perfect for what I need.
I want the circle to start animating when the canvas element is visible in the browser, or possibly when a certain scroll point has been reached (ie: > 1000 px in the Y direction- start animation).
I am completely new to JS, so have tried to piece together code from other sources with no luck. I have tried (in context with the Fiddle):
// requestAnimationFrame Shim
(function() {
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;
})();
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var endPercent = 85;
var curPerc = 0;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
context.lineWidth = 10;
context.strokeStyle = '#ad2323';
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 10;
context.shadowColor = '#656565';
function animate(current) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
context.stroke();
curPerc++;
if (curPerc < endPercent) {
requestAnimationFrame(function () {
animate(curPerc / 100)
});
}
}
// My altered code to the original
$(window).scroll(function() {
if ($('#myCanvas').is(':visible')) {
.animate();
}
});
But this is doing nothing. I would imagine this to be a relatively simple problem for those in the know. But I haven't a clue!
Look forward to your answers, apologies if I have missed anything!
You need to include Jquery in your fiddle. That was giving you an error if your going to try and use it for scroll() Also you dont need the . in front of animate(). Also give your body a height that you can scroll on your else youll never activate it. See my fiddle for the changes
http://jsfiddle.net/uhVj6/220/
call your animate(); in this code
$(document).ready(function () {
called = false;
$(window).scroll(function (e) {
if ($(window).scrollTop() > $("canvas").position().top && !called){
animate();
called = true;
}
});
});
http://jsfiddle.net/uhVj6/223/
Related
I am using requestAnimationFrame in my canvas animation, but I would like to delay the animation for 3 seconds before it starts. I have placed
setTimeout(draw(), 3000);
in many places in my code. Some noted in the comments in the code below:
MyApp.prototype._animation = function() {
var cvs = document.querySelector("#animation");
var ctx = cvs.getContext("2d");
var canvasWidth = cvs.width;
var canvasHeight = cvs.height;
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
var posX = 0;
var posY = 0;
// tried setTimeout here
function draw() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.fillRect(100+posX,0,7,canvasHeight); //pole
var instrument = new Path2D();
instrument.moveTo(65+posX,50+posY);
instrument.lineTo(100+posX,50+posY);
instrument.lineTo(100+posX, 10+posY);
instrument.lineTo(65+posX, 10+posY);
instrument.arc(65+posX,30+posY, 20, Math.PI/2, 3*Math.PI/2, false);
instrument.closePath();
var circle = new Path2D();
circle.arc(65+posX, 30+posY, 15, 0, 2 * Math.PI, true);
ctx.globalCompositeOperation = "xor";
ctx.fill(instrument);
ctx.fill(circle);
if (posY < 50){
posY += 1;
} else {
posX += 1;
};
if (posX > 200) {
return;
}
requestAnimationFrame(draw);
}
// tried setTimeout here
draw();
};
I am trying to figure out how to use setTimeout in this case in order to delay the start of the animation.
EDIT: I am trying to figure out WHERE to place setTimeout in this method in order to initially delay the animation.
At the bottom of your script where you call draw(), replace that with:
// Note: no parens in the draw
window.setTimeout(draw,3000);
I'm trying to smoothly resize an html5 canvas element. I've cobbled together bits of code and I think the following should work:
<body>
<header id='myheader' height='200'>
<canvas id='mycanvas' width='200' height='200'></canvas>
<script>
var mycanvas = document.getElementById('mycanvas');
var context = mycanvas.getContext('2d');
var centerX = mycanvas.width / 2;
var centerY = mycanvas.height / 2;
var radius = 70;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'blue';
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
var animate = function(prop, val, duration) {
var start = new Date().getTime();
var end = start + duration;
var current = mycanvas[prop];
var distance = val - current;
var step = function() {
var timestamp = new Date().getTime();
var progress = Math.min((duration - (end - timestamp)) / duration, 1);
mycanvas[prop] = current + (distance * progress);
if (progress < 1) requestAnimationFrame(step);
};
return step();
};
animate('mycanvas.height', 10, 1000);
</script>
</header>
</body>
...but obviously it doesn't! The result I'm looking for is a canvas that shrinks to just show the middle part of the circle (something more interesting than a circle will be added later). Is there anything obvious that I'm missing, or am I just doing this the wrong way? Ultimately I want to to resize both the canvas and the header together, so getting the canvas resizing to work is stage 1. Any help appreciated...
(Edit: actually, I ultimately want to resize both the canvas and header in response to a scroll event - which I think means avoiding a css solution - but I want to get this bit working first!)
Here are a few changes to your script that I believe do what you want:
var mycanvas = document.getElementById('mycanvas');
var context = mycanvas.getContext('2d');
var radius = 70;
function draw() {
var centerX = mycanvas.width / 2;
var centerY = mycanvas.height / 2;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'blue';
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
}
var animate = function(target, prop, val, duration, action) {
var start = new Date().getTime();
var end = start + duration;
var current = target[prop];
var distance = val - current;
var step = function() {
var timestamp = new Date().getTime();
var progress = Math.min((duration - (end - timestamp)) / duration, 1);
target[prop] = current + (distance * progress);
action();
if (progress < 1) requestAnimationFrame(step);
};
return step();
};
animate(mycanvas, 'height', 10, 1000, draw);
I would like to create a reusable JavaScript component out of the following canvas spinner. Never done this before. How to achieve it and how to use the component?
http://codepen.io/anon/pen/tkpqc
HTML:
<canvas id="spinner"></canvas>
JS:
var canvas = document.getElementById('spinner');
var context = canvas.getContext('2d');
var start = new Date();
var lines = 8,
cW = context.canvas.width,
cH = context.canvas.height;
centerX = canvas.width / 2;
centerY = canvas.height / 2;
radius = 20;
var draw = function() {
var rotation = parseInt(((new Date() - start) / 1000) * lines) % lines;
context.save();
context.clearRect(0, 0, cW, cH);
for (var i = 0; i < lines; i++) {
context.beginPath();
//context.rotate(Math.PI * 2 / lines);
var rot = 2*Math.PI/lines;
var space = 2*Math.PI/(lines * 12);
context.arc(centerX,centerY,radius,rot * (i) + space,rot * (i+1) - space);
if (i == rotation)
context.strokeStyle="#ED3000";
else
context.strokeStyle="#CDCDCD";
context.lineWidth=10;
context.stroke();
}
context.restore();
};
window.setInterval(draw, 1000 / 30);
EDIT - SOLUTION:
Here comes a solution if anybody is interested
http://codepen.io/anon/pen/tkpqc
There are any number of ways to do this. Javascript is an object oriented language so you can easily write like:
var Spinner = function(canvas_context)
{
this.context = canvas_context;
// Whatever other properties you needed to create
this.timer = false;
}
Spinner.prototype.draw = function()
{
// Draw spinner
}
Spinner.prototype.start = function()
{
this.timer = setInterval(this.start, 1000 / 30);
}
Spinner.prototype.stop = function() {
clearInterval(this.timer);
}
Now you can use this object like so:
var canvas = document.getElementById('#canvas');
var context = canvas.getContext('2d');
var spinner = new Spinner(context);
spinner.start();
Basically, you are creating a class whose sole purpose in life is to draw a spinner on a canvas. In this example, you'll note that you're passing in the canvas's context into the object, since the details of the canvas itself is not relevant to this class's interest.
I'm trying to animate a sine wave in JS but it's not acting as expected. I'm using a <canvas> element along with window.requestAnimationFrame() method but it's a CPU hog and as i change frequency with the slider it just break and show random waveforms. I also don't know if drawing adjacent lines is the best way to represent a sine wave. Please note that i'll use vanilla JS and that the sine's frequency and amplitude are variables set by sliders. Thanks in advance.
This is what i got so far: http://cssdeck.com/labs/8cq5vclp
UPDATE: i worked on it and this is the new version: http://cssdeck.com/labs/sbfynjkr
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
cHeight = canvas.height,
cWidth = canvas.width,
frequency = document.querySelector("#f").value,
amplitude = 80,
x = 0,
y = cHeight / 2,
point_y = 0;
window.onload = init;
function init() {
document.querySelector("#f").addEventListener("input", function() {
frequency = this.value;
document.querySelector("#output_f").value = frequency;
}, false);
drawSine();
}
function drawSine() {
ctx.clearRect(0, 0, cWidth, cHeight);
ctx.beginPath();
ctx.moveTo(0, y);
ctx.strokeStyle = "red";
ctx.lineTo(cWidth, y);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.strokeStyle = "black";
for (x = 0; x < 600; x++) {
point_y = amplitude * -Math.sin((frequency / 95.33) * x) + y;
ctx.lineTo(x, point_y);
}
ctx.stroke();
ctx.closePath();
requestAnimationFrame(drawSine);
}
canvas {
border: 1px solid red;
margin: 10px;
}
<input id="f" type="range" min="0" max="20000" value="20" step="1">
<output for="f" id="output_f">20</output>
<canvas width="600px" height="200px"></canvas>
I've messed around with sine waves quite a bit, because I'm working on a little project that involves animated sine waves. I've got some code you might be interested in taking a look at. Like mentioned earlier, you need to make sure you are using the right increment in your loop so the lines do not look jagged.
https://jsfiddle.net/uawLvymc/
window.requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(f) {
return setTimeout(f, 1000 / 60)
};
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var startTime = new Date().getTime();
function getPath(height) {
var width = canvas.width;
var spacing = 0.08;
var loopNum = 0;
var pointList = [];
var i = 0;
for (i = 0; i < width / 2; i++) {
pointList[loopNum] = [loopNum, Math.sin(loopNum * spacing) * (i * height) + 100];
loopNum++;
}
for (i = width / 2; i > 0; i--) {
pointList[loopNum] = [loopNum, Math.sin(loopNum * spacing) * (i * height) + 100];
loopNum++;
}
return pointList;
}
function draw() {
var currentTime = new Date().getTime();
var runTime = currentTime - startTime;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = "rgb(80, 100, 230)";
var height = Math.sin(runTime * 0.008) * 0.2;
var pointList = getPath(height);
for (var i = 0; i < 500; i++) {
if (i === 0) {
ctx.moveTo(pointList[0][0], pointList[0][1]);
} else {
ctx.lineTo(pointList[i][0], pointList[i][1]);
}
}
ctx.stroke();
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
Sorry I didn't really edit down the code, it's just a direct copy from what I was working on. Hope it helps though.
See if this example could help you a little
Sine Wave Example canvas
function init()
{
setInterval(OnDraw, 200);
}
var time = 0;
var color = "#ff0000";
function OnDraw()
{
time = time + 0.2;
var canvas = document.getElementById("mycanvas");
var dataLine = canvas.getContext("2d");
var value = document.getElementById("lineWidth");
dataLine.clearRect(0, 0, canvas.width, canvas.height);
dataLine.beginPath();
for(cnt = -1; cnt <= canvas.width; cnt++)
{
dataLine.lineTo(cnt, canvas.height * 0.5 - (Math.random() * 2 + Math.cos(time + cnt * 0.05) * 20 ));
}
dataLine.lineWidth = value.value * 0.1;
dataLine.strokeStyle = color;
dataLine.stroke();
}
basically what I'm trying to do is create a 'raining effect' on the canvas (doesn't exactly look like rain at the moment but I will sort that later)
This is my JavaScript so far:
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById("canvas");
cx = canvas.getContext("2d");
function rectangle (x, y, w, h) {
var randomx = Math.floor(Math.random() * canvas.width - 50);
var randomy = Math.floor(Math.random() * canvas.height - 100);
this.x = randomx || x || 0;
this.y = randomy || y || 0;
this.w = w || 0;
this.h = h || 0;
this.draw = function () {
cx.fillStyle = "blue";
cx.fillRect(this.x, this.y, this.w, this.h);
};
}
var myRectangle = new rectangle(window.randomx, window.randomy, 10, 10);
function loop () {
cx.clearRect(0, 0, canvas.width, canvas.height);
myRectangle.y++;
myRectangle.draw();
requestAnimFrame(loop);
}
loop();
Basically, it will create a 10 by 10 blue block at a random y and x point of the canvas, what I need to do is keep adding this blue block over and over onto the canvas. I tried including this for loop into the 'loop' function:
for (var i = 0; i < 20; i++) {
var myRectangle = new rectangle(window.randomx, window.randomy, 10, 10);
}
But this just keeps flashing the block at random points (I understand why, it is because it keeps overwriting the variable and placing it at a new point), would anyone be able to help me? I know it would be easier to use jQuery for this, but I'm using JavaScript only
Here is a fiddle for what it looks like at the moment (without the for loop) thanks in advance!
jsfiddle
Make an array of rectangles instead:
myRectangle[i] = new rectangle(...);
This way the previously generated ones won't get overwritten/destroyed.
Here's an example using an array. You also want to make sure you remove them when they're off the screen (note expired variable)
http://jsfiddle.net/8Jqzx/2/
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById("canvas");
cx = canvas.getContext("2d");
function rectangle (x, y, w, h) {
var randomx = Math.floor(Math.random() * canvas.width - 50);
var randomy = Math.floor(Math.random() * canvas.height - 100);
this.x = randomx || x || 0;
this.y = randomy || y || 0;
this.w = w || 0;
this.h = h || 0;
this.expired = false;
this.draw = function () {
cx.fillStyle = "blue";
cx.fillRect(this.x, this.y, this.w, this.h);
};
this.update = function () {
this.y++;
if (y > canvas.height) {
this.expired = true;
}
}
}
var rectangles = new Array();
function newRect () {
rectangles.push(new rectangle(window.randomx, window.randomy, 10, 10));
}
var timing = 0;
function loop () {
cx.clearRect(0, 0, canvas.width, canvas.height);
if (timing%10 == 0) {
newRect();
}
for (var i = 0; i < rectangles.length; i++) {
rectangles[i].update();
rectangles[i].draw();
if (rectangles[i].expired) {
rectangles.splice(i, 1);
i--;
}
}
timing++;
requestAnimFrame(loop);
}
loop();
But in this one you have them appear at the top (more rain-like): http://jsfiddle.net/8Jqzx/3/