EaselJS BitmapAnimation.gotoAndPlay lag - javascript
I've written a requirejs module to take a BitmapAnimation and add methods to allow it to move around on the stage at a certain speed and once its destination has been reached, to move to another looped animation.
The problem I'm having is that there is quite a lot of lag between gotoAndPlay being called and anything happening on stage. I've console.logged the methods that call gotoAndPlay but the logs happen immediately where the animation doesn't change as quickly.
here is my abstract object:
'use strict';
/**
* AbstractCharacter
* Provides common functionality for all characters
*/
define(['easeljs'], function () {
var AbstractCharacter = function (model) {
this.model = model;
// changed from original code
// allows better encapsulation
this._s = new createjs.SpriteSheet(model.ssInit);
// add reversed frames
createjs.SpriteSheetUtils.addFlippedFrames(this._s, true, false, false),
this.initialize();
};
AbstractCharacter.prototype = Object.create(createjs.BitmapAnimation.prototype);
AbstractCharacter.prototype.constructor = AbstractCharacter;
// speed constant
AbstractCharacter.prototype.SPEED = 10;
// from Flash CS6
AbstractCharacter.prototype.BitmapAnimation_initialize = AbstractCharacter.prototype.initialize;
/**
* moves a character to a destination and sets their state at the end of the move
* #param {object} destination destination coordinates {x:XX}
* #param {string} nextAction the next action to perform
* #return {null}
*/
AbstractCharacter.prototype.moveTo = function(destination, nextAction) {
nextAction = nextAction || 'idle';
if (destination) {
if (destination.x < this.x) {
this.walkLeft();
} else {
this.walkRight();
}
this.destination = destination;
this.nextAction = nextAction;
}
};
/**
* public method to perform action
* #param {string} room name of the current room the character is in
* #param {string} action action name
* #param {object} params any parameters required by the action
* #return {null}
*/
AbstractCharacter.prototype.action = function(room, action, params) {
switch (action) {
case 'walk' :
this.moveTo(params, 'idle');
break;
case 'talk' :
this.moveTo(params, 'talk');
break;
case 'sleep' :
this.moveTo(this.model.actions.room[room].sleep, 'sleep');
break;
}
};
/**
* onTick callback to call next animation frame for use with easeljs
* #return {null}
*/
AbstractCharacter.prototype.onTick = function() {
if (this.destination) {
var destination = this.destination,
speed;
if (destination.x < this.x) {
speed = -this.SPEED;
if (this.x + speed < destination.x) {
this.endMove(destination);
} else {
this.x += speed;
}
} else {
speed = this.SPEED;
if (this.x + speed > destination.x) {
this.endMove(destination);
} else {
this.x += speed;
}
}
}
};
/**
* finishes move by calling nextAction() and clearing away desination and nextAction vars
* #param {object} destination format {x:XX}
* #return {null}
*/
AbstractCharacter.prototype.endMove = function(destination) {
this.x = destination.x;
this[this.nextAction]();
this.destination = null;
this.nextAction = null;
};
/**
* these methods come from Flash CS6
* the only difference is that I've removed
* the reference to the Object prototype
*/
AbstractCharacter.prototype.initialize = function() {
this.BitmapAnimation_initialize(this._s);
this.idle();
};
AbstractCharacter.prototype.idle = function(){
console.log('idle'); // fires almost a second before animation plays
this.gotoAndPlay("idle");
};
AbstractCharacter.prototype.idleLeft = function(){
this.gotoAndPlay("idleside");
};
AbstractCharacter.prototype.idleRight = function(){
this.gotoAndPlay("idleside_h");
};
AbstractCharacter.prototype.walkLeft = function(){
this.gotoAndPlay("walk");
};
AbstractCharacter.prototype.walkRight = function(){
this.gotoAndPlay("walk_h");
};
AbstractCharacter.prototype.talk = function(){
console.log('talk');
this.gotoAndPlay("talk");
};
AbstractCharacter.prototype.sleep = function(){
console.log('sleep'); // fires almost a second before animation plays
this.gotoAndPlay("sleep");
};
// public interface
return AbstractCharacter;
});
EDIT: this only seems to affect chrome - ie9 and firefox17 are fine
EDIT: if I use the following code instead of extending my abstractcharacter, this plays out without issue or lag
var test = new createjs.BitmapAnimation(new createjs.SpriteSheet({
images: [model.assets['mycharacters']],
frames: [[0,0,129,344,0,74,327],[129,0,129,345,0,74,327],[258,0,162,331,0,93,313],[420,0,162,331,0,93,313],[582,0,141,338,0,91,320],[723,0,141,338,0,91,320],[864,0,129,345,0,74,327],[993,0,129,345,0,74,327],[1122,0,162,331,0,93,313],[1284,0,162,331,0,93,313],[1446,0,141,338,0,91,320],[1446,0,141,338,0,91,320],[1587,0,129,344,0,74,327],[1716,0,129,344,0,74,327],[1845,0,162,330,0,93,313],[0,345,162,330,0,93,313],[162,345,141,337,0,91,320],[303,345,141,337,0,91,320],[444,345,129,344,0,74,327],[573,345,129,344,0,74,327],[702,345,162,330,0,93,313],[864,345,162,330,0,93,313],[1026,345,141,337,0,91,320],[1167,345,141,337,0,91,320],[1308,345,129,344,0,74,327],[1437,345,129,344,0,74,327],[1566,345,162,330,0,93,313],[1728,345,162,331,0,93,313],[1890,345,141,338,0,91,320],[0,689,141,338,0,91,320],[141,689,129,345,0,74,327],[270,689,129,345,0,74,327],[399,689,162,331,0,93,313],[399,689,162,331,0,93,313],[561,689,141,338,0,91,320],[702,689,141,338,0,91,320],[0,0,129,344,0,74,327],[843,689,129,343,0,74,327],[972,689,162,330,0,93,313],[1134,689,162,330,0,93,313],[1296,689,141,337,0,91,320],[1437,689,141,337,0,91,320],[1578,689,129,344,0,74,327],[1707,689,129,344,0,74,327],[1836,689,162,330,0,93,313],[0,1034,162,329,0,93,313],[162,1034,141,336,0,91,320],[303,1034,141,336,0,91,320],[444,1034,129,343,0,74,327],[573,1034,129,343,0,74,327],[702,1034,162,329,0,93,313],[864,1034,162,329,0,93,313],[1026,1034,141,335,0,91,320],[1167,1034,141,336,0,91,320],[1308,1034,129,343,0,74,327],[1308,1034,129,343,0,74,327],[1437,1034,162,329,0,93,313],[1599,1034,162,329,0,93,313],[1761,1034,141,336,0,91,320],[1902,1034,141,336,0,91,320],[0,1377,129,343,0,74,327],[129,1377,129,343,0,74,327],[258,1377,162,329,0,93,313],[420,1377,162,329,0,93,313],[582,1377,141,336,0,91,320],[723,1377,141,336,0,91,320],[864,1377,129,343,0,74,327],[993,1377,129,343,0,74,327],[1122,1377,162,330,0,93,313],[1284,1377,162,330,0,93,313],[1446,1377,141,337,0,91,320],[1587,1377,141,337,0,91,320],[1728,1377,129,344,0,74,327],[1857,1377,129,344,0,74,327],[0,1721,162,330,0,93,313],[162,1721,162,330,0,93,313],[324,1721,141,325,0,91,320],[465,1721,158,337,0,91,320],[623,1721,141,345,0,74,327],[764,1721,141,345,0,74,327],[905,1721,162,331,0,93,313],[1067,1721,162,331,0,93,313],[1229,1721,158,338,0,91,320],[1387,1721,158,338,0,91,320],[1545,1721,141,345,0,74,327],[1686,1721,141,344,0,74,327],[1827,1721,162,330,0,93,313],[0,2066,162,330,0,93,313],[162,2066,158,337,0,91,320],[320,2066,158,337,0,91,320],[478,2066,141,344,0,74,327],[619,2066,141,344,0,74,327],[760,2066,162,330,0,93,313],[922,2066,162,330,0,93,313],[1084,2066,158,337,0,91,320],[1242,2066,158,337,0,91,320],[1400,2066,141,344,0,74,327],[1541,2066,141,345,0,74,327],[1682,2066,162,331,0,93,313],[1844,2066,162,331,0,93,313],[0,2411,158,338,0,91,320],[158,2411,158,338,0,91,320],[316,2411,141,345,0,74,327],[457,2411,141,345,0,74,327],[598,2411,162,331,0,93,313],[760,2411,162,319,0,93,313],[922,2411,141,337,0,91,320],[1063,2411,141,338,0,91,320],[1204,2411,129,345,0,74,327],[1333,2411,129,345,0,74,327],[1462,2411,162,331,0,93,313],[1624,2411,162,331,0,93,313],[1786,2411,141,338,0,91,320],[0,2756,141,338,0,91,320],[141,2756,129,345,0,74,327],[270,2756,129,345,0,74,327],[399,2756,162,331,0,93,313],[561,2756,162,330,0,93,313],[723,2756,141,337,0,91,320],[864,2756,141,337,0,91,320],[1005,2756,129,344,0,74,327],[1134,2756,129,344,0,74,327],[1263,2756,162,330,0,93,313],[1425,2756,162,330,0,93,313],[1587,2756,141,337,0,91,320],[1728,2756,141,337,0,91,320],[1869,2756,129,344,0,74,327],[0,3101,129,344,0,74,327],[129,3101,162,330,0,93,313],[291,3101,162,330,0,93,313],[453,3101,141,337,0,91,320],[594,3101,141,337,0,91,320],[735,3101,129,344,0,74,327],[864,3101,129,345,0,74,327],[993,3101,162,331,0,93,313],[1155,3101,162,331,0,93,313],[1317,3101,141,338,0,91,320],[1458,3101,141,338,0,91,320],[1599,3101,129,345,0,74,327],[1728,3101,129,345,0,74,327],[1857,3101,162,331,0,93,313],[0,3446,162,331,0,93,313],[162,3446,141,326,0,91,320],[303,3446,148,280,0,82,281]],
animations: {idle:[0,36, true], idleside:[37,76, true], walk:[77,105, true], talk:[106,142, true], sleep:[143,143, true]}
}));
this.addChild(test);
test.gotoAndPlay('idle');
setTimeout(function () {
console.log('idleside');
test.gotoAndPlay('idleside');
}, 3000);
setTimeout(function () {
console.log('walk');
test.gotoAndPlay('walk');
},6000)
setTimeout(function () {
console.log('talk');
test.gotoAndPlay('talk');
},9000)
setTimeout(function () {
console.log('sleep');
test.gotoAndPlay('sleep');
},12000)
I've discovered what the issue is:
// add reversed frames
createjs.SpriteSheetUtils.addFlippedFrames(this._s, true, false, false),
The issue is that the addReversedFrames has issues in chrome - this may be caused by the size of my spritesheet - I'm going to test what happens when i break the spritesheet down into multiple parts
This may be because of the issue documented with multiple images here: Stuttering animation when spanning multiple sprite sheets in easeljs
I've updated my abstract class to use scaleX = -1 instead of flipped frames which works much better. Note: there will be a slight cpu overhead to do this but testing will bear this out. Worse case scenario, I'll have to add the extra frames to the assets.
enjoy
'use strict';
/**
* AbstractCharacter
* Provides common functionality for all characters
* #author Allandt Bik-Elliott
* #version 0.1
*/
define(['easeljs'], function () {
var AbstractCharacter = function (model) {
this.model = model;
// changed from original code
// allows better encapsulation
this._s = new createjs.SpriteSheet(model.ssInit);
// add reversed frames
// NOTE: removed because there is lag switching between spritesheets
//createjs.SpriteSheetUtils.addFlippedFrames(this._s, true, false, false),
this.initialize();
};
AbstractCharacter.prototype = Object.create(createjs.BitmapAnimation.prototype);
AbstractCharacter.prototype.constructor = AbstractCharacter;
// speed constant
AbstractCharacter.prototype.SPEED = 10;
// from Flash CS6
AbstractCharacter.prototype.BitmapAnimation_initialize = AbstractCharacter.prototype.initialize;
/**
* moves a character to a destination and sets their state at the end of the move
* #param {object} destination destination coordinates {x:XX}
* #param {string} nextAction the next action to perform
* #return {null}
*/
AbstractCharacter.prototype.moveTo = function(destination, nextAction) {
nextAction = nextAction || 'idle';
if (destination) {
if (destination.x < this.x) {
this.walkLeft();
} else {
this.walkRight();
}
this.model.destination = destination;
this.model.nextAction = nextAction;
}
};
/**
* public method to perform action
* #param {string} room name of the current room the character is in
* #param {string} action action name
* #param {object} params any parameters required by the action
* #return {null}
*/
AbstractCharacter.prototype.act = function(room, action, actionparams) {
//console.log(action);
switch (action) {
case 'walk' :
this.moveTo(actionparams, 'idle');
break;
case 'talk' :
this.moveTo(actionparams, 'talk');
break;
case 'sleep' :
this.moveTo(this.model.actions.room[room].sleep, 'sleep');
break;
}
};
/**
* onTick callback for use with easeljs
* #return {null}
*/
AbstractCharacter.prototype.onTick = function() {
if (this.model.destination) {
var destination = this.model.destination,
speed;
if (destination.x < this.x) {
speed = -this.SPEED;
if (this.x + speed < destination.x) {
this.endMove(destination);
} else {
this.x += speed;
}
} else {
speed = this.SPEED;
if (this.x + speed > destination.x) {
this.endMove(destination);
} else {
this.x += speed;
}
}
}
};
/**
* finishes move by calling nextAction() and clearing away desination and nextAction vars
* #param {object} destination format {x:XX}
* #return {null}
*/
AbstractCharacter.prototype.endMove = function(destination) {
//console.log('ending move');
this.x = destination.x;
this[this.model.nextAction]();
this.model.destination = null;
this.model.nextAction = null;
};
/**
* these methods come from Flash CS6
* the only difference is that I've removed
* the reference to the Object prototype
*
* scaleX used instead of flipped frames
*/
AbstractCharacter.prototype.initialize = function() {
this.BitmapAnimation_initialize(this._s);
this.idle();
};
AbstractCharacter.prototype.idle = function(){
//console.log('idle');
this.gotoAndPlay("idle");
this.scaleX = 1;
};
AbstractCharacter.prototype.idleLeft = function(){
//console.log('idle left');
this.gotoAndPlay("idleside");
this.scaleX = 1;
};
AbstractCharacter.prototype.idleRight = function(){
//console.log('idle right');
// this.gotoAndPlay("idleside_h");
this.gotoAndPlay("idleside");
this.scaleX = -1;
};
AbstractCharacter.prototype.walkLeft = function(){
//console.log('walk left');
this.gotoAndPlay("walk");
this.scaleX = 1;
};
AbstractCharacter.prototype.walkRight = function(){
//console.log('walk right');
// this.gotoAndPlay("walk_h");
this.gotoAndPlay("walk");
this.scaleX = -1;
};
AbstractCharacter.prototype.talk = function(){
//console.log('talk');
this.gotoAndPlay("talk");
this.scaleX = 1;
};
AbstractCharacter.prototype.sleep = function(){
//console.log('sleep');
this.gotoAndPlay("sleep");
this.scaleX = 1;
};
// public interface
return AbstractCharacter;
});
Related
Cant change the colour of the child element
I have the following markup: I am using Piklor, a cooler picker https://github.com/jillix/piklor.js/ It is initialised by: header = pk.getElm(".selected-icon p") and the colour attributes are assigned here: header.style.color = col; However, the above doesn't work. Am I targeting the p tag right? The class of the p tag changes pragmatically in my scrip, so I can't really use the class name and can only refer to it by the parent element. If I target ".selected-icon" (without the p tag) and with a backgroundColor, e.g. header.style.backgroundColor = col; it seems to work, but not the colour of the p tag. (function (root) { /** * Piklor * Creates a new `Piklor` instance. * * #name Piklor * #function * #param {String|Element} sel The element where the color picker will live. * #param {Array} colors An array of strings representing colors. * #param {Object} options An object containing the following fields: * * - `open` (String|Element): The HTML element or query selector which will open the picker. * - `openEvent` (String): The open event (default: `"click"`). * - `style` (Object): Some style options: * - `display` (String): The display value when the picker is opened (default: `"block"`). * - `template` (String): The color item template. The `{color}` snippet will be replaced * with the color value (default: `"<div data-col=\"{color}\" style=\"background-color: {color}\"></div>"`). * - `autoclose` (Boolean): If `false`, the color picker will not be hided by default (default: `true`). * - `closeOnBlur` (Boolean): If `true`, the color picker will be closed when clicked outside of it (default: `false`). * * #return {Piklor} The `Piklor` instance. */ function Piklor(sel, colors, options) { var self = this; options = options || {}; options.open = self.getElm(options.open); options.openEvent = options.openEvent || "click"; options.style = Object(options.style); options.style.display = options.style.display || "block"; options.closeOnBlur = options.closeOnBlur || false; options.template = options.template || "<div data-col=\"{color}\" style=\"background-color: {color}\"></div>"; self.elm = self.getElm(sel); self.cbs = []; self.isOpen = true; self.colors = colors; self.options = options; self.render(); // Handle the open element and event. if (options.open) { options.open.addEventListener(options.openEvent, function (ev) { self.isOpen ? self.close() : self.open(); }); } // Click on colors self.elm.addEventListener("click", function (ev) { var col = ev.target.getAttribute("data-col"); if (!col) { return; } self.set(col); self.close(); }); if (options.closeOnBlur) { window.addEventListener("click", function (ev) { // check if we didn't click 'open' and 'color pallete' elements if (ev.target != options.open && ev.target != self.elm && self.isOpen) { self.close(); } }); } if (options.autoclose !== false) { self.close(); } } /** * getElm * Finds the HTML element. * * #name getElm * #function * #param {String|Element} el The HTML element or query selector. * #return {HTMLElement} The selected HTML element. */ Piklor.prototype.getElm = function (el) { if (typeof el === "string") { return document.querySelector(el); } return el; }; /** * render * Renders the colors. * * #name render * #function */ Piklor.prototype.render = function () { var self = this , html = "" ; self.colors.forEach(function (c) { html += self.options.template.replace(/\{color\}/g, c); }); self.elm.innerHTML = html; }; /** * close * Closes the color picker. * * #name close * #function */ Piklor.prototype.close = function () { this.elm.style.display = "none"; this.isOpen = false; }; /** * open * Opens the color picker. * * #name open * #function */ Piklor.prototype.open = function () { this.elm.style.display = this.options.style.display; this.isOpen = true; }; /** * colorChosen * Adds a new callback in the colorChosen callback buffer. * * #name colorChosen * #function * #param {Function} cb The callback function called with the selected color. */ Piklor.prototype.colorChosen = function (cb) { this.cbs.push(cb); }; /** * set * Sets the color picker color. * * #name set * #function * #param {String} c The color to set. * #param {Boolean} p If `false`, the `colorChosen` callbacks will not be called. */ Piklor.prototype.set = function (c, p) { var self = this; self.color = c; if (p === false) { return; } self.cbs.forEach(function (cb) { cb.call(self, c); }); }; root.Piklor = Piklor; })(this); window.addEventListener("load", function () { var pk = new Piklor(".color-picker", [ "#1abc9c" , "#2ecc71" , "#3498db" , "#9b59b6" , "#34495e" , "#16a085" , "#27ae60" , "#2980b9" , "#8e44ad" , "#2c3e50" , "#f1c40f" , "#e67e22" , "#e74c3c" , "#ecf0f1" , "#95a5a6" , "#f39c12" , "#d35400" , "#c0392b" , "#bdc3c7" , "#7f8c8d" ], { open: ".picker-wrapper .btn" }) , wrapperEl = pk.getElm(".picker-wrapper") , header = pk.getElm(".selected-icon p") , footer = pk.getElm("footer") ; pk.colorChosen(function (col) { wrapperEl.style.backgroundColor = col; header.style.backgroundColor = col; footer.style.backgroundColor = col; }); }); /* picker */ .color-picker { background: rgba(255, 255, 255, 0.75); padding: 10px; border: 1px solid rgba(203, 203, 203, 0.6); border-radius: 2px; } .color-picker > div { width: 40px; display: inline-block; height: 40px; margin: 5px; border-radius: 100%; opacity: 0.7; } .picker-wrapper { padding: 20px; } .color-picker > div:hover { opacity: 1; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> <span class="selected-icon"><p class="icon-eight">Hello</i></span> <div class="picker-wrapper"> <button class="btn btn-default">Select color</button> <div class="color-picker"> </div> </div>
Okay. If that is the case, your issue is the p tag is not closed. <span class="selected-icon"><p class="icon-eight">Hello</i></span> You have an "i" tag trying to close the leading "p" tag. Change that and you should be good to go. Also change header.style.backgroundColor = col; To header.style.color = col; To achieve the desired result.
Creating a class of Crafty JS entity (class of a class?)
I am trying to create a class which creates a Crafty entity with specific properties. So far, the functions within the class do not run because 'this' refers to the window object $(document).ready(function () { Crafty.init(window.innerWidth, window.innerHeight); var player = new controller(37,38,39,40); player.d.color("red").attr({ w: 50, h: 50, x: 0, y: 0 }); // Jump Height = velocity ^ 2 / gravity * 2 // Terminal Velocity = push * (1 / viscosity) var gravity = 1; var viscosity = 0.5; var frame = (1 / 20); var distanceMultiplier = 10; //pixels per meter var timeMultiplier = 20; //relative to actual time var keystart = []; var keyboard = []; function controller (controls) { this.d = Crafty.e(); this.d.addComponent("2D, Canvas, Color, Collision"); this.d.collision(); this.d.mass = 1; this.d.a = { extradistance : 0, velocity : 0, acceleration : 0, force : 0, resistance : 0 }; this.d.a.push = 0; this.d.v = { extradistance : 0, velocity : 0, acceleration : 0, force : 0 }; this.d.jumping = true; this.d.onHit("Collision", function () { var a = this.d.hit("Collision"); if (a) { for (var b in a) { this.d.x = this.d.x - a[b].normal.x * a[b].overlap; this.d.y = this.d.y - a[b].normal.y * a[b].overlap; if (a[b].normal.y < -0.5) { this.d.jumping = false; } if (Math.abs(a[b].normal.x) < 0.2) { this.d.v.velocity = this.d.v.velocity * a[b].normal.y * 0.2; } if (Math.abs(a[b].normal.y) < 0.2) { this.d.a.velocity = this.d.a.velocity * a[b].normal.x * 0.2; } } return; } }); this.d.physics = function () { if (keyboard[arguments[1]] && !this.jumping) { this.v.velocity = 5; this.jumping = true; } if (keyboard[arguments[1]] && this.jumping) { var now = new Date(); if (now.getTime() - keystart[arguments[1]].getTime() < 500) { this.v.velocity = 5; } } if (keyboard[arguments[0]] && keyboard[arguments[2]]) { this.a.velocity = 0; } else { if (keyboard[arguments[0]]) { this.a.velocity = -3; } if (keyboard[arguments[2]]) { this.a.velocity = 3; } } if (keyboard[arguments[3]]) { this.v.velocity = -5; } this.a.force = this.a.push - this.a.resistance; this.a.acceleration = this.a.force / this.mass; this.a.velocity = this.a.velocity + (this.a.acceleration * frame); this.a.extradistance = (this.a.velocity * frame); this.a.resistance = this.a.velocity * viscosity; this.attr({ x: (this.x + (this.a.extradistance * distanceMultiplier)) }); this.v.force = gravity * this.mass; this.v.acceleration = this.v.force / this.mass; this.v.velocity = this.v.velocity - (this.v.acceleration * frame); this.v.extradistance = (this.v.velocity * frame); this.attr({ y: (this.y - (this.v.extradistance * distanceMultiplier)) }); setTimeout(this.physics, (frame * 1000) / timeMultiplier); }; this.d.listen = function(){ document.body.addEventListener("keydown", function (code) { var then = new Date(); if (!keyboard[code.keyCode] && !this.jumping && code.keyCode == arguments[1]) { //only if not yet pressed it will ignore everything until keyup keyboard[code.keyCode] = true; //start movement keystart[code.keyCode] = then; //set time } if (!keyboard[code.keyCode] && code.keyCode != arguments[1]) { //only if not yet pressed it will ignore everything until keyup keyboard[code.keyCode] = true; //start movement keystart[code.keyCode] = then; //set time } }); }; } player.d.physics(); player.d.listen(); document.body.addEventListener("keyup", function (code) { keyboard[code.keyCode] = false; }); }); In trying to put the functions as prototypes of the class, I run into a problem. Crafty.init(500,500); function block () { block.d = Crafty.e("2D, Color, Canvas"); block.d.color("red"); block.d.attr({x:0,y:0,h:50,w:50}); } block.d.prototype.green = function() { this.color("green"); } var block1 = new block(); block1.d.color(); If an object is defined in the constructor, I cannot use it to add a prototype to.
Generally in Crafty, we favor composition. That is, you extend an entity by adding more components to it. You can have kind of a hierarchy by having one component automatically add others during init. I haven't looked through all of your example code, because there's a lot! But consider the second block: function block () { block.d = Crafty.e("2D, Color, Canvas"); block.d.color("red"); block.d.attr({x:0,y:0,h:50,w:50}); } block.d.prototype.green = function() { this.color("green"); } var block1 = new block(); block1.d.color(); You're trying to combine Crafty's way of doing things (an entity component system) with classes in a way that's not very idiomatic. Better to do this: // Define a new component with Crafty.c(), rather than creating a class Crafty.c("Block", { // On init, add the correct components and setup the color and dimensions init: function() { this.requires("2D, Color, Canvas") .color("red") .attr({x:0,y:0,h:50,w:50}); }, // method for changing color green: function() { this.color("green"); } }); // Create an entity with Crafty.e() block1 = Crafty.e("Block"); // It's not easy being green! block1.green();
Adding an ID or class to Marker using markerwithlabel class
var markers = new MarkerWithLabel({ position: point, icon: imagePath1, map: map, draggable: false, labelContent: image_count, labelAnchor: new google.maps.Point(10, 30), labelClass: "labels", // the CSS class for the label labelInBackground: false, id: "markerId", title : contentVal, }); alert("Id is::"+$("#"+markers.id).val()); When I try to get Id, it is undefined
I too hit this roadblock. I reviewed the source code for MarkerWithLabel, and it does not support setting the ID or Class. But you can add support by changing just 3 lines of code. Below is the modified code, with my added "ID" support. It works as normal, but if you add a .id property when creating a new MarkerWithLabel (as you did above), it will properly set the ID. New markerswithlabel.js code — Added ID Support /** * #name MarkerWithLabel for V3 * #version 1.1.10 [April 8, 2014] * #author Gary Little (inspired by code from Marc Ridey of Google). * #copyright Copyright 2012 Gary Little [gary at luxcentral.com] * #fileoverview MarkerWithLabel extends the Google Maps JavaScript API V3 * <code>google.maps.Marker</code> class. * <p> * MarkerWithLabel allows you to define markers with associated labels. As you would expect, * if the marker is draggable, so too will be the label. In addition, a marker with a label * responds to all mouse events in the same manner as a regular marker. It also fires mouse * events and "property changed" events just as a regular marker would. Version 1.1 adds * support for the raiseOnDrag feature introduced in API V3.3. * <p> * If you drag a marker by its label, you can cancel the drag and return the marker to its * original position by pressing the <code>Esc</code> key. This doesn't work if you drag the marker * itself because this feature is not (yet) supported in the <code>google.maps.Marker</code> class. */ /*! * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /*jslint browser:true */ /*global document,google */ /** * #param {Function} childCtor Child class. * #param {Function} parentCtor Parent class. * #private */ function inherits(childCtor, parentCtor) { /* #constructor */ function tempCtor() {} tempCtor.prototype = parentCtor.prototype; childCtor.superClass_ = parentCtor.prototype; childCtor.prototype = new tempCtor(); /* #override */ childCtor.prototype.constructor = childCtor; } /** * This constructor creates a label and associates it with a marker. * It is for the private use of the MarkerWithLabel class. * #constructor * #param {Marker} marker The marker with which the label is to be associated. * #param {string} crossURL The URL of the cross image =. * #param {string} handCursor The URL of the hand cursor. * #private */ function MarkerLabel_(id, marker, crossURL, handCursorURL) { this.marker_ = marker; this.handCursorURL_ = marker.handCursorURL; this.labelDiv_ = document.createElement("div"); ////////////////////////////////////////////////////////// // New - ID support ////////////////////////////////////////////////////////// if (typeof id !== 'undefined') this.labelDiv_.id = id; this.labelDiv_.style.cssText = "position: absolute; overflow: hidden;"; // Get the DIV for the "X" to be displayed when the marker is raised. this.crossDiv_ = MarkerLabel_.getSharedCross(crossURL); } inherits(MarkerLabel_, google.maps.OverlayView); /** * Returns the DIV for the cross used when dragging a marker when the * raiseOnDrag parameter set to true. One cross is shared with all markers. * #param {string} crossURL The URL of the cross image =. * #private */ MarkerLabel_.getSharedCross = function (crossURL) { var div; if (typeof MarkerLabel_.getSharedCross.crossDiv === "undefined") { div = document.createElement("img"); div.style.cssText = "position: absolute; z-index: 1000002; display: none;"; // Hopefully Google never changes the standard "X" attributes: div.style.marginLeft = "-8px"; div.style.marginTop = "-9px"; div.src = crossURL; MarkerLabel_.getSharedCross.crossDiv = div; } return MarkerLabel_.getSharedCross.crossDiv; }; /** * Adds the DIV representing the label to the DOM. This method is called * automatically when the marker's <code>setMap</code> method is called. * #private */ MarkerLabel_.prototype.onAdd = function () { var me = this; var cMouseIsDown = false; var cDraggingLabel = false; var cSavedZIndex; var cLatOffset, cLngOffset; var cIgnoreClick; var cRaiseEnabled; var cStartPosition; var cStartCenter; // Constants: var cRaiseOffset = 20; var cDraggingCursor = "url(" + this.handCursorURL_ + ")"; // Stops all processing of an event. // var cAbortEvent = function (e) { if (e.preventDefault) { e.preventDefault(); } e.cancelBubble = true; if (e.stopPropagation) { e.stopPropagation(); } }; var cStopBounce = function () { me.marker_.setAnimation(null); }; this.getPanes().overlayMouseTarget.appendChild(this.labelDiv_); // One cross is shared with all markers, so only add it once: if (typeof MarkerLabel_.getSharedCross.processed === "undefined") { this.getPanes().overlayMouseTarget.appendChild(this.crossDiv_); MarkerLabel_.getSharedCross.processed = true; } this.listeners_ = [ google.maps.event.addDomListener(this.labelDiv_, "mouseover", function (e) { if (me.marker_.getDraggable() || me.marker_.getClickable()) { this.style.cursor = "pointer"; google.maps.event.trigger(me.marker_, "mouseover", e); } }), google.maps.event.addDomListener(this.labelDiv_, "mouseout", function (e) { if ((me.marker_.getDraggable() || me.marker_.getClickable()) && !cDraggingLabel) { this.style.cursor = me.marker_.getCursor(); google.maps.event.trigger(me.marker_, "mouseout", e); } }), google.maps.event.addDomListener(this.labelDiv_, "mousedown", function (e) { cDraggingLabel = false; if (me.marker_.getDraggable()) { cMouseIsDown = true; this.style.cursor = cDraggingCursor; } if (me.marker_.getDraggable() || me.marker_.getClickable()) { google.maps.event.trigger(me.marker_, "mousedown", e); cAbortEvent(e); // Prevent map pan when starting a drag on a label } }), google.maps.event.addDomListener(document, "mouseup", function (mEvent) { var position; if (cMouseIsDown) { cMouseIsDown = false; me.eventDiv_.style.cursor = "pointer"; google.maps.event.trigger(me.marker_, "mouseup", mEvent); } if (cDraggingLabel) { if (cRaiseEnabled) { // Lower the marker & label position = me.getProjection().fromLatLngToDivPixel(me.marker_.getPosition()); position.y += cRaiseOffset; me.marker_.setPosition(me.getProjection().fromDivPixelToLatLng(position)); // This is not the same bouncing style as when the marker portion is dragged, // but it will have to do: try { // Will fail if running Google Maps API earlier than V3.3 me.marker_.setAnimation(google.maps.Animation.BOUNCE); setTimeout(cStopBounce, 1406); } catch (e) {} } me.crossDiv_.style.display = "none"; me.marker_.setZIndex(cSavedZIndex); cIgnoreClick = true; // Set flag to ignore the click event reported after a label drag cDraggingLabel = false; mEvent.latLng = me.marker_.getPosition(); google.maps.event.trigger(me.marker_, "dragend", mEvent); } }), google.maps.event.addListener(me.marker_.getMap(), "mousemove", function (mEvent) { var position; if (cMouseIsDown) { if (cDraggingLabel) { // Change the reported location from the mouse position to the marker position: mEvent.latLng = new google.maps.LatLng(mEvent.latLng.lat() - cLatOffset, mEvent.latLng.lng() - cLngOffset); position = me.getProjection().fromLatLngToDivPixel(mEvent.latLng); if (cRaiseEnabled) { me.crossDiv_.style.left = position.x + "px"; me.crossDiv_.style.top = position.y + "px"; me.crossDiv_.style.display = ""; position.y -= cRaiseOffset; } me.marker_.setPosition(me.getProjection().fromDivPixelToLatLng(position)); if (cRaiseEnabled) { // Don't raise the veil; this hack needed to make MSIE act properly me.eventDiv_.style.top = (position.y + cRaiseOffset) + "px"; } google.maps.event.trigger(me.marker_, "drag", mEvent); } else { // Calculate offsets from the click point to the marker position: cLatOffset = mEvent.latLng.lat() - me.marker_.getPosition().lat(); cLngOffset = mEvent.latLng.lng() - me.marker_.getPosition().lng(); cSavedZIndex = me.marker_.getZIndex(); cStartPosition = me.marker_.getPosition(); cStartCenter = me.marker_.getMap().getCenter(); cRaiseEnabled = me.marker_.get("raiseOnDrag"); cDraggingLabel = true; me.marker_.setZIndex(1000000); // Moves the marker & label to the foreground during a drag mEvent.latLng = me.marker_.getPosition(); google.maps.event.trigger(me.marker_, "dragstart", mEvent); } } }), google.maps.event.addDomListener(document, "keydown", function (e) { if (cDraggingLabel) { if (e.keyCode === 27) { // Esc key cRaiseEnabled = false; me.marker_.setPosition(cStartPosition); me.marker_.getMap().setCenter(cStartCenter); google.maps.event.trigger(document, "mouseup", e); } } }), google.maps.event.addDomListener(this.labelDiv_, "click", function (e) { if (me.marker_.getDraggable() || me.marker_.getClickable()) { if (cIgnoreClick) { // Ignore the click reported when a label drag ends cIgnoreClick = false; } else { google.maps.event.trigger(me.marker_, "click", e); cAbortEvent(e); // Prevent click from being passed on to map } } }), google.maps.event.addDomListener(this.labelDiv_, "dblclick", function (e) { if (me.marker_.getDraggable() || me.marker_.getClickable()) { google.maps.event.trigger(me.marker_, "dblclick", e); cAbortEvent(e); // Prevent map zoom when double-clicking on a label } }), google.maps.event.addListener(this.marker_, "dragstart", function (mEvent) { if (!cDraggingLabel) { cRaiseEnabled = this.get("raiseOnDrag"); } }), google.maps.event.addListener(this.marker_, "drag", function (mEvent) { if (!cDraggingLabel) { if (cRaiseEnabled) { me.setPosition(cRaiseOffset); // During a drag, the marker's z-index is temporarily set to 1000000 to // ensure it appears above all other markers. Also set the label's z-index // to 1000000 (plus or minus 1 depending on whether the label is supposed // to be above or below the marker). me.labelDiv_.style.zIndex = 1000000 + (this.get("labelInBackground") ? -1 : +1); } } }), google.maps.event.addListener(this.marker_, "dragend", function (mEvent) { if (!cDraggingLabel) { if (cRaiseEnabled) { me.setPosition(0); // Also restores z-index of label } } }), google.maps.event.addListener(this.marker_, "position_changed", function () { me.setPosition(); }), google.maps.event.addListener(this.marker_, "zindex_changed", function () { me.setZIndex(); }), google.maps.event.addListener(this.marker_, "visible_changed", function () { me.setVisible(); }), google.maps.event.addListener(this.marker_, "labelvisible_changed", function () { me.setVisible(); }), google.maps.event.addListener(this.marker_, "title_changed", function () { me.setTitle(); }), google.maps.event.addListener(this.marker_, "labelcontent_changed", function () { me.setContent(); }), google.maps.event.addListener(this.marker_, "labelanchor_changed", function () { me.setAnchor(); }), google.maps.event.addListener(this.marker_, "labelclass_changed", function () { me.setStyles(); }), google.maps.event.addListener(this.marker_, "labelstyle_changed", function () { me.setStyles(); }) ]; }; /** * Removes the DIV for the label from the DOM. It also removes all event handlers. * This method is called automatically when the marker's <code>setMap(null)</code> * method is called. * #private */ MarkerLabel_.prototype.onRemove = function () { var i; this.labelDiv_.parentNode.removeChild(this.labelDiv_); // Remove event listeners: for (i = 0; i < this.listeners_.length; i++) { google.maps.event.removeListener(this.listeners_[i]); } }; /** * Draws the label on the map. * #private */ MarkerLabel_.prototype.draw = function () { this.setContent(); this.setTitle(); this.setStyles(); }; /** * Sets the content of the label. * The content can be plain text or an HTML DOM node. * #private */ MarkerLabel_.prototype.setContent = function () { var content = this.marker_.get("labelContent"); if (typeof content.nodeType === "undefined") { this.labelDiv_.innerHTML = content; } else { this.labelDiv_.innerHTML = ""; // Remove current content this.labelDiv_.appendChild(content); } }; /** * Sets the content of the tool tip for the label. It is * always set to be the same as for the marker itself. * #private */ MarkerLabel_.prototype.setTitle = function () { this.labelDiv_.title = this.marker_.getTitle() || ""; }; /** * Sets the style of the label by setting the style sheet and applying * other specific styles requested. * #private */ MarkerLabel_.prototype.setStyles = function () { var i, labelStyle; // Apply style values from the style sheet defined in the labelClass parameter: this.labelDiv_.className = this.marker_.get("labelClass"); // Clear existing inline style values: this.labelDiv_.style.cssText = ""; // Apply style values defined in the labelStyle parameter: labelStyle = this.marker_.get("labelStyle"); for (i in labelStyle) { if (labelStyle.hasOwnProperty(i)) { this.labelDiv_.style[i] = labelStyle[i]; } } this.setMandatoryStyles(); }; /** * Sets the mandatory styles to the DIV representing the label as well as to the * associated event DIV. This includes setting the DIV position, z-index, and visibility. * #private */ MarkerLabel_.prototype.setMandatoryStyles = function () { this.labelDiv_.style.position = "absolute"; this.labelDiv_.style.overflow = "hidden"; // Make sure the opacity setting causes the desired effect on MSIE: if (typeof this.labelDiv_.style.opacity !== "undefined" && this.labelDiv_.style.opacity !== "") { this.labelDiv_.style.MsFilter = "\"progid:DXImageTransform.Microsoft.Alpha(opacity=" + (this.labelDiv_.style.opacity * 100) + ")\""; this.labelDiv_.style.filter = "alpha(opacity=" + (this.labelDiv_.style.opacity * 100) + ")"; } this.setAnchor(); this.setPosition(); // This also updates z-index, if necessary. this.setVisible(); }; /** * Sets the anchor point of the label. * #private */ MarkerLabel_.prototype.setAnchor = function () { var anchor = this.marker_.get("labelAnchor"); this.labelDiv_.style.marginLeft = -anchor.x + "px"; this.labelDiv_.style.marginTop = -anchor.y + "px"; }; /** * Sets the position of the label. The z-index is also updated, if necessary. * #private */ MarkerLabel_.prototype.setPosition = function (yOffset) { var position = this.getProjection().fromLatLngToDivPixel(this.marker_.getPosition()); if (typeof yOffset === "undefined") { yOffset = 0; } this.labelDiv_.style.left = Math.round(position.x) + "px"; this.labelDiv_.style.top = Math.round(position.y - yOffset) + "px"; this.setZIndex(); }; /** * Sets the z-index of the label. If the marker's z-index property has not been defined, the z-index * of the label is set to the vertical coordinate of the label. This is in keeping with the default * stacking order for Google Maps: markers to the south are in front of markers to the north. * #private */ MarkerLabel_.prototype.setZIndex = function () { var zAdjust = (this.marker_.get("labelInBackground") ? -1 : +1); if (typeof this.marker_.getZIndex() === "undefined") { this.labelDiv_.style.zIndex = parseInt(this.labelDiv_.style.top, 10) + zAdjust; } else { this.labelDiv_.style.zIndex = this.marker_.getZIndex() + zAdjust; } }; /** * Sets the visibility of the label. The label is visible only if the marker itself is * visible (i.e., its visible property is true) and the labelVisible property is true. * #private */ MarkerLabel_.prototype.setVisible = function () { if (this.marker_.get("labelVisible")) { this.labelDiv_.style.display = this.marker_.getVisible() ? "block" : "none"; } else { this.labelDiv_.style.display = "none"; } }; /** * #name MarkerWithLabelOptions * #class This class represents the optional parameter passed to the {#link MarkerWithLabel} constructor. * The properties available are the same as for <code>google.maps.Marker</code> with the addition * of the properties listed below. To change any of these additional properties after the labeled * marker has been created, call <code>google.maps.Marker.set(propertyName, propertyValue)</code>. * <p> * When any of these properties changes, a property changed event is fired. The names of these * events are derived from the name of the property and are of the form <code>propertyname_changed</code>. * For example, if the content of the label changes, a <code>labelcontent_changed</code> event * is fired. * <p> * #property {string|Node} [labelContent] The content of the label (plain text or an HTML DOM node). * #property {Point} [labelAnchor] By default, a label is drawn with its anchor point at (0,0) so * that its top left corner is positioned at the anchor point of the associated marker. Use this * property to change the anchor point of the label. For example, to center a 50px-wide label * beneath a marker, specify a <code>labelAnchor</code> of <code>google.maps.Point(25, 0)</code>. * (Note: x-values increase to the right and y-values increase to the top.) * #property {string} [labelClass] The name of the CSS class defining the styles for the label. * Note that style values for <code>position</code>, <code>overflow</code>, <code>top</code>, * <code>left</code>, <code>zIndex</code>, <code>display</code>, <code>marginLeft</code>, and * <code>marginTop</code> are ignored; these styles are for internal use only. * #property {Object} [labelStyle] An object literal whose properties define specific CSS * style values to be applied to the label. Style values defined here override those that may * be defined in the <code>labelClass</code> style sheet. If this property is changed after the * label has been created, all previously set styles (except those defined in the style sheet) * are removed from the label before the new style values are applied. * Note that style values for <code>position</code>, <code>overflow</code>, <code>top</code>, * <code>left</code>, <code>zIndex</code>, <code>display</code>, <code>marginLeft</code>, and * <code>marginTop</code> are ignored; these styles are for internal use only. * #property {boolean} [labelInBackground] A flag indicating whether a label that overlaps its * associated marker should appear in the background (i.e., in a plane below the marker). * The default is <code>false</code>, which causes the label to appear in the foreground. * #property {boolean} [labelVisible] A flag indicating whether the label is to be visible. * The default is <code>true</code>. Note that even if <code>labelVisible</code> is * <code>true</code>, the label will <i>not</i> be visible unless the associated marker is also * visible (i.e., unless the marker's <code>visible</code> property is <code>true</code>). * #property {boolean} [raiseOnDrag] A flag indicating whether the label and marker are to be * raised when the marker is dragged. The default is <code>true</code>. If a draggable marker is * being created and a version of Google Maps API earlier than V3.3 is being used, this property * must be set to <code>false</code>. * #property {boolean} [optimized] A flag indicating whether rendering is to be optimized for the * marker. <b>Important: The optimized rendering technique is not supported by MarkerWithLabel, * so the value of this parameter is always forced to <code>false</code>. * #property {string} [crossImage="http://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png"] * The URL of the cross image to be displayed while dragging a marker. * #property {string} [handCursor="http://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur"] * The URL of the cursor to be displayed while dragging a marker. */ /** * Creates a MarkerWithLabel with the options specified in {#link MarkerWithLabelOptions}. * #constructor * #param {MarkerWithLabelOptions} [opt_options] The optional parameters. */ function MarkerWithLabel(opt_options) { opt_options = opt_options || {}; opt_options.labelContent = opt_options.labelContent || ""; opt_options.labelAnchor = opt_options.labelAnchor || new google.maps.Point(0, 0); opt_options.labelClass = opt_options.labelClass || "markerLabels"; opt_options.labelStyle = opt_options.labelStyle || {}; opt_options.labelInBackground = opt_options.labelInBackground || false; if (typeof opt_options.labelVisible === "undefined") { opt_options.labelVisible = true; } if (typeof opt_options.raiseOnDrag === "undefined") { opt_options.raiseOnDrag = true; } if (typeof opt_options.clickable === "undefined") { opt_options.clickable = true; } if (typeof opt_options.draggable === "undefined") { opt_options.draggable = false; } if (typeof opt_options.optimized === "undefined") { opt_options.optimized = false; } opt_options.crossImage = opt_options.crossImage || "http" + (document.location.protocol === "https:" ? "s" : "") + "://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png"; opt_options.handCursor = opt_options.handCursor || "http" + (document.location.protocol === "https:" ? "s" : "") + "://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur"; opt_options.optimized = false; // Optimized rendering is not supported ////////////////////////////////////////////////////////// // New ////////////////////////////////////////////////////////// this.label = new MarkerLabel_(opt_options.id, this, opt_options.crossImage, opt_options.handCursor); // Bind the label to the marker // Call the parent constructor. It calls Marker.setValues to initialize, so all // the new parameters are conveniently saved and can be accessed with get/set. // Marker.set triggers a property changed event (called "propertyname_changed") // that the marker label listens for in order to react to state changes. google.maps.Marker.apply(this, arguments); } inherits(MarkerWithLabel, google.maps.Marker); /** * Overrides the standard Marker setMap function. * #param {Map} theMap The map to which the marker is to be added. * #private */ MarkerWithLabel.prototype.setMap = function (theMap) { // Call the inherited function... google.maps.Marker.prototype.setMap.apply(this, arguments); // ... then deal with the label: this.label.setMap(theMap); }; If you would like to compress/minify/uglify the code, I recommend https://javascript-minifier.com/
Stop a function from running after being passed a value
I'm trying to stop the displayDistance function from running until I click the button. It keeps running the function, I assume because values are being passed to it so it automatically calls it I guess? I need it to display the current lat/long on window load, and then after a button click display the distance to each locations. Right now it displays all it on window load window.addEventListener("load", setupEventHandlers, false); window.addEventListener("load", displayLocation, false); function setupEventHandlers() { console.log("Inside setupEventHandlers function that is fired on the window load event"); var displayButtonReference = document.getElementById("butCalculate"); displayButtonReference.addEventListener("click", displayDistance, false); } //------------------------------------------------------------------------------- // Name: displayLocation() // Description: //------------------------------------------------------------------------------- function displayLocation() { console.log("displayLocation function called!"); //Call getCurrentPosition() method to retrieve current position data //This is an asynchronous call. //When completed it calls the cb_GetCurrentPosition_Success callback function //navigator.geolocation.getCurrentPosition(cb_GetCurrentPosition_Success); navigator.geolocation.getCurrentPosition(cb_GetCurrentPosition_Success, cb_GetCurrentPosition_Error, { enableHighAccuracy: true, maximumAge: 5000 } ); } //------------------------------------------------------------------------------- // Name: cb_GetCurrentPosition_Success // Description: Callback function if GeoLocation info retrieved successfully //------------------------------------------------------------------------------- function cb_GetCurrentPosition_Success(positionObject) { console.log("cb_GetCurrentPosition_Success callback function called!"); //Show returned positionObject in Inspect Element Console console.dir(positionObject); //1. Declare variables var accuracy, distanceInKilometers, distanceInFeet, distanceInMiles, displayString, currentLat, currentLong; //Extract geolocation data from returned object currentLat = positionObject.coords.latitude; currentLong = positionObject.coords.longitude; console.log("distanceInFeet = " + distanceInFeet); distanceInFeet = calculateDistanceInFeet(currentLat, currentLong) //Display retrieved Geolocation data document.getElementById("divLat").innerHTML = currentLat; document.getElementById("divLon").innerHTML = currentLong; } //--------------------------------------------------------------------------------------- // Name: cb_GetCurrentPosition_Error // Description: Callback function if there is a problem retrieving GeoLocation info //--------------------------------------------------------------------------------------- function cb_GetCurrentPosition_Error(err) { var errorMessage; if (err.code == 1) { errorMessage = "You chose not to share your location info."; } else if (err.code == 2) { errorMessage = "Location information currently unavailable!"; } else if (err.code == 3) { errorMessage = "Timed out waiting to receive location information!"; } else { errorMessage = "Unknown error occured"; } } // --------------------------------------------------------------------------------------- // name: calculateDistanceInFeet // source: http://www.codecodex.com/wiki/Calculate_distance_between_two_points_on_a_globe // --------------------------------------------------------------------------------------- function calculateDistanceInFeet(parcurrentLat, parcurrentLon) { console.log("calculateDistanceInFeet function called!"); console.log("parcurrentLat = " + parcurrentLat); console.log("parcurrentLon = " + parcurrentLon); var distanceInKilometersToRamsey, distanceInKilometersToPicnic, distanceInFeetToRamsey, distanceInFeetToPicnic, Lat1 = 35.303209, //Ramsey Coordinates Lon1 = -83.182745, Lat2 = 35.314455, //Picnic Shelter Coordinates Lon2 = -83.188011; //Distance to Ramsey //Code retrieved from source shown above - don't modify //------------------------------------------------------- var R1 = 6371; // km var dLat1 = (Lat1 - parcurrentLat) * Math.PI / 180; var dLon1 = (Lon1 - parcurrentLon) * Math.PI / 180; var a1 = Math.sin(dLat1 / 2) * Math.sin(dLat1 / 2) + Math.cos(parcurrentLat * Math.PI / 180) * Math.cos(Lat1 * Math.PI / 180) * Math.sin(dLon1 / 2) * Math.sin(dLon1 / 2); var c1 = 2 * Math.asin(Math.sqrt(a1)); distanceInKilometersToRamsey = R1 * c1; //--------------------------------------------------------- //end of code to not modify //Distance to Picnic //Code retrieved from source shown above - don't modify //------------------------------------------------------- var R2 = 6371; // km var dLat2 = (Lat2 - parcurrentLat) * Math.PI / 180; var dLon2 = (Lon2 - parcurrentLon) * Math.PI / 180; var a2 = Math.sin(dLat2 / 2) * Math.sin(dLat2 / 2) + Math.cos(parcurrentLat * Math.PI / 180) * Math.cos(Lat2 * Math.PI / 180) * Math.sin(dLon2 / 2) * Math.sin(dLon2 / 2); var c2 = 2 * Math.asin(Math.sqrt(a2)); distanceInKilometersToPicnic = R2 * c2; //--------------------------------------------------------- //end of code to not modify //3. Do calculations distanceInFeetToRamsey = distanceInKilometersToRamsey * 1000 * 3.2808; distanceInFeetToPicnic = distanceInKilometersToPicnic * 1000 * 3.2808; var passedDistance = displayDistance(distanceInFeetToRamsey, distanceInFeetToPicnic); console.log("distanceInFeetToRamsey = " + distanceInFeetToRamsey); console.log("distanceInFeetToPicnic = " + distanceInFeetToPicnic); } function displayDistance(parRamsey, parPicnic) { if (parRamsey >= 5280) { var distanceInMilesToRamsey = parRamsey / 5280; document.getElementById("divDistance1").innerHTML = distanceInMilesToRamsey.toFixed(2) + "miles"; } else { document.getElementById("divDistance1").innerHTML = parPicnic.toFixed(2) + "feet"; } if (parPicnic >= 5280) { var distanceInMilesToPicnic = distanceInFeetToPicnic / 5280; document.getElementById("divDistance2").innerHTML = distanceInMilesToPicnic.toFixed(2) + "miles"; } else { document.getElementById("divDistance2").innerHTML = parPicnic.toFixed(2) + "feet"; } }
displayDistance is getting indirectly called by your load event: Loading calls displayLocation: window.addEventListener("load", displayLocation, false); displayLocation calls cb_GetCurrentPosition_Success (via a callback): navigator.geolocation.getCurrentPosition(cb_GetCurrentPosition_Success, cb_GetCurrentPosition_Error, { enableHighAccuracy: true, maximumAge: 5000 } ); cb_GetCurrentPosition_Success calls calculateDistanceInFeet: distanceInFeet = calculateDistanceInFeet(currentLat, currentLong) And calculateDistanceInFeet calls displayDistance: var passedDistance = displayDistance(distanceInFeetToRamsey, distanceInFeetToPicnic);
How to animate zoom on jquery iviewer plugin?
I have this jquery plugin that zooms in on photos, but I would like to animate the zoom so it just doesn't zoom in, but so that there is like an animated effect on the zoom. Is this possible? Here is the page I have the plugin set up on right now. http://www.buildinfocus.com/clients/gallery_detail.php?title=1&mapid=239 Maybe something like this http://api.jquery.com/animate/ Below is the code I have for plugin (function($){ $.fn.iviewer = function(o) { return this.each(function() { $(this).data('viewer', new $iv(this,o)); }); } var defaults = { /** * start zoom value for image, not used now * may be equal to "fit" to fit image into container or scale in % **/ zoom: "fit", /** * base value to scale image **/ zoom_base: 100, /** * maximum zoom **/ zoom_max: 800, /** * minimum zoom **/ zoom_min: 25, /** * base of rate multiplier. * zoom is calculated by formula: zoom_base * zoom_delta^rate **/ zoom_delta: 1.4, /** * if true plugin doesn't add its own controls **/ ui_disabled: false, /** * if false, plugin doesn't bind resize event on window and this must * be handled manually **/ update_on_resize: true, /** * event is triggered when zoom value is changed * #param int new zoom value * #return boolean if false zoom action is aborted **/ onZoom: null, /** * callback is fired after plugin setup **/ initCallback: null, /** * event is fired on drag begin * #param object coords mouse coordinates on the image * #return boolean if false is returned, drag action is aborted **/ onStartDrag: null, /** * event is fired on drag action * #param object coords mouse coordinates on the image **/ onDrag: null, /** * event is fired when mouse moves over image * #param object coords mouse coordinates on the image **/ onMouseMove: null, /** * mouse click event * #param object coords mouse coordinates on the image **/ onClick: null, /** * event is fired when image starts to load */ onStartLoad: null, /** * event is fired, when image is loaded and initially positioned */ onFinishLoad: null }; $.iviewer = function(e,o) { var me = this; /* object containing actual information about image * #img_object.object - jquery img object * #img_object.orig_{width|height} - original dimensions * #img_object.display_{width|height} - actual dimensions */ this.img_object = {}; this.zoom_object = {}; //object to show zoom status this.image_loaded = false; //drag variables this.dx = 0; this.dy = 0; this.dragged = false; this.settings = $.extend({}, defaults, o || {}); this.current_zoom = this.settings.zoom; if(this.settings.src === null){ return; } this.container = $(e); this.update_container_info(); //init container this.container.css("overflow","hidden"); if(this.settings.update_on_resize == true) { $(window).resize(function() { me.update_container_info(); }); } this.img_object.x = 0; this.img_object.y = 0; //init object this.img_object.object = $("<img>"). css({ position: "absolute", top :"0px", left: "0px"}). //this is needed, because chromium sets them auto otherwise //bind mouse events mousedown(function(e){ return me.drag_start(e); }). mousemove(function(e){return me.drag(e)}). mouseup(function(e){return me.drag_end(e)}). click(function(e){return me.click(e)}). mouseleave(function(e){return me.drag_end(e)}). mousewheel(function(ev, delta) { //this event is there instead of containing div, because //at opera it triggers many times on div var zoom = (delta > 0)?1:-1; me.zoom_by(zoom); return false; }); this.img_object.object.prependTo(me.container); this.loadImage(this.settings.src); if(!this.settings.ui_disabled) { this.createui(); } if(this.settings.initCallback) { this.settings.initCallback.call(this); } } var $iv = $.iviewer; $iv.fn = $iv.prototype = { iviewer : "0.4.2" } $iv.fn.extend = $iv.extend = $.extend; $iv.fn.extend({ loadImage: function(src) { this.current_zoom = this.settings.zoom; this.image_loaded = false; var me = this; if(this.settings.onStartLoad) { this.settings.onStartLoad.call(this); } this.img_object.object.unbind('load'). removeAttr("src"). removeAttr("width"). removeAttr("height"). css({ top: 0, left: 0 }). load(function(){ me.image_loaded = true; me.img_object.display_width = me.img_object.orig_width = this.width; me.img_object.display_height = me.img_object.orig_height = this.height; if(!me.container.hasClass("iviewer_cursor")){ me.container.addClass("iviewer_cursor"); } if(me.settings.zoom == "fit"){ me.fit(); } else { me.set_zoom(me.settings.zoom); } if(me.settings.onFinishLoad) { me.settings.onFinishLoad.call(me); } //src attribute is after setting load event, or it won't work }).attr("src",src); }, /** * fits image in the container **/ fit: function() { var aspect_ratio = this.img_object.orig_width / this.img_object.orig_height; var window_ratio = this.settings.width / this.settings.height; var choose_left = (aspect_ratio > window_ratio); var new_zoom = 0; if(choose_left){ new_zoom = this.settings.width / this.img_object.orig_width * 100; } else { new_zoom = this.settings.height / this.img_object.orig_height * 100; } this.set_zoom(new_zoom); }, /** * center image in container **/ center: function() { this.setCoords(-Math.round((this.img_object.display_height - this.settings.height)/2), -Math.round((this.img_object.display_width - this.settings.width)/2)); }, /** * move a point in container to the center of display area * #param x a point in container * #param y a point in container **/ moveTo: function(x, y) { var dx = x-Math.round(this.settings.width/2); var dy = y-Math.round(this.settings.height/2); var new_x = this.img_object.x - this.dx; var new_y = this.img_object.y - this.dy; this.setCoords(new_x, new_y); }, /** * set coordinates of upper left corner of image object **/ setCoords: function(x,y) { //do nothing while image is being loaded if(!this.image_loaded) { return; } //check new coordinates to be correct (to be in rect) if(y > 0){ y = 0; } if(x > 0){ x = 0; } if(y + this.img_object.display_height < this.settings.height){ y = this.settings.height - this.img_object.display_height; } if(x + this.img_object.display_width < this.settings.width){ x = this.settings.width - this.img_object.display_width; } if(this.img_object.display_width <= this.settings.width){ x = -(this.img_object.display_width - this.settings.width)/2; } if(this.img_object.display_height <= this.settings.height){ y = -(this.img_object.display_height - this.settings.height)/2; } this.img_object.x = x; this.img_object.y = y; this.img_object.object.css("top",y + "px") .css("left",x + "px"); }, /** * convert coordinates on the container to the coordinates on the image (in original size) * * #return object with fields x,y according to coordinates or false * if initial coords are not inside image **/ containerToImage : function (x,y) { if(x < this.img_object.x || y < this.img_object.y || x > this.img_object.x + this.img_object.display_width || y > this.img_object.y + this.img_object.display_height) { return false; } return { x : $iv.descaleValue(x - this.img_object.x, this.current_zoom), y : $iv.descaleValue(y - this.img_object.y, this.current_zoom) }; }, /** * convert coordinates on the image (in original size) to the coordinates on the container * * #return object with fields x,y according to coordinates or false * if initial coords are not inside image **/ imageToContainer : function (x,y) { if(x > this.img_object.orig_width || y > this.img_object.orig_height) { return false; } return { x : this.img_object.x + $iv.scaleValue(x, this.current_zoom), y : this.img_object.y + $iv.scaleValue(y, this.current_zoom) }; }, /** * get mouse coordinates on the image * #param e - object containing pageX and pageY fields, e.g. mouse event object * * #return object with fields x,y according to coordinates or false * if initial coords are not inside image **/ getMouseCoords : function(e) { var img_offset = this.img_object.object.offset(); return { x : $iv.descaleValue(e.pageX - img_offset.left, this.current_zoom), y : $iv.descaleValue(e.pageY - img_offset.top, this.current_zoom) }; }, /** * set image scale to the new_zoom * #param new_zoom image scale in % **/ set_zoom: function(new_zoom) { if(this.settings.onZoom && this.settings.onZoom.call(this, new_zoom) == false) { return; } //do nothing while image is being loaded if(!this.image_loaded) { return; } if(new_zoom < this.settings.zoom_min) { new_zoom = this.settings.zoom_min; } else if(new_zoom > this.settings.zoom_max) { new_zoom = this.settings.zoom_max; } /* we fake these values to make fit zoom properly work */ if(this.current_zoom == "fit") { var old_x = Math.round(this.settings.width/2 + this.img_object.orig_width/2); var old_y = Math.round(this.settings.height/2 + this.img_object.orig_height/2); this.current_zoom = 100; } else { var old_x = -parseInt(this.img_object.object.css("left"),10) + Math.round(this.settings.width/2); var old_y = -parseInt(this.img_object.object.css("top"),10) + Math.round(this.settings.height/2); } var new_width = $iv.scaleValue(this.img_object.orig_width, new_zoom); var new_height = $iv.scaleValue(this.img_object.orig_height, new_zoom); var new_x = $iv.scaleValue( $iv.descaleValue(old_x, this.current_zoom), new_zoom); var new_y = $iv.scaleValue( $iv.descaleValue(old_y, this.current_zoom), new_zoom); new_x = this.settings.width/2 - new_x; new_y = this.settings.height/2 - new_y; this.img_object.object.attr("width",new_width) .attr("height",new_height); this.img_object.display_width = new_width; this.img_object.display_height = new_height; this.setCoords(new_x, new_y); this.current_zoom = new_zoom; $.isFunction( this.settings.onAfterZoom ) && this.settings.onAfterZoom.call( this, new_zoom ); this.update_status(); }, /** * changes zoom scale by delta * zoom is calculated by formula: zoom_base * zoom_delta^rate * #param Integer delta number to add to the current multiplier rate number **/ zoom_by: function(delta) { var closest_rate = this.find_closest_zoom_rate(this.current_zoom); var next_rate = closest_rate + delta; var next_zoom = this.settings.zoom_base * Math.pow(this.settings.zoom_delta, next_rate) if(delta > 0 && next_zoom < this.current_zoom) { next_zoom *= this.settings.zoom_delta; } if(delta < 0 && next_zoom > this.current_zoom) { next_zoom /= this.settings.zoom_delta; } this.set_zoom(next_zoom); }, /** * finds closest multiplier rate for value * basing on zoom_base and zoom_delta values from settings * #param Number value zoom value to examine **/ find_closest_zoom_rate: function(value) { if(value == this.settings.zoom_base) { return 0; } function div(val1,val2) { return val1 / val2 }; function mul(val1,val2) { return val1 * val2 }; var func = (value > this.settings.zoom_base)?mul:div; var sgn = (value > this.settings.zoom_base)?1:-1; var mltplr = this.settings.zoom_delta; var rate = 1; while(Math.abs(func(this.settings.zoom_base, Math.pow(mltplr,rate)) - value) > Math.abs(func(this.settings.zoom_base, Math.pow(mltplr,rate+1)) - value)) { rate++; } return sgn * rate; }, /* update scale info in the container */ update_status: function() { if(!this.settings.ui_disabled) { var percent = Math.round(100*this.img_object.display_height/this.img_object.orig_height); if(percent) { this.zoom_object.html(percent + "%"); } } }, update_container_info: function() { this.settings.height = this.container.height(); this.settings.width = this.container.width(); }, /** * callback for handling mousdown event to start dragging image **/ drag_start: function(e) { if(this.settings.onStartDrag && this.settings.onStartDrag.call(this,this.getMouseCoords(e)) == false) { return false; } /* start drag event*/ this.dragged = true; this.container.addClass("iviewer_drag_cursor"); this.dx = e.pageX - this.img_object.x; this.dy = e.pageY - this.img_object.y; return false; }, /** * callback for handling mousmove event to drag image **/ drag: function(e) { this.settings.onMouseMove && this.settings.onMouseMove.call(this,this.getMouseCoords(e)); if(this.dragged){ this.settings.onDrag && this.settings.onDrag.call(this,this.getMouseCoords(e)); var ltop = e.pageY -this.dy; var lleft = e.pageX -this.dx; this.setCoords(lleft, ltop); return false; } }, /** * callback for handling stop drag **/ drag_end: function(e) { this.container.removeClass("iviewer_drag_cursor"); this.dragged=false; }, click: function(e) { this.settings.onClick && this.settings.onClick.call(this,this.getMouseCoords(e)); }, /** * create zoom buttons info box **/ createui: function() { var me=this; $("<div>").addClass("iviewer_zoom_in").addClass("iviewer_common"). addClass("iviewer_button"). mousedown(function(){me.zoom_by(1); return false;}).appendTo(this.container); $("<div>").addClass("iviewer_zoom_out").addClass("iviewer_common"). addClass("iviewer_button"). mousedown(function(){me.zoom_by(- 1); return false;}).appendTo(this.container); $("<div>").addClass("iviewer_zoom_zero").addClass("iviewer_common"). addClass("iviewer_button"). mousedown(function(){me.set_zoom(100); return false;}).appendTo(this.container); $("<div>").addClass("iviewer_zoom_fit").addClass("iviewer_common"). addClass("iviewer_button"). mousedown(function(){me.fit(this); return false;}).appendTo(this.container); this.zoom_object = $("<div>").addClass("iviewer_zoom_status").addClass("iviewer_common"). appendTo(this.container); this.update_status(); //initial status update } }); $iv.extend({ scaleValue: function(value, toZoom) { return value * toZoom / 100; }, descaleValue: function(value, fromZoom) { return value * 100 / fromZoom; } }); })(jQuery);
Try updating to the latest version, it looks to support smooth zoom transition (so you won't have to implement this yourself): https://github.com/can3p/iviewer/blob/master/jquery.iviewer.js#L415 On line 415, the set_zoom function animates the trnasition, as opposed to the older code you pasted, which just changes the css values.
The last version of the plugin(0.5) supports smooth zoom, try it.