How to animate zoom on jquery iviewer plugin? - javascript

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.

Related

Random movement of circles created by the script

I have a function that craeates divs with a circle.
Now they are all created and appear at the beginning of the page and go further in order.
Next, I need each circle to appear in a random place. I did this.
Now I need all of them to move randomly across the entire page, I have difficulties with this.
Here is an example of how everything works for one element that is already on the page.
https://jsfiddle.net/quej8wko/
But when I add this code, all my created circles don't move.
I get an error:
"message": "Uncaught TypeError: Cannot set properties of null (setting 'willChange')",
This is probably due to the fact that initially there are no circles on the page. How can I connect the code so that all created circles move?
//creating circles
var widthHeight = 40; // <-- circle width
var margin = 20; // <-- margin - is it necessary ?
var delta = widthHeight + margin;
function createDiv(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = document.documentElement.clientHeight;
var documentWidth = document.documentElement.clientWidth;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.backgroundColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
// Get the random positions minus the delta
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
// Keep the positions between -20px and the current positions
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
document.body.appendChild(div);
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`)
}, oneSecond);
//move circles
function RandomObjectMover(obj, container) {
this.$object = obj;
this.$container = container;
this.container_is_window = container === window;
this.pixels_per_second = 250;
this.current_position = { x: 0, y: 0 };
this.is_running = false;
}
// Set the speed of movement in Pixels per Second.
RandomObjectMover.prototype.setSpeed = function(pxPerSec) {
this.pixels_per_second = pxPerSec;
}
RandomObjectMover.prototype._getContainerDimensions = function() {
if (this.$container === window) {
return { 'height' : this.$container.innerHeight, 'width' : this.$container.innerWidth };
} else {
return { 'height' : this.$container.clientHeight, 'width' : this.$container.clientWidth };
}
}
RandomObjectMover.prototype._generateNewPosition = function() {
// Get container dimensions minus div size
var containerSize = this._getContainerDimensions();
var availableHeight = containerSize.height - this.$object.clientHeight;
var availableWidth = containerSize.width - this.$object.clientHeight;
// Pick a random place in the space
var y = Math.floor(Math.random() * availableHeight);
var x = Math.floor(Math.random() * availableWidth);
return { x: x, y: y };
}
RandomObjectMover.prototype._calcDelta = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt( dx*dx + dy*dy );
return dist;
}
RandomObjectMover.prototype._moveOnce = function() {
// Pick a new spot on the page
var next = this._generateNewPosition();
// How far do we have to move?
var delta = this._calcDelta(this.current_position, next);
// Speed of this transition, rounded to 2DP
var speed = Math.round((delta / this.pixels_per_second) * 100) / 100;
//console.log(this.current_position, next, delta, speed);
this.$object.style.transition='transform '+speed+'s linear';
this.$object.style.transform='translate3d('+next.x+'px, '+next.y+'px, 0)';
// Save this new position ready for the next call.
this.current_position = next;
};
RandomObjectMover.prototype.start = function() {
if (this.is_running) {
return;
}
// Make sure our object has the right css set
this.$object.willChange = 'transform';
this.$object.pointerEvents = 'auto';
this.boundEvent = this._moveOnce.bind(this)
// Bind callback to keep things moving
this.$object.addEventListener('transitionend', this.boundEvent);
// Start it moving
this._moveOnce();
this.is_running = true;
}
RandomObjectMover.prototype.stop = function() {
if (!this.is_running) {
return;
}
this.$object.removeEventListener('transitionend', this.boundEvent);
this.is_running = false;
}
// Init it
var x = new RandomObjectMover(document.querySelector(".circle"), window);
// Start it off
x.start();
.circle {
clip-path: circle(50%);
height: 40px;
width: 40px;
margin: 20px;
position: absolute;
}
I have modified the snippet which works as you expected.
There was a mistake where you were initializing and creating the object instance only once and none of the div elements that you created inside the setInterval function never got Instantiated.
I think you are just starting out with JavaScript with this sample project.
Below are few suggestions:
Learn to debug the code. You should be using dev tools by making use of debugger statement where it takes you to the source code to analyze the variable scope and stack during the runtime. console.log also helps in few situations.
I could see a lot of confusing naming convention (You have named the create div parameter as id but creating a div class using that id)
Try using ES6 features (class syntax is really good when writing OOP in JS although it's just a syntactic sugar for prototype)
//creating circles
var widthHeight = 40; // <-- circle width
var margin = 20; // <-- margin - is it necessary ?
var delta = widthHeight + margin;
function createAndInitializeDivObject(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = document.documentElement.clientHeight;
var documentWidth = document.documentElement.clientWidth;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.backgroundColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
// Get the random positions minus the delta
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
// Keep the positions between -20px and the current positions
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
document.body.appendChild(div);
var x = new RandomObjectMover(document.querySelector(`.${id}`), window);
x.start();
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createAndInitializeDivObject(`circle${i}`)
}, oneSecond);
//move circles
function RandomObjectMover(obj, container) {
this.$object = obj;
this.$container = container;
this.container_is_window = container === window;
this.pixels_per_second = 250;
this.current_position = { x: 0, y: 0 };
this.is_running = false;
}
// Set the speed of movement in Pixels per Second.
RandomObjectMover.prototype.setSpeed = function(pxPerSec) {
this.pixels_per_second = pxPerSec;
}
RandomObjectMover.prototype._getContainerDimensions = function() {
if (this.$container === window) {
return { 'height' : this.$container.innerHeight, 'width' : this.$container.innerWidth };
} else {
return { 'height' : this.$container.clientHeight, 'width' : this.$container.clientWidth };
}
}
RandomObjectMover.prototype._generateNewPosition = function() {
// Get container dimensions minus div size
var containerSize = this._getContainerDimensions();
var availableHeight = containerSize.height - this.$object.clientHeight;
var availableWidth = containerSize.width - this.$object.clientHeight;
// Pick a random place in the space
var y = Math.floor(Math.random() * availableHeight);
var x = Math.floor(Math.random() * availableWidth);
return { x: x, y: y };
}
RandomObjectMover.prototype._calcDelta = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt( dx*dx + dy*dy );
return dist;
}
RandomObjectMover.prototype._moveOnce = function() {
// Pick a new spot on the page
var next = this._generateNewPosition();
// How far do we have to move?
var delta = this._calcDelta(this.current_position, next);
// Speed of this transition, rounded to 2DP
var speed = Math.round((delta / this.pixels_per_second) * 100) / 100;
//console.log(this.current_position, next, delta, speed);
this.$object.style.transition='transform '+speed+'s linear';
this.$object.style.transform='translate3d('+next.x+'px, '+next.y+'px, 0)';
// Save this new position ready for the next call.
this.current_position = next;
};
RandomObjectMover.prototype.start = function() {
if (this.is_running) {
return;
}
// Make sure our object has the right css set
this.$object.willChange = 'transform';
this.$object.pointerEvents = 'auto';
this.boundEvent = this._moveOnce.bind(this)
// Bind callback to keep things moving
this.$object.addEventListener('transitionend', this.boundEvent);
// Start it moving
this._moveOnce();
this.is_running = true;
}
RandomObjectMover.prototype.stop = function() {
if (!this.is_running) {
return;
}
this.$object.removeEventListener('transitionend', this.boundEvent);
this.is_running = false;
}
// Init it
var x = new RandomObjectMover(document.querySelector(".circle"), window);
// Start it off
x.start();
.circle {
width: 35px;
height: 35px;
border-radius: 35px;
background-color: #ffffff;
border: 3px solid purple;
position: absolute;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="circle"></div>
<script src="app.js"></script>
</body>
</html>

Make cursor stay in boundary

I have a color-wheel picker (which I took a good chunk of code from this library). I'm trying to get the color-wheels cursor to not go out of boundaries. I don't want it going pass the gray border.
I can do the obvious and make the parent div: overflow:hidden, but that would just hide the cursor, it wouldn't make it not go pass the boundaries.
I think the relevant variable to edit, is one of the following (in the hsvMove function, starting on line 39):
var r = currentTargetHeight / 2,
x = e.pageX - startPoint.left - r,
y = e.pageY - startPoint.top - r
How can I get the cursor to not go past the boundaries?
JSFiddle
(function(window) {
"use strict"
// Some common use variables
var myColor = new Colors(),
startPoint,
currentTarget,
currentTargetHeight = 0,
PI = Math.PI,
PI2 = PI * 2;
/* ---------------------------------- */
/* ---- HSV-circle color picker ----- */
/* ---------------------------------- */
var colorDiskWrapper = document.getElementById('colorDiskWrapper'),
colorDiskCursor = document.getElementById('colorDiskCursor'),
colorDisk = document.getElementById('colorDisk');
var colorDiscRadius = colorDisk.offsetHeight / 2;
// Create Event Functions
var hsvDown = function(e) { // mouseDown callback
var target = e.target || e.srcElement;
if (e.preventDefault) e.preventDefault();
if (target === colorDiskCursor || target === colorDisk) {
currentTarget = target.parentNode;
} else
return;
startPoint = getOrigin(currentTarget);
currentTargetHeight = currentTarget.offsetHeight;
addEvent(window, 'mousemove', hsvMove);
hsvMove(e);
startRender();
},
hsvMove = function(e) { // mouseMove callback
var r = currentTargetHeight / 2,
x = e.pageX - startPoint.left - r,
y = e.pageY - startPoint.top - r,
h = 360 - ((Math.atan2(y, x) * 180 / PI) + (y < 0 ? 360 : 0)),
s = (Math.sqrt((x * x) + (y * y)) / r) * 100;
myColor.setColor({
h: h,
s: s
}, 'hsv');
},
renderHSVPicker = function(color) { // used in renderCallback of 'new ColorPicker'
var x = Math.cos(PI2 - color.hsv.h * PI2),
y = Math.sin(PI2 - color.hsv.h * PI2),
r = color.hsv.s * colorDiscRadius;
// Position the Cursor
colorDiskCursor.style.left = (x * r + colorDiscRadius) + 'px';
colorDiskCursor.style.top = (y * r + colorDiscRadius) + 'px';
};
addEvent(colorDiskWrapper, 'mousedown', hsvDown); // event delegation
addEvent(window, 'mouseup', function() {
removeEvent(window, 'mousemove', hsvMove);
stopRender();
});
var doRender = function(color) {
renderHSVPicker(color);
},
renderTimer,
startRender = function(oneTime) {
if (oneTime) { // only Colors is instanciated
doRender(myColor.colors);
} else {
renderTimer = window.setInterval(
function() {
doRender(myColor.colors);
// http://stackoverflow.com/questions/2940054/
}, 13); // 1000 / 60); // ~16.666 -> 60Hz or 60fps
}
},
stopRender = function() {
window.clearInterval(renderTimer);
};
/*
Function Helpers
*/
function getOrigin(elm) {
var box = (elm.getBoundingClientRect) ? elm.getBoundingClientRect() : {
top: 0,
left: 0
},
doc = elm && elm.ownerDocument,
body = doc.body,
win = doc.defaultView || doc.parentWindow || window,
docElem = doc.documentElement || body.parentNode,
clientTop = docElem.clientTop || body.clientTop || 0, // border on html or body or both
clientLeft = docElem.clientLeft || body.clientLeft || 0;
return {
left: box.left + (win.pageXOffset || docElem.scrollLeft) - clientLeft,
top: box.top + (win.pageYOffset || docElem.scrollTop) - clientTop
};
}
function addEvent(obj, type, func) {
addEvent.cache = addEvent.cache || {
_get: function(obj, type, func, checkOnly) {
var cache = addEvent.cache[type] || [];
for (var n = cache.length; n--;) {
if (obj === cache[n].obj && '' + func === '' + cache[n].func) {
func = cache[n].func;
if (!checkOnly) {
cache[n] = cache[n].obj = cache[n].func = null;
cache.splice(n, 1);
}
return func;
}
}
},
_set: function(obj, type, func) {
var cache = addEvent.cache[type] = addEvent.cache[type] || [];
if (addEvent.cache._get(obj, type, func, true)) {
return true;
} else {
cache.push({
func: func,
obj: obj
});
}
}
};
if (!func.name && addEvent.cache._set(obj, type, func) || typeof func !== 'function') {
return;
}
if (obj.addEventListener) obj.addEventListener(type, func, false);
else obj.attachEvent('on' + type, func);
}
function removeEvent(obj, type, func) {
if (typeof func !== 'function') return;
if (!func.name) {
func = addEvent.cache._get(obj, type, func) || func;
}
if (obj.removeEventListener) obj.removeEventListener(type, func, false);
else obj.detachEvent('on' + type, func);
}
})(window);
#colorDisk {
background-image: url("http://i.imgur.com/tX5NbWs.png");
width: 350px;
height: 350px;
background-repeat: no-repeat;
cursor: pointer;
}
#colorDiskCursor {
position: absolute;
border: 2px solid black;
border-radius: 50%;
width: 9px;
height: 9px;
cursor: pointer;
}
<div id="colorDiskWrapper">
<div id="colorDisk"></div>
<div id="colorDiskCursor"></div>
</div>
<script src="https://rawgit.com/PitPik/colorPicker/master/colors.js"></script>
Problem:
Let's start by outlining the algorithm, so we are all clear about what we are trying to do: with each mouse move/click, calculate the H and S values represented by the mouse position relative to the color disk (of a HSV color system). Periodically, render the disk cursor exactly on the position corresponding to the H and S values.
There are a few things we need to take note of:
The actual radius that we should use to calculate the color values (S in particular) is the radius of the color disk minus the radius of the cursor, because we want to prevent the cursor leaving the boundary of the color disk. In this case, we have 175px - 6.5px, which is 168.5px.
When rendering the cursor, we are setting its top-left position. We need to offset the position by its radius so that our "finger pointer" appears nicely in the middle of the cursor.
Solution:
With the above understanding, the solution is straightforward.
There are problems with your code, since you are using the entire radius of the color disk (175px), without accounting for the radius of the disk cursor (6.5px).
A few things you should also fix / consider in your code:
Your currentTargetHeight is the height of the wrapper (350px), which is then halved to derive the r. This looks wrong to me. You should not be concerned with the wrapper's dimension at all. Remove this variable from the code. The values we need to be concerned with should be colorDiskRadius and colorDiskCursorRadius.
Your colorDiscCursor is set to position: absolute, but its offset parent is not the wrapper, since wrapper is not a positioned element. Hence, the top-left position that we set for colorDiscCursor may be totally unpredictable, depending where its actual parent is on an actual page. To solve this, set wrapper to position: relative.
I notice that you are not setting box-sizing (defaults to content-box), which is why your cursor is actually 13px wide despite having width: 9px; likewise for height. I personally like to use box-sizing: border-box so that when I have to do pixel-accurate calculations, I just need to look at the actual width and height properties in CSS, without having to also refer to padding and border.
Minor issue: You sometimes use disc, and sometimes disk in your code. Try to standardise this for sanity's sake.
TL;DR
Here's the fiddle implementing 1, 2 and 4: https://jsfiddle.net/hrnn9w9k/4/
I didn't include 3 as it might not be your preference, but I strongly recommend it.

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();

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);

EaselJS BitmapAnimation.gotoAndPlay lag

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;
});

Categories