i am trying to do an audio visualization with HTML canvas - html

i did implement the audio visualization but i am not happy with the results. i was hoping for a better solution than what i came up with. like for example is there a better way to draw the bars and also a better way to animate other than what i did which is changing the gradient.
this is the audio track before the play button is clicked
this is the audio track after play button is clicked
here is the code
window.addEventListener('load', init)
const canvas = document.querySelector('canvas')
const btn = document.querySelector('#play')
const ctx = canvas.getContext('2d')
const sample = [...] // this has a random height values between 0 and 24 px
function init() {
let barLength = 55
let barWidth = (canvas.width - (barLength - 1)) / barLength
let region = new Path2D()
for(let i = 0; i < sample.length; i++) {
let halfHeight = sample[i] / 2
region.rect(i * (barWidth + 1), (24 - halfHeight), barWidth, sample[i])
}
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
grd.addColorStop(0, "#ddd")
grd.addColorStop(1, "#ddd")
ctx.clip(region)
ctx.fillStyle = grd
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
let xPos = 0
function drawRect() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
let pos = xPos / canvas.width
pos = pos > 1.0 ? 1 : pos
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
grd.addColorStop(0, "orange")
grd.addColorStop(pos, "orange")
grd.addColorStop(pos, "#ddd")
grd.addColorStop(1, "#ddd")
ctx.fillStyle = grd
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
let prev
let requestId
function draw(sec) {
if (!prev)
prev = new Date()
let now = new Date()
let difference = now - prev
prev = now
xPos += (difference * canvas.width) / (duration * 1000)
drawRect()
requestId = requestAnimationFrame(draw)
if(xPos >= canvas.width) {
prev = null
cancelAnimationFrame(requestId)
}
}
btn.addEventListener('click', () => {
draw()
})

Your idea is pretty good.
The only thing I could think of is that you are currently redrawing the full image every frame, but that just changes the orange part.
What you could do instead is draw "holes" into the picture as to have the white bars be fully transparent. That way, we would see through the waves. The picture would then be in a div and its background would be changed to have the orange highlight.
This in no way changes the visual, it just reduces the computations needed for the same effect.
const canvas = document.querySelector('canvas')
const btn = document.querySelector('#play')
const ctx = canvas.getContext('2d')
const sample = []; // this has a random height values between 0 and 24 px
for(let i=0;i<55;i++){
sample.push(Math.random()*24);
}
function init() {
ctx.fillRect(0,0, canvas.width, canvas.height);
let barLength = 55
let barWidth = (canvas.width - (barLength - 1)) / barLength
let region = new Path2D()
for(let i = 0; i < sample.length; i++) {
let halfHeight = sample[i] / 2
region.rect(i * (barWidth + 1), (24 - halfHeight), barWidth, sample[i])
}
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
grd.addColorStop(0, "#ddd")
grd.addColorStop(1, "#ddd")
ctx.clip(region);
ctx.globalCompositeOperation = 'destination-atop';
ctx.globalAlpha = 0;
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
let xPos = 0
function drawRect() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
let pos = xPos / canvas.width
pos = pos > 1.0 ? 1 : pos
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
grd.addColorStop(0, "orange")
grd.addColorStop(pos, "orange")
grd.addColorStop(pos, "#ddd")
grd.addColorStop(1, "#ddd")
ctx.fillStyle = grd
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
init();
const div = document.querySelector('#div');
setInterval(function(){
xPos++;
const percent = xPos / canvas.width *100;
const t = `linear-gradient(90deg, rgba(255,100,0,1), rgba(255,100,0,1) ${percent}%, rgba(255,255,255,1) ${percent}%, rgba(255,255,255,1) 100%)`;
div.style.backgroundImage = t;
},100);
div{
background-color:white;
display:inline-block;
height:50px;
}
<div id="div"><canvas height="50"></canvas></div>

Related

Chrome Canvas: Small globalAlpha ignored

On Chromium 90.0.4430.93 (which uses the same rendering engine as Chrome), the following fading effect does not work. Why? And how would one fix it?
It is done by painting over the canvas with another black canvas and a low globalAlpha on each requestAnimationFrame call.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
let start;
let lastTime;
function draw() {
canvas.bgcanvas = document.createElement('canvas');
canvas.bgcanvas.width = canvas.width;
canvas.bgcanvas.height = canvas.height;
bgctx = canvas.bgcanvas.getContext('2d');
bgctx.fillStyle = '#000';
bgctx.fillRect(0, 0, canvas.width, canvas.height);
}
function step(timestamp) {
if (start === undefined)
start = timestamp;
lastTime = timestamp;
const elapsed = timestamp - lastTime;
lastTime = timestamp;
ctx.globalAlpha = 0.0001*elapsed;
ctx.drawImage(canvas.bgcanvas, 0, 0);
ctx.globalAlpha = 1;
var x = (timestamp - start)*0.01 % 200;
var y = 0;
ctx.beginPath();
ctx.fillStyle = '#77f';
ctx.arc(x, canvas.height/2-y, 7, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
window.requestAnimationFrame(step);
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
lines = [];
draw();
ctx.drawImage(canvas.bgcanvas, 0, 0);
}
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
window.requestAnimationFrame(step);
* { margin:0; padding:0; } /* to remove the top and left whitespace */
body {
width:100%; height:100%;
}
canvas {
display:block;
}
<canvas id="canvas"></canvas>
Result in Firefox:
Result in Chromium:
Thanks to Blindman67's comment, I now understand that it is non-trivial to get the same exponential decay shown in the above figure. But a linear one that works browser independently is relatively simple: Just subtract #010101 on each rendering step via the following code:
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = '#010101';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = "source-over";
To change the speed of decay, one can e.g. only apply this effect every i-th iteration.
Since I needed this fade with a background that is not pure black, but has other content, I created a luminosity mask on a new canvas (contentcanvas) that I then applied to the background. Here is an example showcasing the full solution:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
let start;
let lastTime;
let stepMod = 0;
function draw() {
canvas.bgcanvas = document.createElement('canvas');
canvas.bgcanvas.width = canvas.width;
canvas.bgcanvas.height = canvas.height;
bgctx = canvas.bgcanvas.getContext('2d');
canvas.contentcanvas = document.createElement('canvas');
canvas.contentcanvas.width = canvas.width;
canvas.contentcanvas.height = canvas.height;
canvas.contentctx = canvas.contentcanvas.getContext('2d');
canvas.tmpcanvas = document.createElement('canvas');
canvas.tmpcanvas.width = canvas.width;
canvas.tmpcanvas.height = canvas.height;
canvas.tmpctx = canvas.tmpcanvas.getContext('2d');
bgctx.fillStyle = '#000';
bgctx.fillRect(0, 0, canvas.width, canvas.height);
bgctx.font = "30px serif";
bgctx.fillStyle = '#ca3';
bgctx.fillText("Hey!", 10, canvas.height/2+10);
}
function step(timestamp) {
if (start === undefined)
start = timestamp;
lastTime = timestamp;
const elapsed = timestamp - lastTime;
lastTime = timestamp;
if (stepMod == 1) {
canvas.contentctx.globalCompositeOperation = "difference";
canvas.contentctx.fillStyle = '#010101';
canvas.contentctx.fillRect(0, 0, canvas.width, canvas.height);
canvas.contentctx.globalCompositeOperation = "source-over";
stepMod = 0;
}
else {
stepMod += 1;
}
var x = (timestamp - start)*0.01 % 200;
var y = 0;
canvas.contentctx.beginPath();
canvas.contentctx.fillStyle = '#fff';
canvas.contentctx.arc(x, canvas.height/2-y, 7, 0, Math.PI * 2, true);
canvas.contentctx.closePath();
canvas.contentctx.fill();
canvas.tmpctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.tmpctx.drawImage(canvas.contentcanvas, 0, 0);
canvas.tmpctx.globalCompositeOperation = "multiply";
canvas.tmpctx.fillStyle = '#77f';
canvas.tmpctx.fillRect(0, 0, canvas.width, canvas.height);
canvas.tmpctx.globalCompositeOperation = "source-over";
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = "difference";
ctx.drawImage(canvas.contentcanvas, 0, 0);
ctx.globalCompositeOperation = "multiply";
ctx.drawImage(canvas.bgcanvas, 0, 0);
ctx.globalCompositeOperation = "lighter";
ctx.drawImage(canvas.tmpcanvas, 0, 0);
ctx.globalCompositeOperation = "source-over";
window.requestAnimationFrame(step);
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
lines = [];
draw();
ctx.drawImage(canvas.bgcanvas, 0, 0);
}
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
window.requestAnimationFrame(step);
* { margin:0; padding:0; } /* to remove the top and left whitespace */
body {
width:100%; height:100%;
}
canvas {
display:block;
}
<canvas id="canvas"></canvas>

HTML5 Canvas - cant apply source-atop on mask

Sorry I am new to Canvas and dont know how to google this out. Problem is that I cant draw on mask if previous layer (night sky) is present.
Here are the two snippets:
const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const { width: w, height: h } = canvas;
// first layer
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = '#555';
let x, y, radius;
for (let i = 0; i < 550; i++) {
x = Math.random() * w;
y = Math.random() * h;
radius = Math.random() * 3;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
ctx.fill();
}
// destination
ctx.font = 'bold 70pt monospace';
ctx.fillStyle = 'black';
ctx.fillText('FOO', 10, 60);
ctx.fillText('BAR', 10, 118);
ctx.fill();
// source
ctx.globalCompositeOperation = 'source-atop';
for (let i = 0; i < 6; i++) {
ctx.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
ctx.fillRect(0, i * 20, 200, 20);
}
<div id="board">
<canvas width="640" height="480"></canvas>
</div>
EXPECTED RESULT (but with the first layer - night sky):
const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const { width: w, height: h } = canvas;
// destination
ctx.font = 'bold 70pt monospace';
ctx.fillStyle = 'black';
ctx.fillText('FOO', 10, 60);
ctx.fillText('BAR', 10, 118);
ctx.fill();
// source
ctx.globalCompositeOperation = 'source-atop';
for (let i = 0; i < 6; i++) {
ctx.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
ctx.fillRect(0, i * 20, 200, 20);
}
<div id="board">
<canvas width="640" height="480"></canvas>
</div>
Compositing will affect the whole context.
source-atop mode will draw only where there were existing pixels (i.e only where alpha > 0).
When you draw your background, all the pixels of your context have alpha values set to 1.
This means that source-atop will not produce anything on your fully opaque image.
Once you understand these points, it's clear that you need to make your compositing alone.
It could be e.g on a different off-screen canvas that you would then draw back on the main canvas with ctx.drawImage(canvas, x, y).
const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const {
width: w,
height: h
} = canvas;
// background
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = '#555';
let x, y, radius;
for (let i = 0; i < 550; i++) {
x = Math.random() * w;
y = Math.random() * h;
radius = Math.random() * 3;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
ctx.fill();
}
// text compositing on an off-screen context
const ctx2 = Object.assign(document.createElement('canvas'), {
width: 200,
height: 120
}).getContext('2d');
// text
ctx2.font = 'bold 70pt monospace';
ctx2.fillStyle = 'black';
ctx2.fillText('FOO', 10, 60);
ctx2.fillText('BAR', 10, 118);
ctx2.globalCompositeOperation = 'source-atop';
// rainbow
for (let i = 0; i < 6; i++) {
ctx2.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
ctx2.fillRect(0, i * 20, 200, 20);
}
// now draw our off-screen canvas on the main one
ctx.drawImage(ctx2.canvas, 0, 0);
<div id="board">
<canvas width="640" height="480"></canvas>
</div>
Or, since this is the only compositing in your composition, you can also do it all on the same, but use an other compositing mode: destination-over.
This mode will draw behind the existing content, this means that you will have to actually draw your background after you made the compositing.
const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const {
width: w,
height: h
} = canvas;
//
// text compositing on a clear context
drawText();
// will draw only where the text has been drawn
ctx.globalCompositeOperation = 'source-atop';
drawRainbow();
// from here we will draw behind
ctx.globalCompositeOperation = 'destination-over';
// so we need to first draw the stars, otherwise they'll be behind
drawStars();
//And finally the sky black background
drawSky();
//... reset
ctx.globalCompositeOperation = 'source-over';
function drawSky() {
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
}
function drawStars() {
ctx.fillStyle = '#555';
let x, y, radius;
for (let i = 0; i < 550; i++) {
x = Math.random() * w;
y = Math.random() * h;
radius = Math.random() * 3;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
ctx.fill();
}
}
function drawText()  {
ctx.font = 'bold 70pt monospace';
ctx.fillStyle = 'black';
ctx.fillText('FOO', 10, 60);
ctx.fillText('BAR', 10, 118);
}
function drawRainbow() {
for (let i = 0; i < 6; i++) {
ctx.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
ctx.fillRect(0, i * 20, 200, 20);
}
}
<div id="board">
<canvas width="640" height="480"></canvas>
</div>

Canvas arc and fill with gradient

I have the following source code:
HTML:
<canvas id="main" width="500" height="500" style="border:1px solid black;"></canvas>
JavaScript:
function myRnd(val)
{
return parseInt(Math.random() * val);
}
function rndCircles() {
var maxCircles = 30;
for (var r = 1; r <= maxCircles; r++) {
var c = document.getElementById("main");
var x = myRnd(c.clientWidth);
var y = myRnd(c.clientHeight);
var radius = parseInt(Math.random() * 30);
var ctx = c.getContext("2d");
// Create gradient
var grd = ctx.createRadialGradient(75, 50, 5, 90, 60, 100);
grd.addColorStop(0, 'rgb(' + myRnd(255) + ', ' + myRnd(255) + ',' + myRnd(255) + ')');
grd.addColorStop(1, 'white');
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2 * Math.PI,false);
// Fill with gradient
ctx.fillStyle = grd;
ctx.fill();
ctx.lineWidth = 5;
//ctx.strokeStyle = '#003300';
ctx.stroke();
}
}
rndCircles();
I cannot see each arc filled with a different color/gradient on Chrome. Why? Am I missing something?
Fiddle here:
https://jsfiddle.net/j9wst2yd/
The gradient will use the initial position and radius when defined, which means the arcs further away from it will eventually turn completely white (or whatever the outer color would be).
You can solve this by using translation instead of setting position for the arcs.
Only a couple of adjustments are needed -
Create the gradient based around origin (0, 0):
var grd = ctx.createRadialGradient(0, 0, 5, 0, 0, 100);
// ...
Then replace setting arc position with translation, here absolute using setTransform() instead (translate() would accumulate, forcing you to reverse the translation afterwards which is more performance costly):
ctx.setTransform(1,0,0,1, x, y); // two last = translation
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI,false); // draw at origin
To reset back to normal (identity matrix) just set (0,0) for setTransform():
ctx.setTransform(1, 0, 0, 1, 0, 0);
Adjust as needed.
function myRnd(val)
{
return parseInt(Math.random() * val);
}
function rndCircles() {
var maxCircles = 30;
var c = document.getElementById("main");
var ctx = c.getContext("2d");
for (var r = 1; r <= maxCircles; r++) {
var x = myRnd(c.clientWidth);
var y = myRnd(c.clientHeight);
var radius = parseInt(Math.random() * 30);
var grd = ctx.createRadialGradient(0, 0, 5, 0, 0, radius);
grd.addColorStop(0, 'rgb(' + myRnd(255) + ', ' + myRnd(255) + ',' + myRnd(255) + ')');
grd.addColorStop(1, 'white');
ctx.fillStyle = grd;
ctx.setTransform(1,0,0,1,x,y);
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI,false);
// Fill with gradient
ctx.fill();
ctx.lineWidth = 5;
//ctx.strokeStyle = '#003300';
ctx.stroke();
}
}
rndCircles();
<canvas id="main" width="500" height="500" style="border:1px solid black;"></canvas>

