How to smooth cocos2d-JS wheel spinning - javascript

I want to make a wheel spinning like this, but it not smooth when rotating. How can I make it run smoothly?
Demo
var audioengine = cc.audioEngine;
audioengine.playMusic(res.Wheel_mp3, false);
var spinNumber = Math.floor((Math.random()*360) + 0);
var angle = spinNumber + 13*360;
var action = new cc.RotateBy(18, angle);
var ease = new cc.EaseSineOut(action);
this.sprite.runAction(ease);

I always create a new class extends Sprite and rewrite draw() or update() to do the job.
Here is the example:
var Wheel = Sprite.extend({
ctor and other function : ...
draw : function(){
this._super();
var speed = this. calculate Speed();
var rotation = this.getRotation();
var neoRotation = (rotation+speed)%360;
this.setRotation(neoRotation)
},
caculateSpeed : function(){
// some function that calculate the speed to simulate acceleration and deceleration.
// return 0 means not rotate.
return speed.
}
})
In order to calculate the speed, you may save some parameter(like current speed, current acceleration speed) to record the status.

Related

Cocos2D-JS Chipmunk PhysicsSprite Move action not working in Android phone

I am trying to develop a simple cross-platform game, and trying to move a PhysicsSprite with a Body to the position I touched/clicked, using Cocos2D-JS.
Here is the code that I have:
var TAG_SPRITE = 1;
var AnimationLayer = cc.Layer.extend({
space:null,
ctor:function (space) {
this._super();
this.space = space;
this.init();
},
init:function () {
this._super();
var winsize = cc.director.getWinSize();
//init physics sprite
var spriteHead = new cc.PhysicsSprite(res.Head_png);
var headContentSize = spriteHead.getContentSize();
//init body
var headBody = new cp.Body(1, cp.momentForBox(1, headContentSize.width, headContentSize.height));
headBody.p = cc.p(winsize.width / 2, winsize.height / 3);
this.space.addBody(headBody);
//init shape
var headShape = new cp.CircleShape(headBody, headContentSize.width / 2, cp.v(0, 0));
headShape.setFriction(0.3);
headShape.setElasticity(0.8);
this.space.addShape(headShape);
spriteHead.setBody(headBody);
this.addChild(spriteHead, 0, TAG_SPRITE);
//for mobile
if('touches' in cc.sys.capabilities ){
cc.eventManager.addListener({
event: cc.EventListener.TOUCH_ONE_BY_ONE,
swallowTouches: true,
onTouchBegan:function (touch, event) {
cc.log('touch began');
event.getCurrentTarget().moveSprite(touch.getLocation());
return true;
},
onTouchMoved: function (touch, event) {
},
onTouchEnded: function (touch, event) {
},
onTouchCancelled: function (touch, event) {
}
}, this);
}
//for desktop
else if ('mouse' in cc.sys.capabilities ) {
cc.eventManager.addListener({
event: cc.EventListener.MOUSE,
onMouseUp: function (event) {
event.getCurrentTarget().moveSprite(event.getLocation());
}
}, this);
}
},
moveSprite:function(position) {
cc.log('move to: ' + position.x + ',' + position.y);
var sprite = this.getChildByTag(TAG_SPRITE);
var moveAction = new cc.moveTo(1, position);
sprite.runAction(moveAction);
}
});
As I see the logs from the logcat, it can handle touch events but cannot move the sprite. When I convert the PhysicsSprite to just Sprite object and remove all other Body and Shape stuff, it can be moved to the location that I touch.
The problem is that I can move the PhysicsSprite in the browser whereas I cannot do that in my Android phone.
Note: I use Chipmunk physics engine
I don't know this is the real solution or should be considered as a workaround but the code below works fine for both web and Android. But still don't know why the code in the question doesn't work for Android while it does for the web. (It would make more sense if it didn't work for both...)
I tried to move body of the sprite instead of itself. The new moveSprite method is like that:
moveSprite: function(sprite){
cc.log('move to: ' + position.x + ',' + position.y);
var sprite = this.getChildByTag(TAG_SPRITE);
var body = sprite.getBody();
var velocity = 300;
this.moveWithVelocity(body, position, velocitiy);
}
moveWithVelocity is the custom function that I created in the same Layer, moving the Body to the destination point with a particular velocity:
moveWithVelocity: function(body, destination, velocity){
var deltaX = destination.x - body.p.x;
var deltaY = destination.y - body.p.y;
var distance = Math.sqrt(Math.pow(deltaX,2) + Math.pow(deltaY,2));
var time = distance / velocity;
var velocityX = deltaX / time;
var velocityY = deltaY / time;
//start the action with the calculated velocity in the calculated direction
body.applyImpulse(cc.v(velocityX, velocityY), cc.v(0,0));
//stop the sprite (or body here) when duration of movement is time out (or when the sprite/body arrives its destination)
setTimeout(function(){
body.setVel(cc.v(0,0));
}, time*1000);
}
Hope this helps anyone encountered the same problem.

