KinetcJS: Drag Performance is very bad when using Sprites - javascript

I use Kinetic Sprite like this
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function() {
var blob = new Kinetic.Sprite({
x: 250,
y: 40,
image: imageObj,
animation: 'idle',
animations: {
idle: [
// x, y, width, height (4 frames)
2,2,70,119,
71,2,74,119,
146,2,81,119,
226,2,76,119
],
punch: [
// x, y, width, height (3 frames)
2,138,74,122,
76,138,84,122,
346,138,120,122
]
},
frameRate: 7,
frameIndex: 0
});
// add the shape to the layer
layer.add(blob);
// add the layer to the stage
stage.add(layer);
// start sprite animation
blob.start();
var frameCount = 0;
blob.on('frameIndexChange', function(evt) {
if (blob.animation() === 'punch' && ++frameCount > 3) {
blob.animation('idle');
frameCount = 0;
}
});
document.getElementById('punch').addEventListener('click', function() {
alert("blob.width: "+blob.width());
}, false);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/blob-sprite.png';
The problem is that when using a sprite on mobile phone I get very poor drag performance. It is very slow to move or resize the sprite.
Is there a way to increase drag performance?
Edit: Here is an example with a Kinetic Image where dragging is very slow in Chrome for Android: http://jsfiddle.net/confile/brecmjzt/

Your performance problem is not the drag operation.
Refactor your code to not use frameIndexChange because it's extremely expensive to do a callback upon every sprite animation loop.

Related

Transparency of multiple tile layers without colors leaking (phaser.js)

So I have been using the Chebyshev Distance example from the Phaser labs, and while this example was using one layer, I happen to be using two, and when i set transparency on them, the colors start leaking into each other, especially on light colors.
Is there any way to circumvent or get rid of this effect
If the problem is that you have two layers, one ontop of the other and you are making both transparent (or only the top one), and you don't want that color to pass through, the solution could be to hide the tiles on the bottom layer.
Just check in the map-tile-loop, if the tile, where you want to change the alpha, has a tile beneath it, and if so make that background tile transparent.
Here a small working demo:
(The main magic is in the updateMap function)
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
scene: {
preload,
create
}
};
var player;
var bgLayer;
var point1 = {x: 250, y: 31};
var isLeaking = false;
new Phaser.Game(config);
function preload (){
this.load.image('tiles', 'https://labs.phaser.io/assets/tilemaps/tiles/catastrophi_tiles_16.png');
this.load.tilemapCSV('map', 'https://labs.phaser.io/assets/tilemaps/csv/catastrophi_level2.csv');
}
function create () {
this.add.text(50, 1, ' <- Background is visible, if no tiles are ontop')
.setOrigin(0)
.setDepth(100)
.setStyle({fontFamily: 'Arial'});
this.infoText = this.add.text(10, 20, 'Click to toggle leaking: on')
.setOrigin(0)
.setDepth(100)
.setStyle({fontFamily: 'Arial'});
// Just creating image for second layer tiles //
let graphics = this.make.graphics();
graphics.fillStyle(0xff0000);
graphics.fillRect(0, 0, 16, 16);
graphics.generateTexture('tiles2', 16, 16);
// Just creating image for second layer tiles //
let map = this.make.tilemap({ key: 'map', tileWidth: 16, tileHeight: 16 });
let tileset = map.addTilesetImage('tiles');
let tileset2 = map.addTilesetImage('tiles2');
bgLayer = map.createBlankLayer('background', tileset2);
bgLayer.fill(0);
let fgLayer = map.createLayer(0, tileset, 0, 0);
// Just to show that the Background is still show if not Tile is covering
fgLayer.removeTileAt(0, 0);
fgLayer.removeTileAt(1, 0);
fgLayer.removeTileAt(2, 0);
player = this.add.rectangle(point1.x, point1.y, 5, 5, 0xffffff, .5)
.setOrigin(.5);
this.input.on('pointerdown', () => {
isLeaking = !isLeaking;
this.infoText.setText( `Click to toggle leaking: ${isLeaking?'off':'on'}` )
updateMap(map);
});
updateMap(map);
}
function updateMap (map) {
let originPoint1 = map.getTileAtWorldXY(point1.x, point1.y);
console.info(map.layers.sort((a,b) => b.depth - a.depth))
map.forEachTile(function (tile) {
var dist = Phaser.Math.Distance.Chebyshev(
originPoint1.x,
originPoint1.y,
tile.x,
tile.y
);
let bgTile = bgLayer.getTileAt(tile.x, tile.y, false)
let hideOnlyTheseTiles = [ 0, 1, 2, 3, 4]; // Indexes to hide
if( !isLeaking ){
if(hideOnlyTheseTiles.indexOf(bgTile.index) > -1){ // here yopu can select the
bgTile.setAlpha(0);
}
} else{
bgTile.setAlpha(1);
}
tile.setAlpha(1 - 0.09 * dist);
});
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

KineticJS: How do I get the Width and Height of a Sprite or Image?

I loaded a sprite from a URL and wanted to get the width and height of the sprite it turns out that the width and height is alway zero. I created the following example.
How do I get the real width and height of the sprite?
I use the sprite example and created a jsfiddle here: http://jsfiddle.net/confile/jVG9t/
This is the html code:
<div id="container"></div>
<div id="buttons">
<input type="button" id="punch" value="Width">
</div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.1.0.min.js"></script>
Image <br>
<div id="mydiv" ></div>
This is the JS code:
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function() {
var blob = new Kinetic.Sprite({
x: 250,
y: 40,
image: imageObj,
animation: 'idle',
animations: {
idle: [
// x, y, width, height (4 frames)
2,2,70,119,
71,2,74,119,
146,2,81,119,
226,2,76,119
],
punch: [
// x, y, width, height (3 frames)
2,138,74,122,
76,138,84,122,
346,138,120,122
]
},
frameRate: 7,
frameIndex: 0
});
// add the shape to the layer
layer.add(blob);
// add the layer to the stage
stage.add(layer);
// start sprite animation
blob.start();
var frameCount = 0;
blob.on('frameIndexChange', function(evt) {
if (blob.animation() === 'punch' && ++frameCount > 3) {
blob.animation('idle');
frameCount = 0;
}
});
document.getElementById('punch').addEventListener('click', function() {
alert("blob.width: "+blob.width());
}, false);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/blob-sprite.png';
You can get the width/height of the whole imageObj by requesting its .width & .height properties:
var imageObj = new Image();
imageObj.onload = function() {
var width=imageObj.width;
var height=imageObj.height;
}
If the imageObj is a spritesheet then there is no way to programatically get the x, y, width, height of the individual sprites on that spritesheet. Often the spritesheet author will give you this information. You can often eyeball the spritesheet and see how its laid out.
Your KineticJS demo required the coder to know where the x,y,width,height of each sprite on the spritesheet.

Rotate image to angle on click with Kineticjs

I am creating a speedometer with with kineticjs and I need the ability to rotate the needle it to a specified angle when the user clicks a button. I also need to be able to set the rotation point as well since it is not in the center.
I am using jQuery 1.8.3 and kinetic 1.5.1
Here is what I have so far:
var stage = new Kinetic.Stage({
container: 'needle',
width: 152,
height: 152
});
var layer = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function() {
var needle = new Kinetic.Image({
x: 63,
y: 65,
image: imageObj,
width: 83,
height: 22
});
// add the shape to the layer
layer.add(needle);
// add the layer to the stage
stage.add(layer);
};
imageObj.src = 'http://example.com/assets/img/layout/dials/speedo_needle.png'); ?>';
So taking this example how would I rotate the needle to whatever angle I want in an interval or click event?
To rotate the image, use a function with imageObject.setRotation(45) where '45' is number of degrees.
To define the rotation point, set offset propertie for imageObject, or change it dynamically with imageObject.setOffsetX(100) and imageObject.setOffsetY(100) or any other number.

KineticJS: How do I Crop an Image?

I use KineticJs and want to crop an existing image. I tried the following:
var image = new Kinetic.Image({}); // some kinetic image
var layer = new Kinetic.Layer();
var stage = new Kinetic.Stage({
container: "iPhone",
width: 1000,
height: 1000
});
image.crop({
x: 0,
y : 0,
width : 100,
height : 100
});
image = image.crop();
layer.add(image);
layer.draw();
stage.add(layer);
The former does not work. How do I crop an existing image with KineticJs?
You should pass javascript object with x, y, width and height properties to crop function:
image.crop({
x: 10,
y : 10,
width : 66,
height : 60
});
http://jsbin.com/zubek/2/edit

Understanding placing images in Kinetic.JS?

Below I have some code for an assignment I'm working on. Having been placed in a 2nd year course without taking foundations of JavaScript, I'm kind of in a rut.
I'm trying to place three images, and be able to call on them later with functions to change their opacity.
When I have the images placed, only one seems to want to appear at a time, and when I call to them, I'm told that they don't exist.
// Create the canvas based on the existing div for it in the HTML file
var stage = new Kinetic.Stage
({
container: 'canvasContainer',
width: 600,
height: 600
});
/* /////////////////////////////////////////////////////
C O D E F O R L O A D I N G U P I M A G E S
///////////////////////////////////////////////////// */
var characters = new Kinetic.Layer();
stage.add(characters);
window.onload = function()
{
var majoraCharacter = new Image();
majoraCharacter.src ="https://upload.wikimedia.org/wikipedia/commons/3/34/Red_star.svg"
majoraCharacter.onload = function()
{
character1= new Kinetic.Image({ x: 400, y: 300, width: 150, height: 150, offset: {x: 75, y: 75}, image: majoraCharacter});
characters.add(character1);
characters.draw();
}
}
window.onload = function()
{
var amaterasuCharacter = new Image();
amaterasuCharacter.src ="https://upload.wikimedia.org/wikipedia/commons/3/34/Red_star.svg"
amaterasuCharacter.onload = function()
{
character2= new Kinetic.Image({ x: 400, y: 200, width: 150, height: 150, offset: {x: 75, y: 75}, opacity:0.5, image: amaterasuCharacter});
characters.add(character2);
characters.draw();
}
}
window.onload = function()
{
var toothlessCharacter = new Image();
toothlessCharacter.src ="https://upload.wikimedia.org/wikipedia/commons/3/34/Red_star.svg"
toothlessCharacter.onload = function()
{
character3= new Kinetic.Image({ x: 400, y: 100, width: 150, height: 150, offset: {x: 75, y: 75}, image: toothlessCharacter});
characters.add(character3);
characters.draw();
}
}
/* /////////////////////////////////////////////////////
C A N V A S F U N C T I O N A L I T Y
///////////////////////////////////////////////////// */
// Create new layer for background images
// Create layer for character images
var character = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x: 100,
y: 100,
width: 150,
height: 150,
fill: 'grey',
strokeWidth: 4,
offset: {x: 75, y: 75},
});
// add the shape to the layer
character.add(rect);
// add the layer to the stage
stage.add(character);
Is there a way I can better add images to canvas?
And what is preventing them from co-existing, and being editable outside of their loading functions?
Any and all answers are greatly appreciated!
A Demo: http://jsfiddle.net/m1erickson/Bf88D/
Some KineticJS basics:
KineticJS starts with a stage.
The stage is a container for one or more layers which are added to the stage
Each layer is actually a canvas
Shapes (including images) are added to a layer.
Since you're new to javascript, here's an outline of how to structure your project
just use one window.onload and put all your javascript in that one window.onload
load all images in advance so they are available to draw on your canvas
When all the images are loaded, then create Kinetic.Image objects from them.
Here's an image loader that loads all your images in advance and then calls the start function when your images are fully loaded.
// image loader
var imageURLs=[]; // put the paths to your images here
var imagesOK=0;
var imgs=[];
imageURLs.push("");
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
function start(){
// the imgs[] array holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
// use the images now!
}
After you have all your images loaded, you can use this reusable function to add Kinetic Images on the layer.
You send in one image that you've loaded in advance.
You also send in the x,y position where you want the image located.
You also send in the width,height that you want the image resized to.
This function adds one new image to the layer
function addKineticImage(image,x,y,w,h){
var image = new Kinetic.Image({
x:x,
y:y,
width:w,
height:h,
image:image,
draggable: true
});
layer.add(image);
layer.draw();
}

Categories