Kinetic.js [How can I stop one sprite] - javascript

I´m starting with Kinetic.js and I have my first app working perfectly, but I don´t find the way to stop sprites... for example, I have one sprite and I want to "play" that sprite just once, see the frames and in the last one, one simple stop(). How can I do that? (Stop after the last "frame" of the sprite).
Here´s my code (for that sprite):
var myImage = new Image();
myImage.onload = function() {
var mySprite = new Kinetic.Sprite({
x: 250,
y: 40,
width: 70,
height: 109,
image: myImage,
animation: 'idle',
animations: animations,
frameRate: 5
});
layer1.add(mySprite);
mySprite.start();
};
myImage.src = 'sheet2.png';
Thanks in advance!

You can use the afterFrame function, which will enable you to trigger an event after a specific frame.
Then you can call stop() after frame 5, for example. (you'll need to update this to however many frames there are in your sprite.)
mySprite.afterFrame(5, function(){
mySprite.stop();
});
You can see an example of the use of afterFrame in the following two posts:
http://ramkulkarni.com/blog/sprite-animation-in-html5-canvas-with-kineticjs/
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-sprite-tutorial/

Related

Phaser 3: Change "Hitbox"/Interactive area of sprite without physics

The game I'm creating doesn't require any physics, however you are able to interact when hovering over/clicking on the sprite by using sprite.setInteractive({cursor: "pointer"});, sprite.on('pointermove', function(activePointer) {...}); and similar. However I noticed two issues with that:
The sprite has some area which are transparent. The interactive functions will still trigger when clicking on those transparent areas, which is unideal.
When playing a sprite animation, the interactive area doesn't seem to entirely (at all?) change, thus if the sprite ends on a frame bigger than the previous, there end up being small areas I can't interact with.
One option I thought of was to create a polygon over my sprite, which covers the area I want to be interactive. However before I do that, I simply wanted to ask if there are simpler ways to fix these issues.
Was trying to find an answer for this myself just now..
Think Make Pixel Perfect is what you're looking for.
this.add.sprite(x, y, key).setInteractive(this.input.makePixelPerfect());
https://newdocs.phaser.io/docs/3.54.0/focus/Phaser.Input.InputPlugin-makePixelPerfect
This might not be the best solution, but I would solve this problem like this. (If I don't want to use physics, and if it doesn't impact the performance too much)
I would check in the event-handler, if at the mouse-position the pixel is transparent or so, this is more exact and less work, than using bounding-boxes.
You would have to do some minor calculations, but it should work well.
btw.: if the origin is not 0, you would would have to compensate in the calculations for this. (in this example, the origin offset is implemented)
Here is a demo, for the click event:
let Scene = {
preload ()
{
this.load.spritesheet('brawler', 'https://labs.phaser.io/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
},
create ()
{
// Animation set
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('brawler', { frames: [ 0, 1, 2, 3 ] }),
frameRate: 8,
repeat: -1
});
// create sprite
const cody = this.add.sprite(200, 100).setOrigin(0);
cody.play('walk');
cody.setInteractive();
// just info text
this.mytext = this.add.text(10, 10, 'Click the Sprite, or close to it ...', { fontFamily: 'Arial' });
// event to watch
cody.on('pointerdown', function (pointer) {
// calculate x,y position of the sprite to check
let x = (pointer.x - cody.x) / (cody.displayWidth / cody.width)
let y = (pointer.y - cody.y) / (cody.displayHeight / cody.height);
// just checking if the properties are set
if(cody.anims && cody.anims.currentFrame){
let currentFrame = cody.anims.currentFrame;
let pixelColor = this.textures.getPixel(x, y, currentFrame.textureKey, currentFrame.textureFrame);
// alpha > 0 a visible pixel of the sprite, is clicked
if(pixelColor.a > 0) {
this.mytext.text = 'Hit';
} else {
this.mytext.text = 'No Hit';
}
// just reset the textmessage
setTimeout(_ => this.mytext.text = 'Click the Sprite, or close to it ...' , 1000);
}
}, this);
}
};
const config = {
type: Phaser.AUTO,
width: 400,
height: 200,
scene: Scene
};
const game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

Raphael.js animate of image doesn't repaint in chrome