Ball movement path in canvas, and other ideas you may have?

I've created this animation for my project that had to use any form of physics.
I am a total beginner, too :) Anyway, this is my project now :
Bouncing Balls
You can setup gravity and force, then click play, and just drag and drop to shoot the balls. You can change the values and hit update too see an effect.
My question is, how can I create an effect that when I press ratio button (for example) I can see the path that ball makes? Is it complicated? As I was saying I am a beginner, so no complex code for me :)
Also, doyou have any ideas to make the project better? Any additional "physics" effects? Or maybe you know a website that shows tutorials for simile (please) effects made in HTML5/js so I can add additional effects to my project.
One possibility (as you're clearing the canvas each frame) would be to draw ball paths onto a secondary canvas, which would not be cleared each frame. Then, when you come to clear the first frame, render the second frame after clearing, and before rendering the balls.
The second canvas would of course have to be the same dimensions as the first, so that all of the ball points line up correctly. The second canvas should also have a z-index lower than the first, so that it is only shown when you specifically render it to the first canvas (i.e. when the radio button is checked).
To decrease any lag while the radio is not checked, you could skip drawing the ball paths to the second canvas, although I don't think you would see any great increase in performance.
On each frame update, you would mark the position of each ball with a pixel, or line (from the previous position to the current) on the second canvas.
Looking at your code, you seem pretty competent, so I've skipped writing an example as I think this would be good experience for you :)
Modified 'script.js' source demonstrating solution
window.onload = function(){
$("#canvas").hide();
var howManyPaths = 0;
var showPath=false;
// SLIDERS
var gravitySlider = document.getElementById('gravitySlider');
var gravityVal = document.getElementById('gravityValue');
gravitySlider.onchange = function(){
gravityVal.value = gravitySlider.value;
}
gravityVal.onkeyup = function(){
gravitySlider.value = gravityVal.value;
}
var forceSlider = document.getElementById('forceSlider');
var forceValue = document.getElementById('forceValue');
forceSlider.onchange = function(){
forceValue.value = forceSlider.value;
}
forceValue.onkeyup = function(){
forceSlider.value = forceValue.value;
}
// GLOBAL VARIABLES
var test = false;
var gravityCount = $("#gravity").val();
var forceCount = $("#rectangles").val();
// CSS :
var playCSS = document.getElementById("play");
var restartCSS = document.getElementById("restart");
var clickableCSS = document.getElementById("setup");
var clickableBG = document.getElementById("img");
//restartCSS.style.visibility="hidden";
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvas2 = document.getElementById("canvas2");
var ctx2 = canvas2.getContext("2d");
//var ctx;
var gravity = 9.86;
var forceFactor = 0.5;
var mouseDown = false;
var balls = new Array();
var mousePos = new Array();
// EVENT HANDLER
function onMouseDown(evt){
mouseDown = true;
mousePos['downX'] = evt.pageX;
mousePos['downY'] = evt.pageY;
}
function onMouseUp(evt){
mouseDown = false;
setup.style.visibility="visible";
if(test == true && !( mousePos['downX'] < 200 && mousePos['downY'] < 150) ){
restartCSS.style.visibility="visible";
forceFactor = forceCount;
balls.push(new ball(mousePos["downX"],
mousePos["downY"],
(evt.pageX - mousePos["downX"]) * forceFactor,
(evt.pageY - mousePos["downY"]) * forceFactor,
10 + (Math.random() * 10),
0.8,
randomColor()
));
}
ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
}
function onMouseMove(evt){
mousePos['currentX'] = evt.pageX;
mousePos['currentY'] = evt.pageY;
}
function resizeWindow(evt){
//canvas.height = 960;
//canvas.width = 720;
canvas.height = $(window).height()-6;
canvas.width = $(window).width();
canvas2.height = $(window).height()-6;
canvas2.width = $(window).width();
}
$(document).mousedown(onMouseDown);
$(document).mouseup(onMouseUp);
$(document).mousemove(onMouseMove);
$(window).bind("resize", resizeWindow);
// GRAPHICS CODE
function circle(x, y, r, col){
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.closePath;
// fill
ctx.fillStyle = col;
ctx.fill();
// stroke
ctx.lineWidth = r * 0.1;
ctx.strokeStyle = "#000000";
ctx.stroke();
}
function circlePath(x, y)
{
ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
ctx2.fillStyle = '#3f4043';
ctx2.fillRect(x, y, 5, 5);
ctx2.strokeStyle = "black";
ctx2.strokeRect(x, y, 5, 5);
}
function randomColor(){
var letter = "0123456789ABCDEF".split("");
var color = "#";
for(var i=0; i<6; i++){
color += letter[Math.round(Math.random()*15)];
}
return color;
}
function arrow(fromX, fromY, toX, toY, color){
// path
ctx.beginPath();
var headLen = 10;
var angle = Math.atan2(toY - fromY, toX - fromX);
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.lineTo(toX - headLen * Math.cos(angle - Math.PI/6), toY - headLen * Math.sin(angle - Math.PI/6));
ctx.moveTo(toX, toY);
ctx.lineTo(toX - headLen * Math.cos(angle + Math.PI/6), toY - headLen * Math.sin(angle + Math.PI/6));
// style
ctx.lineWith = 1;
ctx.strokeStyle = color;
ctx.lineCap = "butt";
ctx.stroke();
}
function drawBall(){
// Gravity
gravity = gravityCount;
this.speedY += gravity * 0.5; // v = a * t
this.x += this.speedX * 0.05; // s = v * t
this.y += this.speedY * 0.05;
// prawa ściana
if(this.x + this.r > canvas.width){
this.x = canvas.width - this.r;
this.speedX *= -1 * this.bounce;
}
// lewa ściana
if(this.x - this.r < 0){
this.x = this.r;
this.speedX *= -1 * this.bounce;
}
// dolna ściana
if(this.y + this.r > canvas.height){
this.y = canvas.height - this.r;
this.speedY *= -1 * this.bounce;
}
// górna ściana
if(this.y - this.r < 0){
this.y = this.r;
this.speedY *= -1 * this.bounce;
}
// zwalnianie na ziemi
if (this.speedX > 0.25){
this.speedX -= 0.25;
if (this.speedY > 0.25)
this.speedY -= 0.25;
}
if (this.speedX < -0.25){
this.speedX += 0.25;
//if (this.speedY < -0.25)
// this.speedY += 0.25;
}
circle(this.x, this.y, this.r, this.col);;
}
// OBJECTS
function ball(positionX, positionY, sX, sY, radius, b, color){
this.x = positionX;
this.y = positionY;
this.speedX = sX;
this.speedY = sY;
this.r = radius;
this.bounce = b;
this.col = color;
this.draw = drawBall;
}
//GAME LOOP
function gameLoop(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
//grab the context from your destination canvas
//if path drawing is enabled, first draw the path canvas to the display canvas
if (showPath) ctx.drawImage(canvas2,0,0);
if(mouseDown == true){
// ctx.clearRect(0, 0, canvas.width, canvas.height); /* !important !!!!!!!!!!!!!!! */
arrow(mousePos['downX'], mousePos['downY'], mousePos['currentX'], mousePos['currentY'], "red");
}
for(var i=0; i<balls.length; i++){
balls[i].draw();
if (i==balls.length-1) {
//draw path
ctx2.fillStyle = '#3f4043';
ctx2.fillRect(balls[i].x, balls[i].y, 5, 5);
ctx2.strokeStyle = "black";
ctx2.strokeRect(balls[i].x, balls[i].y, 5, 5);
}
}
ctx.fillStyle = "#000000";
ctx.font = "15px Arial";
ctx.fillText("Balls: " + balls.length + " " + gravityCount + " " + forceCount + " " + howManyPaths, 10, canvas.height -10);
}
// START THE GAME
function init(){
//$("#setup").hide();
$("#canvas").show();
$("#canvas2").hide();
ctx = $('canvas')[0].getContext("2d");
canvas.height = $(window).height()-6;
canvas.width = $(window).width();
//canvas.width = 960;
//canvas.height = 720;
canvas2.height = $(window).height()-6;
canvas2.width = $(window).width();
return setInterval(gameLoop, 10);
}
$("#play").click(function() {
test = true;
playCSS.style.visibility="hidden";
gravityCount = $("#gravitySlider").val();
forceCount = $("#forceSlider").val();
init();
});
$("#restart").click(function() {
window.location.href="index.html";
});
$("#refresh").click(function() {
gravityCount = $("#gravitySlider").val();
forceCount = $("#forceSlider").val();
});
$("#showPath").click(function() {
showPath=true;
});
$("#hidePath").click(function() {
showPath=false;
});
}

Canvas transparency mask effect reset

I'm trying to overlay a canvas on a page in an attempt to make it look like it has been frosted over and then use mousemove to clear it away, following this post:
http://www.stevecalderon.com/2012/03/html5-canvas-tricks-transparency-mask-for-eraser-effect.html
I have created a jsfiddle - http://jsfiddle.net/ZWsG6/2/
var canvas = "";
var ctx = "";
function frost2(){
var int=self.setInterval(function(){clock()},1000);
canvas = document.createElement("canvas");
canvas.width = window.innerWidth;
canvas.height= window.innerHeight;
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
var objTo = document.getElementById("fltouch");
objTo.appendChild(canvas);
ctx = canvas.getContext('2d');
ctx.globalAlpha = 0.95;
ctx.fillStyle = "rgb(0, 255, 255)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'destination-out';
function drawPoint(pointX,pointY){
///scrape
var grd = ctx.createLinearGradient(pointX - 75,pointY-5,pointX + 75,pointY + 5)
grd.addColorStop(0, "transparent");
grd.addColorStop(0.3, "rgba(255,255,255,.6)");
grd.addColorStop(0.7, "rgba(255,255,255,.6)");
grd.addColorStop(1, "transparent");
ctx.fillStyle = grd;
ctx.fillRect(pointX - 75,pointY,400,30);
}
canvas.addEventListener('mousemove',function(e){
e.preventDefault();
drawPoint(e.pageX,e.pageY);
},false);
}
function clock(){
ctx.globalAlpha = 0.95;
ctx.fillStyle = "rgb(0, 255, 255)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'destination-out';
}
I've added a function clock() to run every second and refill the canvas with the colour so that it needs to be cleared again but it doesn't work, only remove the fill altogether.
Can anyone please tell how to reset the canvas to be filled with colour again? If it could be done slowly to look sorta like it is freezing over again that would be a bonus.
You need to set globalCompositeOperation back to default source-over value before refilling a canvas:
function clock() {
ctx.globalCompositeOperation = 'source-over';
ctx.globalAlpha = 0.95;
ctx.fillStyle = "rgb(0, 255, 255)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'destination-out';
}
http://jsfiddle.net/VqCbv/1/