Creating a scratch card in EaselJS

I've been trying to develop a scratch card in EaselJS.
So far, I've managed to get a Shape instance above a Bitmap one and enabled erasing it with click and drag events, so the image below becomes visible.
I've used the updateCache() with the compositeOperation approach and it was easy enough, but here is my issue:
How can I find out how much the user has already erased from the Shape instance, so I can setup a callback function when, say, 90% of the image below is visible?
Here is a functioning example of what I'm pursuing: http://codecanyon.net/item/html5-scratch-card/full_screen_preview/8721110?ref=jqueryrain&ref=jqueryrain&clickthrough_id=471288428&redirect_back=true
This is my code so far:
function Lottery(stageId) {
this.Stage_constructor(stageId);
var self = this;
var isDrawing = false;
var x, y;
this.autoClear = true;
this.enableMouseOver();
self.on("stagemousedown", startDrawing);
self.on("stagemouseup", stopDrawing);
self.on("stagemousemove", draw);
var rectWidth = self.canvas.width;
var rectHeight = self.canvas.height;
// Image
var background = new createjs.Bitmap("http://www.taxjusticeblog.org/lottery.jpg");
self.addChild(background);
// Layer above image
var overlay = new createjs.Shape();
overlay.graphics
.f("#55BB55")
.r(0, 0, rectWidth, rectHeight);
self.addChild(overlay);
overlay.cache(0, 0, self.canvas.width, self.canvas.height);
// Cursor
self.brush = new createjs.Shape();
self.brush.graphics
.f("#DD1111")
.dc(0, 0, 5);
self.brush.cache(-10, -10, 25, 25);
self.cursor = "none";
self.addChild(self.brush);
function startDrawing(evt) {
x = evt.stageX-0.001;
y = evt.stageY-0.001;
isDrawing = true;
draw(evt);
};
function stopDrawing() {
isDrawing = false;
};
function draw(evt) {
self.brush.x = self.mouseX;
self.brush.y = self.mouseY;
if (!isDrawing) {
self.update();
return;
}
overlay.graphics.clear();
// Eraser line
overlay.graphics
.ss(15, 1)
.s("rgba(30,30,30,1)")
.mt(x, y)
.lt(evt.stageX, evt.stageY);
overlay.updateCache("destination-out");
x = evt.stageX;
y = evt.stageY;
self.update();
$rootScope.$broadcast("LotteryChangeEvent");
};
}
Any ideas?
That's a tricky one, regardless of the language. The naive solution would simply be to track the length of the paths the user "draws" within the active area, and then reveal when they scratch long enough. That's obviously not very accurate, but is fairly simple and might be good enough.
The more accurate approach would be to get the pixel data of the cacheCanvas, then check the alpha value of each pixel to get an idea of how many pixels are transparent (have low alpha). You could optimize this significantly by only checking every N pixel (ex. every 5th pixel in every 5th row would run 25X faster).

How to control animation speed (requestAnimationFrame)?