It seems to happen only when I try to animate an image on Chrome. All I want to do is make an image to move back an forth in Raphaël.js. I created a jsfiddle that illustrates the problem. I'm very sure that it used to work in Chrome since I use it to develop and it seems to be broken in later versions of Chrome. When I change the image to a rect for example it seems to render fine. When you resize the screen that contains the animation it repaints.
http://jsfiddle.net/k69yzz0o/1/
var moveForth = function () {
useControl.animate({x : 38, y: 0}, 900, moveBack);
};
var moveBack = function () {
useControl.animate({x : 0, y: 0}, 600, moveForth);
};
var R = Raphael("holder", 500, 500);
useControl = R.image("http://i.imgur.com/ta8zlD2.png", 0, 0, 189, 18);
moveForth();
It only happens in Chrome and I use latest Raphael.js 2.1.2.
How can I resolve this issue?
Yes, there appears to be an issue with Chrome recognizing the rect as dirty, and refusing to repaint it. You can see that it's working by mousing over the area.
I tried the same animation using a Transform rather than using the actual position. Transforming an object evidently correctly tells modern Chrome to redraw that area.
Here it is working:
var moveForth = function () {
useControl.animate({"transform":"T38,0"}, 900, ">", moveBack);
};
var moveBack = function () {
useControl.animate({"transform":""}, 600, "<", moveForth);
};
var R = Raphael("holder", 500, 500);
useControl = R.image("http://i.imgur.com/ta8zlD2.png", 0, 0, 189, 18);
moveForth();
I also added some easing (">") to give it a more natural bounce.

Group transition not drawn to canvas until forced by other actions

I am trying to transitionTo a group and it does it, but I can see the transition only when something else forces stage to be drawn. Transition itself doesn’t update the canvas while it’s going. There are 4 Kinetic.Image's and 4 Kinetic.Text's inside the group. Any idea how to get it working?
Let's say #score group x: 1000
var points = self.stage.get('#scoreGroup')[0];
points.transitionTo({
x: 800,
duration: 5
});
I'm afraid more code would be needed to actually tell what might be missing, but my guess is that you added the elements to the group, which was already in a layer, which was already on the stage. Your transitionTo is being rendered in the buffer, but because the elements were never "drawn" on the stage, the animation isn't being translated to the viewable stage. Perhaps if you provide more code or create a jsFiddle of this, I can provide more insight.
In the meantime, make sure your 4 images and 4 text objects appear on the stage before you call the transitionTo (comment out the transition temporarily), then give it another go.
var stage = new Kinetic.Stage({container : 'container', width : 800, height : 600});
var layer = new Kinetic.Layer();
var group = new Kinetic.Group({id : 'scoreGroup', x : 0, y : 0});
layer.add(group);
stage.add(layer);
// add images and text as usual
var imageObj = new Image();
imageObj.onload = function() {
var image = new Kinetic.Image({x : 20, y : 20, width : 100, height : 100, image : imageObj});
group.add(image);
};
imageObj.src = '[path_to_image]';
layer.draw();
group.transitionTo({x : 600, y : 400, duration : 3});
Let me know what you come up with.

Zoom and Pan in KineticJS

