Animation while playing audio in HTML5 - javascript

I have a flash player programmed by a different team. Now I have 1 project where I need to re program that player into HTML5. All functions of the flash player looks doable. I am just not sure how to show the animation when audio is being played. Eg. When beats get high, the animation shows up more when there is a bit silence the animation show up less. Could anybody please help me?
To get an idea, here is the attached screenshot:

With SoundManager2 you can easily access waveform data and make a graphical display with it:
someSoundObject.whileplaying = function() {
// Move 256 absolutely-positioned 1x1-pixel DIVs, for example (ugly, but works)
var gPixels = document.getElementById('graphPixels').getElementsByTagName('div');
var gScale = 32; // draw -32 to +32px from "zero" (i.e., center Y-axis point)
for (var i=0; i<256; i++) {
graphPixels[i].style.top = (gScale+Math.ceil(this.waveformData.left[i]*-gScale))+'px';
}
}
http://www.schillmania.com/projects/soundmanager2/doc/

Related

Efficiently limit the area to render graphics to on an HTML5 canvas using JavaScript

I'm looking for a way to render graphics onto an HTML5 canvas using JavaScript, but I want to only render said graphics if they're inside a pre-defined mask.
I'm creating a GUI framework that can be used to easily and quickly create GUIs on an HTML5 canvas. I think that something that would be really nice to have is a way to render graphics inside an element, and make the element auto-crop the graphics so that they always stay inside of it. For example, I can make a rectangular element and animate a circular pulse inside of it, and as the circle extends past the outside of the element, those parts of he circle should just not render to keep it looking smooth and sharp. This is similar to what CSS does with overflow: hidden;
Now, I know that one option is to use a mask-like feature. For example, P5.js has mask(). However, this is very very slow. Masking a single element a single time using P5.js significantly reduces framerate, and I want to be doing this potentially hundreds of times per frame without frame drops. I know that CSS does this incredibly efficiently (from my own experience working with it), but I can't seem to think of any way to make it efficient on a canvas element.
I could do it pretty simply if it was just a rectangle, but I want to do this for any shape. For example, a circle, a star, a rectangle with rounded edges, or really any polygon at all.
How can this be done? I thought of potentially rendering to an off screen canvas (which is shrunken to the size of the element in question), then render the element onto that screen using one color (let's say the background color will be white, and the shape will be black), then rendering the image we want masked onto another off screen canvas that's the same width as our other OSC, then looping through one of their image data arrays and mapping one to the other based on whether said pixel is white or black on the mask canvas.
But........ I can't help but think that that's going to be incredibly slow for the computer to process. I assume that CSS somehow leverages the GPU to do this type of computation incredibly efficiently and that's why they get such an increase in performance. Is it possible for me to do the same or am I just dreaming?
Okay, so I have found two different means of doing this (huge thank you to #Kaiido). One method is to use ctx.clip() while one works with CanvasPattern.
This snippet shows both means in action:
<canvas id = "c" width = "400" height = "400"></canvas>
<canvas id = "c2" width = "400" height = "400"></canvas>
<script>
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "yellow";
ctx.fillRect(0,0,400,400);
ctx.beginPath();
ctx.arc(200,200,100,0,6);
ctx.clip();
ctx.beginPath();// This clears our previous arc from the path so that it doesn't render in when we `fill()`
ctx.fillStyle = "rgb(255,0,0)";
for(var i = 0;i < 20;i++){
for(var j = 0;j < 40;j++){
ctx.rect(i * 20 + j % 2 * 10,j * 10,10,10);
}
}
ctx.fill();
</script>
<script>
var canvas2 = document.getElementById("c2");
var ctx2 = canvas2.getContext("2d");
ctx2.fillStyle = "orange";
ctx2.fillRect(0,0,400,400);
var osc = new OffscreenCanvas(400,400);
var oscctx = osc.getContext("2d");
oscctx.fillStyle = "rgb(255,0,0)";
for(var i = 0;i < 20;i++){
for(var j = 0;j < 40;j++){
oscctx.rect(i * 20 + j % 2 * 10,j * 10,10,10);
}
}
oscctx.fill();
var pattern = ctx2.createPattern(osc,"no-repeat");
ctx2.fillStyle = pattern;
ctx2.arc(200,200,100,0,6);
ctx2.fill();
</script>
Which one is more efficient and better to be run hundreds of times per frame?
Another edit:
I spent about an hour messing around with it on a sandbox website, and I made this small project:
https://www.khanacademy.org/computer-programming/-/6446241383661568
There I run each one every millisecond and see how quickly each one updates to see which appears more efficient. clip() is on top while CanvasPattern is on the bottom. They both appear to be incredibly fast to me, and I feel that no matter which I chose I will have almost exactly the same results. However, clip() does still appear to be a bit faster as far as I can tell.
See for yourself and let me know what you think!

Using SVG with ajax to animate state of IO hardware

The company I'm working for is selling micro computers that can manage and monitor diffrent IO devices.
They are using ajax for the web IO stuff and I created a new graphic for a Voltmeter that contains 41 states from 0 Volt to 20 with 0,5 stepping.
My first question would be, if that is being called a sprite because there are so many images?
The code I wrote to load the images was already much shorter than the company's because I made a function to generate the img links via a counter.
var i = 1;
function counter()
{
var img = "http://"adress"/"+i.toString()+".png";
if (i == 40)
{
i=0;
}
i++;
document.getElementById('picture').src = img;
}
now the next step I was asked was doing that with a svg.
I could do the same thing with a svg of course, but I´ve read about being able to animate svg.
The first big question is:
Should I make a svg file that contains all 41 images as code or should I just do one image and animate the needle by creating an own pivot for it?
Note that the animation states would be hand in hand with a javascript code that "GET"s hex values via xmlHTTP which define the states of the device.
So i wanna turn the needle to 3 volts on the svg if I rotate the knob at the device.
I don't ask for a full solution but some hints if this would even be possible and what i need to read about.
Here is the img I am talking about as an example
Voltmeter
https://www.deviantart.com/blue-lovag/art/Voltmeter-759876423
An SVG with 41 groups in it might be a big file. If you create an SVG containing just one image, where the needle is a <g> group with an ID defined on it, you can refer to that group from JavaScript and have the needle rotate -- even with a smooth animation.
You might define a CSS class for each state the needle can be in, with the rotation in it:
.pos20 {
transform: rotate(45deg);
}
There are some gotchas with Internet Explorer support for this, so you may have to set an attribute on the group directly:
<g transform="rotate(45deg)">...</g>
Please refer to this article on CSS-Tricks for details on SVG transformations.

Image on HTML5 canvas only appearing for one frame

Long time lurker but never made an account. Just wanted to preface that I'm by no means a dev and just tinkering and experimenting for fun, so I apologise in advance if I seem really dumb.
I'm working on a dynamic overlay for Twitch streaming and was previously using AS3 but I've switched over to HTML5 now. I'm trying to load an image onto the canvas (which will eventually be a profile picture fetched using Twitch API... but one step at a time). I'm using Adobe Animate and I have the following so far applied in Actions on the first frame of the layer:
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
show_image();
function show_image() {
source_image = new Image();
source_image.src = 'https://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png';
source_image.onload = function () {
context.drawImage(source_image, 100, 100);
}
}
When I hit Ctrl+Enter and see it in Chrome, the image appears for the first frame then disappears. I'm not sure how I'm supposed to get it to stay indefinitely. I need to be able to animate it later, and it'll change depending on the latest follow/donation/sub, etc.
I tried extending the frame itself in the timeline, however, this just changed long how it took to loop and didn't make the image itself stay longer. I'm probably missing something really simple!
Any help would be appreciated. Thanks!
Your code is okay if your approach is using a canvas with HTML and JS, without any libraries involved. However, this is not the case, as you are using Animate, and the way to draw graphics with it is different than using default canvas methods like drawImage().
Animate includes the CreateJS suite, which includes the EaselJS library ,and this allows you to use another tools to draw to your canvas. Two or them are the Stage object, the visual container of your animate project, and the Bitmap object, who represents an image, canvas or video. For effects of this question, only both objects are required.
Note that the code below is only for the first frame:
/* It is not necessary to declare the canvas or stage element,
as both are already declared. At this point the stage is ready to be drawn */
show_image();
function show_image() {
var source_image = new Image();
source_image.src = 'https://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png';
source_image.onload = function(event) {
/* A new Bitmap object is created using your image element */
var bmp = new createjs.Bitmap(event.currentTarget);
/* The Bitmap is added to the stage */
stage.addChild(bmp);
}
}

Soundcloud Custom Player with Waveform.js and scrubber functionality

I'm working on a custom Soundcloud player for my website using waveform.js to generate waveforms. It works great but it lacks the scrubber functionality. How can I add that?
I'm not a JS wizard, still learning my ways around so I'll be very thankful for any help or suggestions!
Update IV: I found a new way of including canvas generated waveforms into original SoundCloud Custom Player sc-player.js.
First of all I found a line of code responsible for HTML structure of the player and added id="waveform" to sc-waveform container on line 529:
.append('<div class="sc-time-span"><div class="sc-waveform-container" id="waveform">
</div><div class="sc-buffer"></div><div class="sc-played"></div></div>')
Then I updated the line 676, replacing img with canvas
$available = $scrubber.find('.sc-waveform-container canvas'),
Next, I located a piece of code responsible for embedding the original image of waveform on line 340 and commented it out:
// $('.sc-waveform-container', $player).html('<img src="' + track.waveform_url +'" />');
And then I posted the code below at the bottom of my page:
<script>
SC.get("/tracks/80348246", function(track){
var waveform = new Waveform({
container: document.getElementById("waveform"),
height: 40,
innerColor: '#ffffff'
});
waveform.dataFromSoundCloudTrack(track);
});
//----------- end of insterted waveform.js code ----------------------
</script>
Results are very promising, Now I have fully customizable waveform and scrubber is working as well. However there are still things I'd like to fix.
In Chrome, when I press play and pause, then click the waveform, the track starts playing, but the play button doesn't change its state. Then need to double click it to stop the track.
The buffer and progress bar are still just the sc-player divs in the background. How could I link sc-player.js and waveform.js together so the progress is generated on the waveform canvas (as in example on http://waveformjs.org/)?
Any ideas how to fix those?
Here's the player on the live website:
http://www.code.spik3s.com/rnr/
on play call
myVar=setInterval(Timed,100);
function Timed() {
total_duration = duration of the track playing;
current_duration = the current position of the track.
width = canvas width;
pointer = the id of the pointer being used to show the progress.
position = (canvas_width / total_duration) * current_duration;
pointer.style.left = position;
}
you'll have to set the information in, but something like this will do

Engine for HTML5 Canvas animations and effects?

I am developing a HTML5 game. The server-side code (node.js, socket.io) is mostly done and I am moving on to polishing the client-side code.
I have been directly drawing tiles/grid on the canvas and moving the player's sprite using context and clearRect etc. I am thinking of drawing simple animations and effects over the tile-map/grid such as:
Rain, with flashes of lightning, and thunder audio clip.
Animating some of the tiles. E.g. grass tile has grass blowing in the wind by cycling through frames (like an animated gif).
Pop up text boxes that are close-able with mouse clicks or keyboard button press.
I have checked out this long list of JavaScript engines and tried out CraftyJS and MelonJS but most of these are made for platform or arcade style games, and many of them are not ready for production or are poorly maintained.
Is there a simple, lightweight, production-quality HTML5 canvas engine that can accomplish what I want?
Take a look at CreateJS; it's a great engine for what you're looking for.
EaseJS can be used for the Canvas element
SoundJS for the audio clip which you want played
It's well maintained, but an 1.0 version hasn't been released (yet).
Is it just animated sprites you want to achieve? You can do this easy without the use of a game engine. As for dialog boxes - you could just use dom elements over the canvas.
Here is a sprite class I wrote in javascript - maybe it's of some help :)
var FrtlsSprite = Class.extend({
init: function(bitmap, offsetX, offsetY, frameWidth, frameHeight, frameCount, loop){
this.dtotal=0;
this.framerate=0.007;
this.loop = loop;
this.isPlaying=false;
this.bitmap = new Image();
this.bitmap.src = bitmap;
this.frames= new Array();
this.currentFrame=0;
this.endFrame=0;
for(var i=0;i<frameCount;i++){
this.frames.push(new FrtlsFrame(offsetX+i*frameWidth, offsetY+0, frameWidth, frameHeight));
}
},
update: function(dt){
if(this.isPlaying){
this.dtotal += dt //we add the time passed since the last update, probably a very small number like 0.01
if (this.dtotal >= this.framerate){
this.dtotal -= this.framerate;
this.currentFrame++;
if(this.currentFrame==this.endFrame){
if(this.loop == false){
this.stop();
}
else{
this.currentFrame=0;
}
}
}
}
},
draw: function(){
fruitless.ctx.drawImage(this.bitmap,
this.frames[this.currentFrame].pos.x,
this.frames[this.currentFrame].pos.y,
this.frames[this.currentFrame].dimensions.x,
this.frames[this.currentFrame].dimensions.y,
0,
0,
this.frames[this.currentFrame].dimensions.x*fruitless.worldScale,
this.frames[this.currentFrame].dimensions.y*fruitless.worldScale);
},
play:function(frame){
this.currentFrame=(frame==undefined)?0:frame;
this.endFrame = this.frames.length-1
this.isPlaying=true;
},
playTo:function(frame, endFrame){
this.currentFrame=frame;
this.endFrame = endFrame;
this.isPlaying=true;
},
stop:function(frame){
this.currentFrame=(frame==undefined)?this.currentFrame:frame;
this.isPlaying=false;
}
});
cgSceneGraph will do the job for you.
Look at the examples web page, there are some examples with animated Sprite. It's a native component of the framework and is really easy to use with several featres like multi animation inside the same instance of the animated sprite, use of spritesheet, ...

Categories