I change the text color with requestAnimationFrame(animate); function:
requestAnimationFrame(animate);
function animate(time){
... // change text color here
if (offset_s < offset_e) {requestAnimationFrame(animate);}
}
offset_s and offset_s indicates start and end positions of the text for color change. In some cases the animation should last for 2 seconds, but in order cases - for 5 seconds, but offset_e - offset_s could be the same in these two cases. What can I do to control the speed of animation based on given time in seconds/milliseconds?
From the tags of the question i can only see that you animate something drawn on canvas and thats why u cannot use css-animation or jquery-animation.
You have to control the length of the animation by calculating the time difference.
u can do it similar to this example
function start_animate(duration) {
var requestID;
var startTime =null;
var time ;
var animate = function(time) {
time = new Date().getTime(); //millisecond-timstamp
if (startTime === null) {
startTime = time;
}
var progress = time - startTime;
if (progress < duration ) {
if(offset_s < offset_e){
// change text color here
}
requestID= requestAnimationFrame(animate);
}
else{
cancelAnimationFrame(requestID);
}
requestID=requestAnimationFrame(animate);
}
animate();
}
trigger your animation and call start_animate(2000) //duration in millisecond 1000=1 sec
You should separate concerns clearly.
Have a single requestAnimationFrame running, which computes the current animation time and calls every update and draw related functions.
Then your animations would be handled by a function (or class instance if you go OOP) that deals with the current animation time.
Just some direction for the code :
var animationTime = -1;
var _lastAnimationTime = -1;
function launchAnimation() {
requestAnimationFrame(_launchAnimation);
}
function _launchAnimation(time) {
animationTime = 0;
_lastAnimationTime = time;
requestAnimationFrame(animate);
}
function animate(time){
requestAnimationFrame(animate);
var dt = time - _lastAnimationTime ;
_lastAnimationTime = time;
animationTime += dt;
// here call every draw / update functions
// ...
animationHandler.update(animationTime);
animationHandler.draw(context);
}
To start your 'engine', just call launchAnimation then you'll have a valid animationTime and dt to deal with.
I'd make animationHandler an instance of an AnimationHandler class, that allows to add/remove/update/draw animations.
I suggest to use setInterval function in JavaScript.
requestAnimationFrame really needs some 'ugly' calculations. Don't
believe me? Scroll up, you will see...
So, to make setInterval function as handy as rAF(requestAnimationFrame) store the function inside of variable. Here is an example:
var gameLoop = setInterval(function() {
update();
draw();
if (gameOver)
clearInterval(gameLoop);
}, 1000/FPS);
given way, you can control your FPS and pick correct velocity for your objects.
I typically do something like
es6
constructor() {
this.draw();
}
draw() {
const fps30 = 1000 / 30;
const fps60 = 1000 / 60;
window.requestAnimationFrame(() => {
setTimeout(this.draw.bind(this), fps30);
});
}
es5
function DrawingProgram() {
this.draw();
}
DrawingProgram.prototype.draw = function() {
var fps30 = 1000/30;
var fps60 = 1000/60;
var self = this;
window.requestAnimationFrame(function() {
window.setTimeout(function() {
self.draw(); // you could also use apply/call here if you want
}, fps30)
});
}

HTML Canvas animation wont run on mobile devices

The canvas animation runs effectively on web browsers but when testing on mobile browsers with iPad and iPhone the animation never starts. It just simply displays the background image. There are no error messages given.
The animation is basically an image that moves from offscreen on the left hand side of the canvas and stops when it reaches 75% of the canvas width.
Heres the code
<script>
window.addEventListener("load", eventWindowLoaded, false);
function eventWindowLoaded () {
start();
}
function canvasSupport () {
return Modernizr.canvas;
}
var canvas = document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var image1 = new Image();
image1.onload = function() {
ctx.clearRect(0, 0, 600, 400);
ctx.drawImage(image1, 0, 0);
}
image1.src="images/oven.jpg";
ctx.fillStyle = image1;
var currentX=cw;
var continueAnimating=true;
var nextMoveTime,maxMoves;
var expX = 50;
var expY = 200;
var image2 = new Image();
image2.onload=start;
image2.src="images/pies.png";
var image = new Image();
image.onload=start;
image.src="images/pies.png";
function start(){
maxMoves=(cw+image.width)*0.5;
nextMoveTime=performance.now();
requestAnimationFrame(animate);
function animate(currentTime){
if(continueAnimating){ requestAnimationFrame(animate); }
if(currentTime<nextMoveTime){return;}
nextMoveTime=currentTime; // + delay;
ctx.drawImage(image,currentX,193);
if(--currentX<-image.width){ currentX=cw; }
if(--maxMoves<0){continueAnimating=false;}
}
}
</script>
So the issue comes from your use of performance.now, which is not always implemented, especially on mobile devices where the power drain of a precise timer is too high.
Just use the time provided with the requestAnimationFrame : on accurate browsers/devices, it will use the sub-millisecond accuracy, otherwise it will have only millisecond accuracy.
(accurate = Chrome desktop for sure,... others ???)
I'll let you see below how i use the time of rAF to build the current 'dt' = elapsed time since last frame, and 'applicationTime' = time elapsed in the application (not counting when you tabbed out the app).
A secondary benefit of this method is that you can change easily the application speed to have 'bullet-time' or speed up (or even rewind if speed is <0).
fiddle is here :
http://jsfiddle.net/gamealchemist/KVDsc/
// current application time, in milliseconds.
var applicationTime = 0;
// scale applied to time.
// 1 means no scale, <1 is slower, >1 faster.
var timeSpeed = 1;
// after launchAnimation is called,
// draw/handleInput/update will get called on each rAF
function launchAnimation() {
requestAnimationFrame(_launchAnimation);
}
// ------------- Private methods ----------------
function _launchAnimation(now) {
_lastTime = now;
applicationTime = 0
requestAnimationFrame(_animate);
}
// ----------------------------------------------
// Animation.
// Use launchAnimate() to start the animation.
// draw, handleInput, update will be called every frame.
// ----------------------------------------------
function _animate(now) {
requestAnimationFrame(_animate);
// _______________________
var dt = now - _lastTime;
if (dt < 12) return; // 60 HZ max
if (dt > 200) dt = 16; // consider 1 frame elapse on tab-out
_lastTime = now;
dt *= timeSpeed;
applicationTime += dt;
// _______________________
handleInput(); // ...
// update everything with this frame time step.
update(dt);
// draw everything
draw();
}
var _lastTime = 0;
(( Notice that to handle most gracefully the tab-out, you have to handle the blur event, cancel the rAF, then set-it again on focus. ))
var now =
( typeof performance === 'object' && 'now' in performance )
? function() { return performance.now(); }
: function() { return ( new Date ).getTime(); };