Is there a way one could zoom and pan on a canvas using KineticJS? I found this library kineticjs-viewport, but just wondering if there is any other way of achieving this because this library seems to be using so many extra libraries and am not sure which ones are absolutely necessary to get the job done.
Alternatively, I am even open to the idea of drawing a rectangle around the region of interest and zooming into that one particular area. Any ideas on how to achieve this? A JSFiddle example would be awesome!
You can simply add .setDraggable("draggable") to a layer and you will be able to drag it as long as there is an object under the cursor. You could add a large, transparent rect to make everything draggable. The zoom can be achieved by setting the scale of the layer. In this example I'm controlling it though the mousewheel, but it's simply a function where you pass the amount you want to zoom (positive to zoom in, negative to zoom out). Here is the code:
var stage = new Kinetic.Stage({
container: "canvas",
width: 500,
height: 500
});
var draggableLayer = new Kinetic.Layer();
draggableLayer.setDraggable("draggable");
//a large transparent background to make everything draggable
var background = new Kinetic.Rect({
x: -1000,
y: -1000,
width: 2000,
height: 2000,
fill: "#000000",
opacity: 0
});
draggableLayer.add(background);
//don't mind this, just to create fake elements
var addCircle = function(x, y, r){
draggableLayer.add(new Kinetic.Circle({
x: x*700,
y: y*700,
radius: r*20,
fill: "rgb("+ parseInt(255*r) +",0,0)"
})
);
}
var circles = 300
while (circles) {
addCircle(Math.random(),Math.random(), Math.random())
circles--;
}
var zoom = function(e) {
var zoomAmount = e.wheelDeltaY*0.001;
draggableLayer.setScale(draggableLayer.getScale().x+zoomAmount)
draggableLayer.draw();
}
document.addEventListener("mousewheel", zoom, false)
stage.add(draggableLayer)
http://jsfiddle.net/zAUYd/
Here's a very quick and simple implementation of zooming and panning a layer. If you had more layers which would need to pan and zoom at the same time, I would suggest grouping them and then applying the on("click")s to that group to get the same effect.
http://jsfiddle.net/renyn/56/
If it's not obvious, the light blue squares in the top left are clicked to zoom in and out, and the pink squares in the bottom left are clicked to pan left and right.
Edit: As a note, this could of course be changed to support "mousedown" or other events, and I don't see why the transformations couldn't be implemented as Kinetic.Animations to make them smoother.
this is what i have done so far.. hope it will help you.
http://jsfiddle.net/v1r00z/ZJE7w/
I actually wrote kineticjs-viewport. I'm happy to hear you were interested in it.
It is actually intended for more than merely dragging. It also allows zooming and performance-focused clipping. The things outside of the clip region aren't rendered at all, so you can have great rendering performance even if you have an enormous layer with a ton of objects.
That's the use case I had. For example, a large RTS map which you view via a smaller viewport region -- think Starcraft.
I hope this helps.
As I was working with Kinetic today I found a SO question that might interest you.
I know it would be better as a comment, but I don't have enough rep for that, anyway, I hope that helps.
These answers seems not to work with the KineticJS 5.1.0. These do not work mainly for the signature change of the scale function:
stage.setScale(newscale); --> stage.setScale({x:newscale,y:newscale});
However, the following solution seems to work with the KineticJS 5.1.0:
JSFiddle: http://jsfiddle.net/rpaul/ckwu7u86/3/
Unfortunately, setting state or layer draggable prevents objects not draggable.
Duopixel's zooming solution is good, but I would rather set it for stage level, not layer level.
Her is my solution
var stage = new Kinetic.Stage({
container : 'container',
width: $("#container").width(),
height: $("#container").height(),
});
var layer = new Kinetic.Layer();
//layer.setDraggable("draggable");
var center = { x:stage.getWidth() / 2, y: stage.getHeight() / 2};
var circle = new Kinetic.Circle({
x: center.x-100,
y: center.y,
radius: 50,
fill: 'green',
draggable: true
});
layer.add(circle);
layer.add(circle.clone({x: center.x+100}));
// zoom by scrollong
document.getElementById("container").addEventListener("mousewheel", function(e) {
var zoomAmount = e.wheelDeltaY*0.0001;
stage.setScale(stage.getScale().x+zoomAmount)
stage.draw();
e.preventDefault();
}, false)
// pan by mouse dragging on stage
stage.on("dragstart dragmove", function(e) {window.draggingNode = true;});
stage.on("dragend", function(e) { window.draggingNode = false;});
$("#container").on("mousedown", function(e) {
if (window.draggingNode) return false;
if (e.which==1) {
window.draggingStart = {x: e.pageX, y: e.pageY, stageX: stage.getX(), stageY: stage.getY()};
window.draggingStage = true;
}
});
$("#container").on("mousemove", function(e) {
if (window.draggingNode || !window.draggingStage) return false;
stage.setX(window.draggingStart.stageX+(e.pageX-window.draggingStart.x));
stage.setY(window.draggingStart.stageY+(e.pageY-window.draggingStart.y));
stage.draw();
});
$("#container").on("mouseup", function(e) { window.draggingStage = false } );
stage.add(layer);
http://jsfiddle.net/bighostkim/jsqJ2/

Restarting animated Gif

I want to put a timer onto my gallery rotation script. This timer is a circular animated gif. i want it to start again when a new image loads..
I would have thought that this would work but apparently not.
// Loader needs to be a Global
loader = new Image();
loader.id = "loader_graphic";
loader.src = "images/loader.gif";
var $container = $('#slider .imgs').cycle({
fx: 'scrollHorz',
speed: 1000,
timeout: 5000,
before : function() {
resetLoader();
}
});
function resetLoader() {
$("#loader_graphic").remove();
$("#loader").append(loader);
}
Does anyone have any ideas on fixing this. If this cannot work, does anyone know how to achieve this effect?
Your javascript code doesn't recreate the variable when resetting, perhaps setting the variable to a newly created dom element would work?
// Loader needs to be a Global
var loader = newLoaderImage();
var $container = $('#slider .imgs').cycle({
fx: 'scrollHorz',
speed: 1000,
timeout: 5000,
before : function() {
resetLoader();
}
});
function newLoaderImage() {
i = new Image();
i.id = "loader_graphic";
i.src = "images/loader.gif";
return i;
}
function resetLoader() {
$("#loader_graphic").remove(); // or loader.remove();
loader = newLoaderImage();
$("#loader").append(loader);
}
Maybe you can use a workaround with JavaScript but restarting is an option for a gif image, it would be simpler to modify it (with gimp eg.).
One approach might be to use two images: one animated gif, and a static gif that shows the "stopped" frame of the animation.
Position them in the same place. When the image is loading, you show the animated gif (probably using visibility:visible; and hide the stopped one, using visibility: hidden;. When you need the animation to stop, hide the animated gif, and show the stopped image.

Categories

Resources