I have stripped back my Fiddle to show my issue.
I have a Rectagle that I am looping so it gives a different name with multiple instances. I can add and remove the rectangles fine. My issue is that I am replacing this rectangle with an image and I can get it to loop ok, but it will only remove 1 image and not them all. I think I am doing something wrong with the name, but i can not see the wood through the trees.
JSFiddle
var canvas = new fabric.Canvas('c', { selection: false });
var land = [];
var turret = [];
var wh=[];
var op=[];
//////////////////////////////////////////////////////////////////
// Outpost Level Dropmenu Select
$(document).ready(function () {$('#method').change(
function () {
var method = $('option:selected').val();
if (this.value == "0") {
for (var i=0; i<96; i++){
canvas.remove(land[i]);
canvas.remove(turret[i]);
canvas.remove(wh[i]);
canvas.remove(op[i]);
};
} else if (this.value == "1") {
//Clear All
for (var i=0; i<96; i++){
canvas.remove(land[i]);
canvas.remove(turret[i]);
canvas.remove(wh[i]);
};
//Add Buildings
for (var i=0; i<40; i++){
land[i] = new fabric.Rect({
fill: 'green',left: 25,top: 25,width: 25,height: 25,
perPixelTargetFind: true,hasBorders: false,hasControls:false, hasRotatingPoint: false,
});
canvas.add(land[i])
};
for (var i=0; i<6; i++){
turret[i] = new fabric.Rect({
fill: 'brown',left: 75,top: 25,width: 15,height: 15,
perPixelTargetFind: true,hasBorders: false,hasControls: false,hasRotatingPoint: false,
});
canvas.add(turret[i])
};
for (var i=0; i<3; i++){
fabric.Image.fromURL('http://www.ahoymearty.co.uk/baseplanner2/images/buildings/warehouse.png', function(myImgwh) {wh[i] = myImgwh.set({ left: 25, top: 75 ,width:25,height:25, hasControls: false, hasRoatatingPoint: false,stroke: 'blue',strokeWidth: 1,
});
canvas.add(wh[i]);
});
};
}
});
});
All 3 of your images are added async with a reference to i and the reference to i is 3 when all images are added. if you look at your 'wh' array you have a bunch of undefineds and then only 1 object added to the array. that is why you can only delete one object.
this JSFiddle just gets a new id in the callback which sets the array up correctly and then they can be deleted since you will now have all the proper references.
if it's acceptable to clear the whole canvas then this you can use the context -fiddle here https://fiddle.jshell.net/2xozu3wk/ as discussed in this query How to undraw, hide, remove, or delete an image from an html canvas? I think all but one of your images is being committed to the canvas and images paint pixels in a manner that's not so easy to clean up. Alternatively you can blank the canvas and reinstate elements you wish to retain but be prepared for a flicker?
var canvas = new fabric.Canvas('c', { selection: false });
var context = canvas.getContext('2d');
.
.
.
if (this.value == "0") {
context.clearRect(0, 0, canvas.width, canvas.height);
}
don't know if that helps
Related
I found this great tutorial on creating draggable maps with inertia: http://www.emanueleferonato.com/2016/01/18/how-to-create-a-html-draggable-and-scrollable-map-with-inertia-using-phaser-framework/
The tutorial has the dragging effect applied on just an image. I'm trying to have the dragging effect applied on a group of sprites instead, so they would all drag at the same time (map image + group of sprites).
The issue I have is that I'm a little confused about what this.scrollingMap represents syntax-wise. So when it comes to replacing this line with a group, I'm a little lost.
this.scrollingMap = game.add.image(0, 0, "map");
Anyone have any ideas?
I copied the simplified code below as well if that helps.
var game = new Phaser.Game(window.innerWidth, window.innerHeight, Phaser.AUTO, '', { preload: preload, create: create, update: update });
function preload() {
game.load.image('map', 'assets/images/baseMap.png');
game.load.image('star', 'assets/images/star.png');
}
function create() {
// Creating the group
world = game.add.group();
world.create(0, 0, 'map');
// Adding random sprites to it
for (var i = 0; i < 10; i++)
{ world.create(game.world.randomX, game.world.randomY, 'star');}
//This group works on its own.
//I would like to link it to the dragging animation below "scrollingMap".
//The Draggable Map from the tutorial
// Adding the big map to scroll
this.scrollingMap = game.add.image(0, 0, "map"); //<-- This is where I am having trouble changing from an image to a group.
// map will accept inputs
this.scrollingMap.inputEnabled = true;
// map can be dragged
this.scrollingMap.input.enableDrag(false);
// custom property: we save map position
this.scrollingMap.savedPosition = new Phaser.Point(this.scrollingMap.x, this.scrollingMap.y);
// custom property: the map is not being dragged at the moment
this.scrollingMap.isBeingDragged = false;
// custom property: map is not moving (or is moving at no speed)
this.scrollingMap.movingSpeed = 0;
// map can be dragged only if it entirely remains into this rectangle
this.scrollingMap.input.boundsRect = new Phaser.Rectangle(game.width - this.scrollingMap.width, game.height - this.scrollingMap.height, this.scrollingMap.width * 2 - game.width, this.scrollingMap.height * 2 - game.height);
// when the player starts dragging...
this.scrollingMap.events.onDragStart.add(function(){
this.scrollingMap.isBeingDragged = true;
// set movingSpeed property to zero. This will stop moving the map
// if the player wants to drag when it's already moving
this.scrollingMap.movingSpeed = 0;
}, this);
// when the player stops dragging...
this.scrollingMap.events.onDragStop.add(function(){
this.scrollingMap.isBeingDragged = false;
}, this);
} //End create function
function update() {
// if the map is being dragged...
if(this.scrollingMap.isBeingDragged){
this.scrollingMap.savedPosition = new Phaser.Point(this.scrollingMap.x, this.scrollingMap.y);
}
// if the map is NOT being dragged...
else{
// if the moving speed is greater than 1...
if(this.scrollingMap.movingSpeed > 1){
this.scrollingMap.x += this.scrollingMap.movingSpeed * Math.cos(this.scrollingMap.movingangle);
this.scrollingMap.y += this.scrollingMap.movingSpeed * Math.sin(this.scrollingMap.movingangle);
if(this.scrollingMap.x < game.width - this.scrollingMap.width){
this.scrollingMap.x = game.width - this.scrollingMap.width;
}
if(this.scrollingMap.x > 0){
this.scrollingMap.x = 0;
}
if(this.scrollingMap.y < game.height - this.scrollingMap.height){
this.scrollingMap.y = game.height - this.scrollingMap.height;
}
if(this.scrollingMap.y > 0){
this.scrollingMap.y = 0;
}
this.scrollingMap.movingSpeed *= friction;
// save current map position
this.scrollingMap.savedPosition = new Phaser.Point(this.scrollingMap.x, this.scrollingMap.y);
}
// if the moving speed is less than 1...
else{
var distance = this.scrollingMap.savedPosition.distance(this.scrollingMap.position);
var angle = this.scrollingMap.savedPosition.angle(this.scrollingMap.position);
if(distance > 4){
this.scrollingMap.movingSpeed = distance * speedMult;
this.scrollingMap.movingangle = angle;
}
}
}
}
So, I ended up finding the solution...
First off, I removed all this.scrollingMap and changed them to scrollingMap to remove any confusion. Ended up stil working perfectly.
scrollingMap = game.add.image(0, 0, "map");
scrollingMap.anchor.set(0.05,0.5);
scrollingMap.inputEnabled = true;
[etc...]
Next, Groups in Phaser don't seem to be able to have input working on elements together, only one at a time. So changing to something like wouldn't work:
scrollingMap = game.add.group();
map = game.add.image(0, 0, "map");
scrollingMap.add(map);
// The following line won't work
scrollingMap.inputEnabled = true;
I tried using the Align functions that Phaser offers... Until I ended up realising that you can actually nest sprites within other sprites like so:
scrollingMap = game.add.image(0, 0, "map");
someSprite = game.add.image(100, 100, "sprite");
scrollingMap.addChild(someSprite);
And voila! There's the solution, as simple as that.
Note that you can also add groups as children:
someGroup = game.add.group();
scrollingMap.addChild(someGroup);
Found the solution here if anyone's curious:
http://www.html5gamedevs.com/topic/7745-move-a-group-of-sprites-together-as-one-body/
I sell custom equipment and on my site, I have a flash tool where customers can assign colors to glove parts and see what it will look like.
I've been working on a HTML5 version of this tool, so the iPad crowd can do the same thing. Click here for what I've done,
I took kineticjs multiple picture loader and hacked it to load all the pics necessary to stage and the color buttons, which are multiple instances of the same image. In their example, it was only 2 images, so they var name each image, which were manipulative. My goal is to dynamically create variable, based on image name.
I'm using a for loop and if statements to position the parts according to their type. If the image being loaded is a button, the original instance is not added to the stage, but another for loop, with a counter, creates instances and put on the stage. The variable is part string+n (wht0). From here I initiate an eventlistener, when clicked is suppose to hide all glove parts pertaining to the option and show the appropriate color. That code I have already in my AS.
I created an eventlistener on the white buttons (first button) that when clicked, I set it to hide one of the white leather part of glove. But when I click the button, I get the error in console that the glove part (ex wlt_wht), I get an error stating that the object is not defined. But when the image was loaded the variable name came from the current array object being loaded.
I added another variable before the callback call, to convert the content of the array to a string and used the document.write to confirm that the object name is correct, but after creating the object its now [object object]. In flash, you manually assign the movie clip name and target.name is available if you call it.
How can I write the Image obj so I can control the object? In the doc there is a reference for id and name as properties of the object, but when I set these, it did not work with me. Sure, I could have manually created each Kinetic.Image(), but there's no fun in that.. especially with 191 images. Any tip on how I can get around this problem?
Checkout http://jsfiddle.net/jacobsultd/b2BwU/6/ to examine and test script.
function loadImages(sources, callback) {
var assetDir = 'http://dev.nystixs.com/test/inf/';
var fileExt = '.png';
var images = {};
var loadedImages = 0;
var numImages = 0;
for (var src in sources) {
numImages++;
}
for (var src in sources) {
images[src] = new Image();
images[src].onload = function () {
var db = sources[src].toString();
var dbname = db.slice(-0, -4);
if (++loadedImages >= numImages) {
callback(images, dbname);
}
};
images[src].src = assetDir + sources[src];
//images[src].src = assetDir+sources[src]+".png";
}
}
function initStage(images, db) {
var shapesLayer = new Kinetic.Layer();
var messageLayer = new Kinetic.Layer();
//Loading Images
var xpos = 0;
var ypos = 200;
for (var i in images) {
var glvP = i.slice(0, 3);
db = new Kinetic.Image({
image: images[i],
x: xpos,
y: ypos
});
if (glvP == "wlt") {
shapesLayer.add(db);
db.setPosition(186.95, 7.00);
//db.hide();
shapesLayer.draw();
} else if (glvP == "lin") {
shapesLayer.add(db);
db.setPosition(204.95, 205.00);
} else if (glvP == "plm") {
shapesLayer.add(db);
db.setPosition(311.95, 6.00);
} else if (glvP == "web") {
shapesLayer.add(db);
db.setPosition(315.95, 7.00);
} else if (glvP == "lce") {
shapesLayer.add(db);
db.setPosition(162.95, 3.00);
} else if (glvP == "thb") {
shapesLayer.add(db);
db.setPosition(63.00, 28.60);
} else if (glvP == "bfg") {
shapesLayer.add(db);
db.setPosition(167.95, 7.00);
} else if (glvP == "wst") {
shapesLayer.add(db);
db.setPosition(208.95, 234.00);
} else if (glvP == "fpd") {
shapesLayer.add(db);
db.setPosition(252.95, 82.00);
} else if (glvP == "bac") {
shapesLayer.add(db);
db.setPosition(0, 0);
} else if (glvP == "bnd") {
shapesLayer.add(db);
db.setPosition(196.95, 164.00);
} else {}
var rect = new Kinetic.Rect({
x: 710,
y: 6,
stroke: '#555',
strokeWidth: 5,
fill: '#ddd',
width: 200,
height: 325,
shadowColor: 'white',
shadowBlur: 10,
shadowOffset: [5, 5],
shadowOpacity: 0.2,
cornerRadius: 10
});
shapesLayer.add(rect);
// End of Glove Parts Tabs
//Load Color Buttons
if (glvP == "wht") {
xpos = -5.00;
bpos = 375;
var zpos = -5.00;
var tpos = -5.00;
db.setPosition(xpos, bpos);
//shapesLayer.add(db);
var n = 0;
for (n = 0; n < 12; n++) {
if (n < 4) {
var glvB = "wht" + n;
var btn = glvB;
glvB = new Kinetic.Image({
image: images[i],
width: 18,
height: 18,
id: 'wht0'
});
glvB.on('mouseout', function () {
blankText('');
});
glvB.on('mouseover', function () {
writeColors('White', btn);
});
glvB.on('click', function () {
console.log(glvB + " clicked");
wht.hide();
shapesLayer.draw();
});
glvB.setPosition((xpos + 20), bpos);
shapesLayer.add(glvB);
xpos = (xpos + 230);
}
You can use your .png image filenames to automate your color-button coding efforts.
No need to manually code 10 glove components X 21 colors per component (210 color buttons).
Assume you’ve split the each image URL (filename) to get the color and glove-type.
Then you can create all 210 color buttons with one piece of reusable code.
Demo: http://jsfiddle.net/m1erickson/H5FDc/
Example Code:
// Usage:
addColorButton(100,100,"red","fingers");
// 1 function to add 210 color-buttons
function addColorButton(x,y,color,component){
// create this button
var button=new Kinetic.Image({
x:x,
y:y,
image: imageArray[ color+"-color-button" ],
});
// save the color as a property on this button
button.gloveColor=color;
// save the glove component name as a property on this button
button.gloveComponent=component; // eg, "fingers"
// resuable click handler
// Will change the gloves "#fingers" to "red-fingers-image"
button.on("click",function(){
// eg. get the image "red-fingers-image"
var newImage = imageArray[this.gloveColor+"-"+this.gloveComponent+"-image"];
// eg. get the Kinetic.Image with id:”finger”
var glovePart = layer.find("#"+this.gloveComponent”][0];
// change the Kinetic id:finger’s image
// to the red-fingers-image
glovePart.setImage(newImage);
layer.draw();
});
layer.add(button);
}
I have code that creates an object array in a layer as shown below:
var labels = layer.get('Label');
var labelCount = labelLeft.length;
var tweens = [];
var tweenCounter = 1;
var duration=5;
for(var i=0; i<labelCount; i++)
{
var tween = new Kinetic.Tween({
node: labelLeft[i],
duration: animspeed[i],
x: 0,
onFinish: function() {
if (tweenCounter !== labelCount) { //Prevent an undefined tween from being played at the end
tweens[tweenCounter].play();
tweenCounter++;
}
}
});
tweens.push(tween);
}
tweens[0].play();
The problem is that I want to hide the object once done scrolling to left using onFinish. I tried using labelLeft[i].hide()
onFinish: function() {
labelLeft[i].hide();
if (tweenCounter !== labelCount) { //Prevent an undefined tween from being played at the end
tweens[tweenCounter].play();
tweenCounter++;
}
}
But this triggers TypeError: labelLeft[i] is undefined
Any ideas? Please help. Thanks
It seems as closure problem. Could you try this, I am not sure if it will work but anyway:
for(var i=0; i<labelCount; i++)
{
var label = labelLeft[i];
var tween = new Kinetic.Tween({
node: labelLeft[i],
duration: animspeed[i],
x: 0,
onFinish: function(l) {
return function()
hide(l);
}(label)
});
tweens.push(tween);
}
function hide(label) {
labelLeft[label].hide();
if (tweenCounter !== labelCount) { //Prevent an undefined tween from being played at the end
tweens[tweenCounter].play();
tweenCounter++;
}
}
Try to debug your code and check of i is defined.
I think its not because you are in a for loop and execute the code when the loop has finished. You easily can solve this problem with a anonymous function to induce a scope.
for(var i=0; i<labelCount; i++)
{
(function(i){
//put code here
}(i))
}
I am trying to re-size a circle using papeJS but since i used two onMouseDrag function it if conflicting. I am unable to create it. Can anyone help me. Here is the fiddle with circle
Here is the code.
<script type="text/paperscript" canvas="canvas">
var raster = new Raster({
source: 'Chrysanthemum.jpg',
position: view.center
});
var path = null;
var circles = [];
var isDrawing = false;
var draggingIndex = -1;
var segment, movePath;
var resize = false;
project.activeLayer.selected = false;
function onMouseDrag(event) {
if (!isDrawing && circles.length > 0) {
for (var ix = 0; ix < circles.length; ix++) {
if (circles[ix].contains(event.point)) {
draggingIndex = ix;
break;
}
}
}
if (draggingIndex > -1) {
circles[draggingIndex].position = event.point;
} else {
path = new Path.Circle({
center: event.point,
radius: (event.downPoint - event.point).length,
fillColor: null,
strokeColor: 'black',
strokeWidth: 10
});
path.removeOnDrag();
isDrawing = true;
}
}
;
function onMouseUp(event) {
if (isDrawing) {
circles.push(path);
}
isDrawing = false;
draggingIndex = -1;
}
;
function onMouseMove(event) {
project.activeLayer.selected = false;
if (event.item)
event.item.selected = true;
resize = true;
}
var segment, path;
var movePath = false;
function onMouseDown(event) {
segment = path = null;
var hitResult = project.hitTest(event.point, hitOptions);
if (!hitResult)
return;
if (hitResult) {
path = hitResult.item;
if (hitResult.type == 'segment') {
segment = hitResult.segment;
} else if (hitResult.type == 'stroke') {
var location = hitResult.location;
segment = path.insert(location.index + 1, event.point);
path.smooth();
}
}
movePath = hitResult.type == 'fill';
if (movePath)
project.activeLayer.addChild(hitResult.item);
}
</script>
First, your code (on jsfiddle) does not run.
The paperjs external resource returned a 404. https://raw.github.com/paperjs/paper.js/master/dist/paper.js works for paperjs.
The raster source was for a local file, not a URI.
In onMouseDown, project.hitTest references an undefined hitOptions.
It seems from your question that you want to be able to drag the circle segments to resize the circle, and you tried using two onMouseDrag functions to do that, which would not work. Instead, both operations should be in the same onMouseDrag, using if-then-else to choose between them. To make this work as expected, the item that was hit should be stored in onMouseDown instead of whatever circle your code finds at the beginning of onMouseDrag. For example, here onMouseDrag can either "move" or "resize" (jsfiddle here):
<script type="text/paperscript" canvas="myCanvas">
var raster = new Raster({
source: 'http://i140.photobucket.com/albums/r10/Array39/Chrysanthemum.jpg',
position: view.center
});
var circles = [];
var hitItem = null;
var currentAction = null;
function onMouseMove(event) {
project.activeLayer.selected = false;
if (event.item) {
event.item.selected = true;
}
}
function onMouseDown(event) {
hitItem = null;
var aColor = new Color('black');
for (var i = 0; i < circles.length; i++) {
circles[i].fillColor = aColor;
}
view.draw();
var hitResult = project.hitTest(event.point);
for (var i = 0; i < circles.length; i++) {
circles[i].fillColor = null;
}
view.draw();
if (!hitResult) {
return; //only happens if we don't even hit the raster
}
hitItem = hitResult.item;
if (circles.indexOf(hitItem) < 0) {
var newCircle = new Path.Circle({
center: event.point,
radius: 2,
strokeColor: 'black',
strokeWidth: 10
});
hitItem = newCircle;
circles.push(hitItem);
currentAction = 'resize';
return;
}
if (hitResult.type == 'segment') {
currentAction = 'resize';
} else if (hitResult.type == 'stroke') {
hitItem.insert(hitResult.location.index + 1, event.point);
hitItem.smooth();
currentAction = 'resize';
} else if (hitResult.type == 'fill') {
currentAction = 'move';
}
}
function onMouseDrag(event) {
if (!hitItem) {
return;
}
if (currentAction == 'move') {
hitItem.position = event.point;
} else if (currentAction == 'resize') {
if ((event.downPoint - event.point).length >= 1) {
hitItem.fitBounds(new Rectangle(event.downPoint, event.point), true);
}
}
};
</script>
<canvas id="myCanvas"></canvas>
Also note:
In onMouseDown, the function returns if !hitResult, so you do not need to test if (hitResult) right after that return.
Naming variables the same as objects makes searching more difficult, e.g., in your code path is an instance of Path.
Using the same variable for different purposes makes code more difficult to parse, e.g., in your code path is used to create new circles as well as to store which circle has been selected.
You have multiple variables defined twice: path, movePath, and segment.
If a variable will only be used in a single function, e.g., movePath and segment, then it makes the code more readable if the variable is defined in that function. Also, movePath is only used in a single if-statement, which just adds items back to the layer, but the only items not in the layer have been removed when the circle was originally drawn. Since those items cannot be hit, the item that was hit must already be in the layer.
The variable segment is not used.
It makes the code flow/read better if the functions are ordered logically. In this case, onMouseMove should go first because it happens before the button is clicked. Then onMouseDown goes next because it must happen before the other actions. Then onMouseDrag, and finally onMouseUp.
Instead of creating new circles in onMouseDrag and then throwing them away on the next drag, it makes more sense to create one in onMouseDown if there was no item hit, or if the hit item is not a circle. Then in onMouseDown, you just resize that circle. Path.scale or Path.fitBounds can be used for such resizing.
Instead of using multiple boolean variables to keep track of the current action (e.g., resize vs move), it is more logical to have a single variable keeping track of the current action.
Instead of your code to find whether the point is within a circle, the code I am using temporarily sets the circles' fillColor, do the hitTest, and then clears the circles' fillColor. I did this because when you hit a stroke, the shape of the circle changes, for which your code to find the draggingIndex does not account.
I have a memory leak in my code and I cannot figure out what is causing it. I am receiving target data from a WebSocket which contains basically an ID and an X,Y coordinate. The data is passed to a HandleData function which creates a circle for each target and a line (which is updated) to show where the target has moved from/to.
If a target no longer appears in the WebSocket stream it is removed. On testing I have found the webpage quickly amasses hundreds of mb of data in spite of me removing these items. Upon using Chrome's memory profiler it seems Raphael (or something) is holding onto all the path information in spite of me deleting it.
If anyone can help me in anyway I would be very grateful. It does seem that Raphael is holding onto the data but there is every chance I have made a mistake somewhere in my code :)
var arrayColours = ["#f33", "#3f3", "#33f", "#f66", "#6f6", "#66f"];
var targetStructArray = [];
function HandleTargetData(data) {
//Go through all our existing targets and mark them as not updated
for (var i = 0; i < targetStructArray.length; i++) {
targetStructArray[i].updated = false;
}
for (var i = 0; i < data.targets.length; i++) {
var targetData = data.targets[i];
var targetStruct = undefined;
//find our targetStruct
for (var j = 0; j < targetStructArray.length; j++) {
if (targetStructArray[j].id == targetData.id) {
targetStruct = targetStructArray[j];
break;
}
}
//If it doesnt exist, create it
if (!targetStruct) {
var path = paper.path();
path.attr({ "stroke-width": "2", "stroke": "#888" });
path.addPart(['M', targetData.x, targetData.y]);
var circle = paper.circle(targetData.x, targetData.y, 15, 15).attr({
stroke: "none",
fill: arrayColours[Math.floor(Math.random() * arrayColours.length)] //random colour
});
//Create our struct
targetStruct = {
circle: circle,
path: path,
id: targetData.id,
updated: false
};
targetStructArray.push(targetStruct);
}
else {
targetStruct.circle.attr({ cx: targetData.x, cy: targetData.y });
targetStruct.path.addPart(['L', targetData.x, targetData.y]);
}
//ensure we are set as updated
targetStruct.updated = true;
}
//Go through all our existing targets and delete any that werent updated
for (var i = targetStructArray.length - 1; i >= 0; i--) {
if (!targetStructArray[i].updated) {
targetStructArray[i].circle.remove();
targetStructArray[i].path.remove();
targetStructArray[i].circle.removeData();
targetStructArray[i].path.removeData();
targetStructArray[i].circle = null;
targetStructArray[i].path = null;
targetStructArray[i] = null;
targetStructArray.remove(i);
}
}
}
I use two functions not listed here which are John Resig's Array.Remove and a Raphael helper function from a different question