Changing canvas drawn image on mouseover

I'm really struggling with the changing of my canvas drawn image so I thought I would see if anyone could assist me on here or offer advice.
I've drawn a static flag in canvas, and I've also drawn a waving flag. I'm trying to get this flag to wave on mouseover.
I initially thought that I was going to have to create two separate files, one for the static and one for the waving aspect. Then save each of them as a jpg/gif image using window.location = canvas.toDataURL("image/");.
But I've just discovered that you can apparently do this all in the same file via jquery/hover. Which seems a lot simpler and a more efficient way of doing it.
Here is the code for the waving flag:
window.onload = function(){
var flag = document.getElementById('banglaFlag');
banglaStatic( flag, 320 );
var timer = banglaWave( flag, 30, 15, 200, 200 );
};
function banglaStatic( canvas, width ){
//Drawing the Bangladesh flag.
//Declaring variables that regard width and height of the canvas.
//Variables C to L are needed for the waving function.
var a = width / 1.9;
var b = 200;
var c = 7*a/13;
var l = a / 13;
canvas.width = b;
canvas.height = a;
var ctx = canvas.getContext('2d');
var radius = 45;
};
function banglaWave( canvas, wavelength, amplitude, period, shading ){
var fps = 30;
var ctx = canvas.getContext('2d');
var w = canvas.width, h = canvas.height;
var od = ctx.getImageData(0,0,w,h).data;
// var ct = 0, st=new Date;
return setInterval(function(){
var id = ctx.getImageData(0,0,w,h);
var d = id.data;
var now = (new Date)/period;
for (var y=0;y<h;++y){
var lastO=0,shade=0;
for (var x=0;x<w;++x){
var px = (y*w + x)*4;
var o = Math.sin(x/wavelength-now)*amplitude*x/w;
var opx = ((y+o<<0)*w + x)*4;
shade = (o-lastO)*shading;
d[px ] = od[opx ]+shade;
d[px+1] = od[opx+1]+shade;
d[px+2] = od[opx+2]+shade;
d[px+3] = od[opx+3];
lastO = o;
}
}
ctx.putImageData(id,0,0);
// if ((++ct)%100 == 0) console.log( 1000 * ct / (new Date - st));
},1000/fps);
}
Thanks in advance for any advice/assistance.
I am not sure where your problem is. I did not see any event handling code, so I assume that's your question:
Define a function to "handle the mouse event". For example, if you want to move the flag when the user moves the mouse over it, define something like:
function mouseMove(event) {
var mouseX,
mouseY;
event.preventDefault(); // stops browser to do what it normally does
// determine where mouse is
mouseX = event.pageX;
mouseY = event.pageY;
// do something useful, e.g. change the flag to waving when mouse is over flag
}
Then, register this function to be called when the mouse moves:
canvas.addEventListener("mousemove", mouseMove, false);
canvas is the canvas you paint the flag on, "mousemove" is the name of the event (many more exist, such as "mousedown", "mouseup", "mouseout" (leaving canvas), "mousewheel", etc.), mouseMove is the name of your function (the event handler, as it's called).
Events are a little different from browser to browser (and even browser version), so you might need to implement different event handler if you need it across browsers.
Hoping this helped...
canvas is like a sheet. there is no any object on which you can hover.
for doing what you wanted to do is just,bound an area on the flag,
follow the 'virtualnobi' answer and calculate if mouse co-ordinate falls on that region,
if true do what ever you want.
like
if (mouseX<100 && mouseX>0 && mouseY>0 && mouseY<100){
//animate the flag
}
use mouseX=event.clientX;
mouseY=event.clientY;
bounded area is x=(0,100) , y=(0,100) here.

Categories