HTML5 Canvas - Popping popcorn animation / random images? - html

Maybe I am asking the wrong question completely. I have read quite a bit and seen quite a bit of HTML 5 capabilities; however, I have not yet had time to sit down and really start utilizing any of it yet :(, but hope to soon.
Anyway, I was curious if I could do the following with HTML5 and how to begin to implement it. Or, if this does not work, then how can I make it work?
I have a popcorn image (1 piece of popped popcorn that is).
I wanted to create a canvas and on a button click, start a chain of this image being randomly "popped" onto the canvas, slowly at first then gaining speed until such time there should be a stopping point.
Anyone who has popped popcorn can understand what I am looking to do here.
Is this possible to do easily?

I've popped popcorn and, yep, you can do this. Load the popcorn image by creating an Image object and setting its src attribute. Use the image's onload property to start the animation. Given the animation's duration k, use a sine curve, sin(x/(k/π)), to calculate the number of kernels to show per frame.
Here's one way of doing it, demo here: http://jsfiddle.net/uyk63/8/
var IMAGE_URL = 'http://i.istockimg.com/file_thumbview_approve/959519/2/stock-photo-959519-isolated-single-popcorn.jpg';
var DURATION = 10 * 1000, FRAMES = 30, KERNELS = 10;
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var frame = 0,
start = new Date().getTime(),
image = new Image;
image.src = IMAGE_URL;
image.onload = function() {
function pop() {
ctx.drawImage(image,
Math.floor(Math.random() * canvas.width),
Math.floor(Math.random() * canvas.height),
100, 50);
}
// A little overcomplicated. You could probably do this in a single loop.
// (It's late and I'm tired, though. Sorry.)
function animate() {
var i, delay,
count = Math.floor(Math.sin(frame / (FRAMES / Math.PI)) * KERNELS);
for (i = 0; i < count; i++) {
delay = (DURATION / FRAMES) / count * i;
setTimeout(pop, delay);
}
if (++frame < FRAMES) {
setTimeout(animate, DURATION / FRAMES);
}
}
animate();
};

Related

HTML canvas image is fading out after multiple iterations/reusing

I have a portal which is used for collecting orders from users in hand written format.
In my portal, I am using HTML canvas for getting inputs from user.
Once the user write order and submits it, I will read the drawings from the canvas and saves it into my DB.
HTML
<canvas height="750" width="768" id="userNotes"></canvas>
Javascript
var canvas = document.getElementById('userNotes');
var notesDataURL = canvas.toDataURL();
saveImageDataToDataBase (notesDataURL);
Next time when the user comes for a new order, I will draw this image back into the canvas, so that he can make modifications on the same and submit it as fresh order.
Javascript
var canvas = document.getElementById('userNotes');
var context = canvas.getContext('2d');
var img = new Image();
img.src = imageData;
context.drawImage(img, 0, 0);
Problem that I am facing is that after multiple iterations, the image starts fading out.
One observation is that fading is more at the bottom part of the image and less on the top side.
Consider the below sample images,
After 10 iterations image became like this,
Below is a JS FIddle created using sample code, in this after about 25 iterations fading will be visible(issue is visible only in tablet mentioned below).
https://jsfiddle.net/hz8r993v/
Observation:
An observation which I made is the issue is happening only in a specific tablet model, Samsung SM-P550, which is unfortunately the one my application is build for.
I am not able to reproduce this issue while using this application in my laptop, PC or another sm-p650 tablet.
Currently Only happening in all tablets of model SM-P550. Even I am confused with this observation.
I also tried disabling ImageSmoothingEnabled properties, but not helping.
Any leads/clues are appreciated.
JPEG compression quirk & rounding.
Looking at the image you provided suggests to me that you are incorrectly offsetting the image each time you render it. As you have not provided enough code for anyone to make an assessment as to why this is happening leaves us only to guess.
Lossless JPEG!
My first instinct is that you have offset the drawing or scaled it. I create the example below and offset a jpeg url of the rendered text by 0.2 pixels.
The result no blur Not a real surprise Jpegs are designed so that they can be copied. The artifacts introduced by the compression actually remove the blur introduced by the offset
Draw jpeg image 0.2 pixels down save and repeat.
var ctx = canvas.getContext("2d");
ctx.font = "64px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("testing",canvas.width / 2,canvas.height / 2);
ctx.font = "18px Arial";
var count = 0;
var imgURL;
function copy(){
imgURL = canvas.toDataURL();
}
function paste(x,y){
var img = new Image;
img.src = imgURL;
img.onload = function(){
ctx.fillStyle = "white";
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.drawImage(img,x,y);
ctx.fillStyle = "red";
ctx.fillText("Copy : " + count,canvas.width / 2,count * 20);
}
}
function iterate(){
count += 1;
copy();
paste(0,0.2);
if(count < 100){
setTimeout(iterate,50);
}
}
iterate();
<canvas id="canvas" width = 300 height = 150></canvas>
Your image clearly shows a vertical blurring so I set about finding when I can blur the image so that it over comes the Jpeg compression. Offsetting by 0.5 or more does not blur the image just scrolls pixel perfect 100 pixels (note I copy past 100 times move image 0.5 pixels down yet resulting image has moved 100 pixels. I have marked row 100)
Jpeg turns 0.5 pixel steps into 1 pixel steps
var ctx = canvas.getContext("2d");
ctx.font = "64px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("testing",canvas.width / 2,canvas.height / 2);
ctx.font = "18px Arial";
var count = 0;
var imgURL;
function copy(){
imgURL = canvas.toDataURL();
}
function paste(x,y){
var img = new Image;
img.src = imgURL;
img.onload = function(){
ctx.fillStyle = "white";
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.drawImage(img,x,y);
ctx.fillStyle = "red";
ctx.fillText("Copy : " + count,canvas.width / 2,count * 20);
if(count === 100){
ctx.fillRect(0,100,canvas.width,1);
}
}
}
function iterate(){
count += 1;
copy();
paste(0,0.5);
if(count < 100){
setTimeout(iterate,50);
}
}
iterate();
<canvas id="canvas" width = 300 height = 150></canvas>
At end redline is pixel row 100.
Seams that Jpeg compression is much better at preserving the original src image pixels than I suspected. But that does not help solve the problem.
Device specific
So is it a quirk of the device. I look up the specs and nothing stands out. I begin to suspect that you may have a canvas display scaling issues (the canvas resolution not matching the display size)
I start to set up the snippet to use differing resolutions and by shear chance I run the code at the same resolution as the samsung device mentioned in the question.
Blurring yay... but i did not change the canvas pixel scaling, all I did was change the canvas resolution.
Example of blurring.
Note that offset is 0.01 pixels (100th of a pixel)
var ctx = canvas.getContext("2d");
ctx.font = "64px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("testing",canvas.width / 2,canvas.height / 2);
ctx.font = "18px Arial";
var count = 0;
var imgURL;
function copy(){
imgURL = canvas.toDataURL();
}
function paste(x,y){
var img = new Image;
img.src = imgURL;
img.onload = function(){
ctx.fillStyle = "white";
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.drawImage(img,x,y);
ctx.fillStyle = "red";
ctx.fillText("Copy : " + count,canvas.width / 2,count * 20);
}
}
function iterate(){
count += 1;
copy();
paste(0,0.01);
if(count < 100){
setTimeout(iterate,50);
}
}
iterate();
<canvas id="canvas" width = 300 height = 1023></canvas>
Possible fix.
As there is not enough code to give a complete answer the above experiments have given me enough information to make an educated guess.
The problem is specific to the resolution 1024 height. (I have tried a few other (very limited) resolutions and could not get blurring)
The blurring does not occur at that resolution if the image is rendered at the pixel boundaries.
The possible fix.
When you render the image convert the render coordinates to integers using Math.floor(coordinate) this will ensure there is no fractional offset, even very tiny offsets that should not affect the image can be amplified by the jpeg compression when the resolution is at 1024 (and maybe some other resolutions as well).
var oldImage = new Image;
oldImage.src = "old image url";
oldImage.onload = function(){
ctx.drawImage(oldImage,Math.floor(x),Math.floor(y));
}
Hope this helps.
I am probably not experienced enough to tell you what about that tablet version, or what in your HTML/Javascript could be causing this issue. I am, however, good at problem solving, and solving puzzles.
I have two possible guesses:
Is there anything in your code, which could lead to a change in the resolution? The only reason that I think this may be part of the cause, is that because the image is being redrawn on a canvas that is the same size, any change in the image size could cause the image to become slightly pixilated. and repetitive changes in the image size would only exaggerate this change.
That is probably not the sole cause. I think that there is probably an additional cause because the issue is only present on the one specific tablet version. I am not familiar with that tablet version, but is there anything about its OS or interface that could alter the file when it is saved or redisplayed?
As a side note, it would be nice if you could provide a comparison image, just to see the change.
Hope that this at least points you in the right direction.

Pan And Volume Depending on Movieclip's rela

I am creating a simple 2d platformer in as3, and am starting to implement sounds.
I have a dog that follows you around the level, and plays an ambient sound everywhere.
I am just wondering if there is a way to change the volume and pan of the sound channel, depending on the distance between two movieclips?
So far I am checking if the dog movieclips's x is greater or less than the player's x and soundTransforming it from there, but it is not at all smooth.
I really can't think of any math to do it at the moment, and any help is appreciated!
Thanks!
Here is an example. This assumes you have the following vars:
var player:Sprite;
var dog:Sprite;
var mySoundChannel:SoundChannel;//assigned when you play a sound
var myVolume:SoundTransform = new SoundTransform(); //sound transform to hold the current volume and pan settings
var currentDistance:Number; //the distance between dog and player
var currentDistanceX:Number; //the x distance between dog and player for panning
var minSound:Number = 0.1; //10% is the quietesst it can get;
var minDistance:Number = 500; //500 pixels away is when it should reach the min sound value
var distanceGrace:Number = 10; //if within 10 pixels, keep the volume at 100%
//you'll probably want run the code below in an enter frame handler
function enterFrameHandler(e:Event):void {
//populate the distance vars
currentDistance = Point.distance(new Point(dog.x, dog.y), new Point(player.x, player.y)); //for volume
currentDistanceX = Point.distance(new Point(dog.x, 0), new Point(player.x, 0)); //for panning
myVolume.volume = Math.max(minSound, 1 - Math.abs(currentDistance - distanceGrace) / minDistance);
myVolume.pan = ((currentDistanceX + 1) / minDistance);//only adding 1 to the distance so we don't ever divide by 0
if (dog.x < player.x) myVolume.pan *= -1;
mySoundChannel.soundTransform = myVolume;
}

Actionscript 3: Rotate multiple shapes around internal point

I'm having trouble with AS3 which I have to use for a little research project of mine.
The AS3 project would create a number of randomly placed squares which all would rotate around its center point.
I managed to figure out how to rotate it internally using this handy walkthrough.
However I am having trouble applying this method to all squares which were created in a for loop with randomly selected stage points. Only the first created will rotate
Here is the code in question:-
for(var i=0; i<10; i++)
{
var square:Shape = new Shape();
this.addChild(square);
var posX = Math.floor(Math.random() * stage.stageWidth) + 50;
var posY = Math.floor(Math.random() * stage.stageHeight) + 50;
square.x=posX;
square.y=posY;
var curSquareAng:Number=0;
var squareRotCenter:Point=new Point(0,0);
drawShapes();
var squareMat:Matrix=square.transform.matrix.clone();
}
this.addEventListener(Event.ENTER_FRAME, onEnter);
function onEnter(e:Event):void {
curSquareAng = (curSquareAng+2)%360;
rotateSquare(curSquareAng);
}
function rotateSquare(deg:Number):void {
var mat:Matrix= squareMat.clone();
MatrixTransformer.rotateAroundInternalPoint(mat,squareRotCenter.x, squareRotCenter.y, deg);
square.transform.matrix = mat;
}
I realize I likely have to create an array for each of the squares initial center points and loop through them. However I am completely lost on how to do so. As you can likely tell I am unfamiliar with AS3 and would much appreciate any help you can give this beginner programmer. :P
You need to create your own class based on a shape, then you stuff it full of properties that represent rotation center point, current angle and whatever else you'd want your squares to contain, then give the class update method which will do what you wrote in onEnter function for itself only. Then you will have easier control over what your squares are able to do. This technique is called "encapsulation".
On a side note, if you're wanting your square to rotate around internal point of (0,0), you can set their rotation property to achieve the desired effect. For other points, the walkthrough or its equivalent should be used.
public class Square extends Shape {
public var rotationCenter:Point=new Point();
private var currentAngle:Number=0;
public var rotationSpeed:Number=2; // degrees per frame
private var baseMatrix:Matrix;
public function Square() {
// draw the shape on "this.graphics"
this.graphics.beginFill(0xffff00,1);
this.graphics.moveTo(-20,-20);
this.graphics.lineTo(20,-20);
this.graphics.lineTo(20,20);
this.graphics.lineTo(20,-20);
this.graphics.lineTo(-20,-20);
this.graphics.endFill();
// if not set in declaration, set internal vars
baseMatrix=this.transform.matrix; // likely identity matrix, but let's initialize anyway
}
public function storeMatrix():void {
// you are positioning a square after you create it, so probably you want its new location to be transformed
// that one's matrix will no longer be an identity, so go capture
baseMatrix=this.transform.matrix;
}
public function update():void {
// should be called once per frame
currentAngle=(currentAngle+rotationSpeed)%360;
var mat:Matrix= baseMatrix.clone();
MatrixTransformer.rotateAroundInternalPoint(mat,rotationCenter.x, rotationCenter.y, currentAngle);
this.transform.matrix = mat;
}
}
Now, you will have to maintain an array of squares to make them rotate separately:
var squares:Array=[];
for (var i:int=0;i<10;i++) {
var square:Square=new Square();
var posX = Math.floor(Math.random() * stage.stageWidth) + 50;
var posY = Math.floor(Math.random() * stage.stageHeight) + 50;
square.x=posX;
square.y=posY;
// after you position, give it a rotation point
square.rotationCenter.x=Math.random()*40-20;
square.rotationCenter.y=Math.random()*40-20; // -20 to 20, should do for this example
// now fix the position so your square will know that it should rotate
// its *current* transform matrix
square.storeMatrix();
// even if it's actually unchanged by changing X or Y
// also, should you desire to scale some square, you should do that prior to calling this
// now add the square to an array
squares.push(square);
}
addEventListener(Event.ENTER_FRAME,onEnter);
function onEnter(e:Event):void {
for (var i:int=0;i<squares.length;i++) squares[i].update();
// simple, isn't it? Each square will know what to do.
}
Nevermind. Thank you Vesper for putting me on the right track I've managed to solve my problem thanks to your input (not necessarily through your way but your input helped me get to the destination per say).
I think I was making it a little too complicated going though the matrix route and instead used a shape array to loop through the squares and add rotation. The solution I came to is a little simple but gets the job done.
public var rotationSpeed:Number=2; // degrees per frame
public var square:Array = new Array( );
public function Square() {
for (var i:int=0;i<10;i++) {
square[i] = new Shape();
var posX = Math.floor(Math.random() * stage.stageWidth) + 50;
var posY = Math.floor(Math.random() * stage.stageHeight) + 50;
square[i].graphics.lineStyle();
var rgb = Math.random() * 0xFFFFFF;
square[i].graphics.beginFill(rgb);
// -50 determines where the spin will center from.
square[i].graphics.drawRect(-50,-50,100,100);
square[i].graphics.endFill();
square[i].x = posX;
square[i].y = posY;
addChild(square[i]);
}
addEventListener(Event.ENTER_FRAME,onEnter);
}
private function onEnter(e:Event):void {
for (var i:int=0; i < square.length; i++) {
getChildAt(i).rotation += rotationSpeed;
}
}

Actionscript 3/Flash: Basic Game Loop Stuttering Problems

I'm trying to make a basic game in Flash and Actionscript 3.
As of now, I've been working on a smooth game loop but ran into problems. I tried to implement a fixed time-stamp loop seen: http://gafferongames.com/game-physics/fix-your-timestep/ and in Flixel.
The main issue that I have right now is that moving a simple object across the screen produces noticeable stuttering. I am aiming for a smoother experience but can't seem to figure out what the issue is.
The main loop is called on an Event.ENTER_FRAME at 60 fps.
These variables are instantiated first:
public var total:uint = 0;
public var fixedDT:Number = 1000.0/60.0; //Shoot for 60 FPS in MS
public var accumulator:int = 0;
public var maxAccumulation:uint = 120;
This is the main loop on every ENTER_FRAME:
//Most times are in ms
var mark:uint = getTimer();
var elapsedMS:uint = mark-total;
total = mark;
accumulator += elapsedMS;
if(accumulator > maxAccumulation){
accumulator = maxAccumulation;
}
while(accumulator > fixedDT){
step();
accumulator = accumulator - fixedDT;
}
//Convert from ms to secs. to interpolate graphics drawing (linear interpolation)
renderGameState(accumulator/fixedDT/1000.0);
step() is just updating every game-object with the fixed delta-time. The game object update function is simple and is as follows:
//First part is just updating the previous position for graphic interpolation
position.x += velocity.x*deltaTime;
position.y += velocity.y*deltaTime;
For rendering, I am just drawing bitmap.copyPixel. The graphical interpolation I mentioned is using a basic linear interpolation function that uses prev./curr. position and deltaTime to calculate the drawX/Y.
public function render(bitmap:BitmapData, deltaTime:Number, xOff:Number, yOff:Number):void{
this.x = lerp(prevPosition.x,position.x,deltaTime) + xOff;
this.y = lerp(prevPosition.y,position.y,deltaTime) + yOff;
bitmap.copyPixels(bitmapData, bitmapData.rect,new Point(this.x,this.y),null,null,true);
}
public function lerp(v0:Number, v1:Number, t:Number):Number {
return (1-t)*v0 + t*v1;
}
However, there is noticeable stuttering appearing. In the image below, I don't clear the bitmap before drawing to it. You should be able to see that there's a lot of variation between the spacing of circles rendered, and sometimes it's extremely noticeable.
http://i.stack.imgur.com/00c39.png
I would appreciate any help at all, thanks!
I don't know if this helps but here's the code I use to fix my time step.
private var _pause :Boolean;
private var _prevTimeMS :int;
private var _simulationTime :Number;
override public function update():void
{
super.update();
if (!_pause)
{
var curTimeMS:uint = getTimer();
if (curTimeMS == _prevTimeMS)
{
return;
}
var deltaTime:Number = (curTimeMS - _prevTimeMS) / 1000;
if (deltaTime > 0.05)
{
deltaTime = 0.05;
}
_prevTimeMS = curTimeMS;
_simulationTime += deltaTime;
while (space.elapsedTime < _simulationTime)
{
// Your game step goes here.
_space.step((stage.frameRate > 0) ? (1 / stage.frameRate) : (1 / 60));
}
}
}
(Originally taken from a Nape Physics sample)

html5 getImageData then putImageData results in fading image

I'm very new to Html5 and I was wondering if someone could shed some light on this:
<script type="text/javascript">
window.onload = function () {
var canvas = document.getElementById('canvas'); //682 x 111 pixel canvas
var context = canvas.getContext('2d');
var image = new Image();
image.src = "/Content/ImageTestOne/logo-for-dissolve.png"; //682 x 111 pixel image
image.onload = function () { context.drawImage(image, 0, 0); drawFrame(); };
function drawFrame() {
window.requestAnimationFrame(drawFrame, canvas);
imageData = context.getImageData(0, 0, canvas.width, canvas.height);
//Do something to some pixels here that persists over time
context.clearRect(0, 0, canvas.width, canvas.height);
context.putImageData(imageData, 0, 0);
};
};
</script>
According to my limited knowledge of Html5 this code should do nothing except continually display the "image". But instead the image quite rapidly burns out to almost white which suggests that the imageData is changed slightly each time it is either read from or written to the canvas...
Basically I wanted to fade the image where the mouse was located so that a background image shows through as the mouse is moved around. Is there a way around this or am I going to have to become a little more creative with the process? Is there anyway I can manipulate the "image" ImageData rather than getting it from the canvas each time?
Thanks in advance, I've tried using jpg and png and loading into DOM rather than via image.src but they all have the same issue.
Using the latest Chrome btw.
Here is the setup for the requestionAnimationFrame to handle a range of browsers with a fail over to SetTimeout:
(!window.requestAnimationFrame)
{
window.requestAnimationFrame = (window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, 1000/60);
});
}
Here is the code for the canvas
<canvas id="canvas" width="682" height="111"></canvas>
That's all the code for this.
putImageData() and getImageData() can be lossy. There's a note in the spec about this:
Due to the lossy nature of converting to and from premultiplied alpha
color values, pixels that have just been set using putImageData()
might be returned to an equivalent getImageData() as different values.
See also this related question:
Why does HTML Canvas getImageData() not return the exact same values that were just set?
I have tried also to apply this to my game where in im going to manipulate the selected pixels to have effect but It doesn't give me the expected result
here is some sample code that i used to manipulate the pixel to change
get image information and store
var img = context.getImageData(0,0, width, height)
var imgdata = img.data
var len = imgdata.length
loop to all data and manipulate pixel information
var i = 0;
for(i; i<leng; i++) {
var red = imgdata[i]
var green = imgadata[i+1]
var blue = imgdata[i+2]
var alpha = imgdata[i+3]
imgdata[i] = new value
imgdata[i+1] = new value
imgdata[i+2] = new value
imgdata[i+3] = new value
}
context.putImageData(img, 0,0)
then create animation frame to see effect
requestAnimationFrame is an experimental feature (june 2012) that uses time based frame access. The reason for this is avoid latency in animations.
I suggest you take a look at this Moz article.