I can't push items into jasvacript array quick enough - javascript

I have a jQuery UI slider that when a user triggers a slide function, I need to push the current slide value into an array. The problem is, when a user slides the handle too quickly, the length of the array and the current index value fall out of sync. As if the array cant update as quick as the user can slide...
Is there a solution for this?
Thanks.
$( "#slider" ).slider({
max: gCoordsArray.length,
min: 0,
step: 1,
slide: function(event, ui){
var direction = '';
// test whether we are sliding left or right ( increasing / decreasing increment )
if (ui.value > count) direction = 'right';
if (ui.value < count) direction = 'left';
count = ui.value;
if (direction == 'right') {
test.push(count);
console.log(test.length); // Should match count
console.log(count); // Should match length
}
if (direction === 'left') {
test.pop();
console.log(test.length); // Should match count
console.log(count); // Should match length
}
}, 1)
});

test.length and count=ui.value won't be the same except in the edge-case that the user only moves one step at a time.
The slide event returns the current mouse position, if the user skips straight to the end, ui.value will be gCoordsArray.length while test.length == 0
One solution might be to record in a "global" var (outside the event handler) the previous position and compare that, eg:
var prevPosition = 0;
$( "#slider" ).slider({
max: gCoordsArray.length,
min: 0,
step: 1,
slide: function(event, ui) {
var thisPosition = ui.value;
if (thisPosition > prevPosition) direction = 'right';
if (thisPosition < prevPosition) direction = 'left';
if (direction == 'right')
{
while (prevPosition != thisPosition)
{
prevPosition++;
test.push(prevPosition);
}
}
... equivalent for left
}, 1)
});

Related

Matching iterating values in js when iterating with a higher step count than 1

How do I match values when I use a different iterating step count other than 1?
Please see the example below. I am interating by 5 since 5 is the speed.
But, how do I stop once I come close to 53. How do I detect and stop start from going way past 53 and make it 53?
var start=0;
var value_to_reach=53;
var increment_speed=5;
while(true) {
if(start>value_to_reach)
{
start-=increment_speed
} else { start+=increment_speed }
if (start==value_to_reach) {
console.log("reached :" + value_to_reach); //Obviously this will never happen with the increment being +5."
}
if (start>54)
{
console.log("Let's break this loop for the sake of stopping this infinite loop. But we couldn't achieve what we want. Not reached " + value_to_reach);
break;
}
}
How to detect if it's close to 53 or not
You can get total steps by this:
var totalRepeation = Math.ceil(value_to_reach / increment_speed);
And so you can create counter that increment one by one and check to see last step.
You can try this one:
var start = 0;
var value_to_reach = 53;
var increment_speed = 5;
let totalRepeation = Math.ceil(value_to_reach / increment_speed);
let i = 0;
while (true) {
if (start > value_to_reach) {
start -= increment_speed
} else { start += increment_speed; i++ }
if (i + 1 >= totalRepeation) {
console.log("you are close to 53. the start numbre is: ", start)
break;
}
if (start == value_to_reach) {
console.log("reached :" + value_to_reach); //Obviously this will never happen with the increment being +5."
}
if (start > 54) {
console.log("Let's break this loop for the sake of stopping this infinite loop. But we couldn't achieve what we want. Not reached " + value_to_reach);
break;
}
}
To lower the steps if we're getting close to target
In the while (start <= reach) loop we can:
Check if the next iterator is above reach
If so, lower the iterator
Otherwise, keep regular step size
Increment counter
let start = 0,
steps = 5,
reach = 53,
debug = 0;
while (start <= reach) {
console.log(`Step: ${steps}, current: ${start}`);
// If next iteration will be above reach
if ((start + steps) >= reach) {
// Set steps to the diff
steps = (start + steps) - reach;
}
// Increment
start += steps;
}
This will output:
Step: 5, current: 0
Step: 5, current: 5
Step: 5, current: 10
Step: 5, current: 15
Step: 5, current: 20
Step: 5, current: 25
Step: 5, current: 30
Step: 5, current: 35
Step: 5, current: 40
Step: 5, current: 45
Step: 5, current: 50
Step: 2, current: 52
Step: 1, current: 53
If you want to go above reach, and then go back to target, you can use something like:
let start = 0,
steps = 5,
reach = 53,
debug = 0;
do {
console.log(`Step: ${steps}, current: ${start}`);
// If we're above the target
if (start > reach) {
// Set steps to negative
steps = -1;
}
// Increment
start += steps;
console.log(`Step: ${steps}, current: ${start}`);
} while (start !== reach)

Counter with thousands separator

So I have used this counter to count to a specific number :
(function($) {
$.fn.countTo = function(options) {
// merge the default plugin settings with the custom options
options = $.extend({}, $.fn.countTo.defaults, options || {});
// how many times to update the value, and how much to increment the value on each update
var loops = Math.ceil(options.speed / options.refreshInterval),
increment = (options.to - options.from) / loops;
return $(this).each(function() {
var _this = this,
loopCount = 0,
value = options.from,
interval = setInterval(updateTimer, options.refreshInterval);
function updateTimer() {
value += increment;
loopCount++;
$(_this).html(value.toFixed(options.decimals));
if (typeof(options.onUpdate) == 'function') {
options.onUpdate.call(_this, value);
}
if (loopCount >= loops) {
clearInterval(interval);
value = options.to;
if (typeof(options.onComplete) == 'function') {
options.onComplete.call(_this, value);
}
}
}
});
};
$.fn.countTo.defaults = {
from: 0, // the number the element should start at
to: 100, // the number the element should end at
speed: 1000, // how long it should take to count between the target numbers
refreshInterval: 100, // how often the element should be updated
decimals: 0, // the number of decimal places to show
onUpdate: null, // callback method for every time the element is updated,
onComplete: null, // callback method for when the element finishes updating
};
})(jQuery);
jQuery(function($) {
$('.timer').countTo({
from: 50,
to: 2500,
speed: 5000,
refreshInterval: 50,
onComplete: function(value) {
console.debug(this);
}
});
});
http://jsfiddle.net/YWn9t/
But unfortunately there are no separators (dots) between every third numbers.
To sum up: How to make the output of the script from e.g. 1000000 to 1.000.000?
I recommend using numberFormatter.
console.log(new Intl.NumberFormat().format(123456));

Trigger Alert When Slider Moves Left A Certain Amount

So, I am going to actually kill two birds with one stone. First off, I am trying to trigger an alert once the slider moves left to a specific degree. For example, once it goes to, let’s say, the 3rd slide I would like for it to trigger an alert. Also, I need help with the cycling of the slider. I want the slider to cycle like a rotation (360), but instead at the end of the cycle it slides all the way back to the start. View the Codepen to have a better understanding of what I mean. Thank you for your time. Your help is much appreciated.
Live Codepen
var W = $('#image_rotator').width(),
N = $('#slider .slide').length,
C = 0,
intv;
if (N <= 1) $('#left, #right').hide();
$('#slider').width(W * N);
$('#left, #right').click(function() {
C = (this.id == 'right' ? ++C : --C) < 0 ? N - 1 : C % N;
$('#slider').stop().animate({
left: -C * W
}, 300);
});
function setResetInterval(e) {
var intv = $("#slider");
if (e) {
timer = setInterval(function() {
$('#right').click();
}, 1000);
} else {
clearInterval(timer);
}
$('#slider').click(function() {
var x = $('#slider').offset();
alert("Top: " + x.top + " Left: " + x.left);
});
}
$("#btn").click(function(e) {
e.preventDefault();
setResetInterval(true);
});
$("#btn2").click(function(e) {
e.preventDefault();
setResetInterval(false);
});
$('#slider').hover(function(e) {
return e.type == 'mouseenter' ? setResetInterval(false) : setResetInterval(true);
});
This accomplishes both of the things that you wanted using the optional callback parameter in the animate function. It checks first to see if the variable C (the 0-based index of the current slide) is equal to 2 (3 in 1-based indexing), and shows an alert if it is. Then it checks to see if C is equal to 10 (which would mean that the slide currently being shown is the 9th one; The 9th image is just a duplicate of the 1st one) If it is on the 9th one, then jump back to the first one and trigger $("#right").click();.
$('#left, #right').click(function() {
C = (this.id == 'right' ? ++C : --C) < 0 ? N - 1 : C % N;
$('#slider').stop().animate({
left: -C * W
}, 300, function() {
if (C == 2){alert("3rd");}
if (C == 10) {
$('#slider').css("left", "0");
$('#right').click();
}
});
});
JSFiddle (Because CodePen's layout is weird to me)

How to stop jQuery sliders (bars) when range of points is emptied

I have a problem with jQuery-UI sliders ( http://jqueryui.com/demos/slider/#default ).
I've created a couple of them on my website, and there is DIV with "points" (I entered there 1500). Each slider after increasing takes points from the DIV, so - when I increase one slider from 0 to 100, there is only 1400 points in DIV left. When I increase another slider, there will be less points in the DIV. If I decrease one of the sliders, these points will be added to the DIV - this works great.
But... I need to disable increasing value of the sliders after points in DIV gains 0 (there can't be values less than 0), decreasing must be still available (so .slider({ disabled: true }) is not an option). I've tried almost everything, and nothing works...
JS code:
$("#addMapPage").ready(function() {
$("#addMap").click(function(){ checkMap() });
$(".vChanger").slider({ step: 10,
max: 1500,
slide: function(event, ui) { checkValues() },
stop: function(event, ui) { checkValues() }
});
checkValues();
});
function getValues() {
var data = new Array();
data['water'] = $("#water").slider("value");
data['steppe'] = $("#steppe").slider("value");
data['swamp'] = $("#swamp").slider("value");
data['desert'] = $("#desert").slider("value");
data['forest'] = $("#forest").slider("value");
data['mountain'] = $("#mountain").slider("value");
data['hill'] = $("#hill").slider("value");
return data;
}
function checkValues() {
var slidersValues = getValues(); //getting slider values
var sum = 0;
var pula = parseInt($("#pula").text()); //points to use (now can be less than 0)
if (pula < 0){
$(".vChanger").slider({ range: pula });
}
for (value in slidersValues) {
sum += slidersValues[value];
$("#"+value+"Info").text(slidersValues[value]);
}
var pula = 1500-sum;
$("#pula").text(pula);
}
HTML:
<div id="addMapPage">
<ul>
<li><span>Woda:</span><div class="vChanger" id="water"></div><span id="waterInfo"><span/></li>
<li><span>Step:</span><div class="vChanger" id="steppe"></div><span id="steppeInfo"><span/></li>
<li><span>Bagno:</span><div class="vChanger" id="swamp"></div><span id="swampInfo"><span/></li>
<li><span>Pustynia:</span><div class="vChanger" id="desert"></div><span id="desertInfo"><span/></li>
<li><span>Las:</span><div class="vChanger" id="forest"></div><span id="forestInfo"><span/></li>
<li><span>Góry:</span><div class="vChanger" id="mountain"></div><span id="mountainInfo"><span/></li>
<li><span>Wzgórza:</span><div class="vChanger" id="hill"></div><span id="hillInfo"><span/></li>
<li><span>Nazwa mapy:</span><input type="text" id="mapName"></input></li>
</ul>
<p id="mapInfo"><span id="pula"></span>Points to use</p>
<input type="button" id="addMap" value="create map"></INPUT>
</div>
Here is example (3 screenshots):
Any ideas, how to limit points (don't let them to be less than 0)?
You'll find that adjusting the max values of your sliders is probably not a very desirable solution. It will change the scale of the increments on your slider and not be very representative of what portion of the whole the current slider value represents. Instead, intercept and prevent the slide event if the current sum of all other sliders plus the value for this event exceed the max. For instance:
var maxSum = 1500;
var $sliders = $(".slider").slider({
value: 0,
min: 0,
max: 1500,
step: 10,
slide: function(event, ui) {
var sum = 0;
// Don't add the value for this slider;
// We need to add the new value represented by the slide event
$(".slider").not(this).each(function() {
sum += $(this).slider("value");
});
// Add the slide event value
sum += ui.value;
if (sum > maxSum) event.preventDefault();
else $(this).next().html("Value: " + ui.value);
}
});
The only thing this is prone to is not pushing the values all the way to the max if the user is sliding very rapidly by large increments. See a working demo here.
You're already storing the available points in "pula".
Whenever a user adjusts a slider, all the remaining sliders need to be changed so that the user can't allocate more than the remaining points. You do this by setting the .slider() MAX option to the current value of the slider + the remaining available points.
for (value in slidersValues) {
var currentValue = slidersValues[value];
var newMax;
//If max + available is > 1500, we set it to 1500,
//otherwise we add the available points to the current values
if ( currentValue + pula >= 1500) {
newMax = 1500;
} else {
newMax = currentValue + pula;
}
//jQuery allows you to reset the 'max' value for a slider
$('#'+value).slider( "option" , max , newMax );
}

Implementing jQuery's shake effect with animate

I've been given a cut down subset of the jQuery lib one of the key features I'm missing is the .effect functions. I do however have .animate. I was wondering if anyone would have any ideas how I could go about reproducing the animation functions.
I am particularly consious of making this only a few lines as I need to keep the code size down. Which is why the jquery lib is as small as it is and doesnt have the effects functions.
TLDR - I'm trying to replace
$("#"+id_string).effect( "shake", {}, "fast" );
With something using .animate within jQuery.
So far I have something like this ..
jQuery.fn.shake = function(intShakes, intDistance, intDuration) {
this.each(function() {
$(this).css("position","relative");
for (var x=1; x<=intShakes; x++) {
$(this).animate({left:(intDistance*-1)}, (((intDuration/intShakes)/4)))
.animate({left:intDistance}, ((intDuration/intShakes)/2))
.animate({left:0}, (((intDuration/intShakes)/4)));
}
});
return this;
};
I like #phpslightly solution so much, I keep using it. So here it is updated to basic jquery plugin form which will return your element
jQuery.fn.shake = function(interval,distance,times){
interval = typeof interval == "undefined" ? 100 : interval;
distance = typeof distance == "undefined" ? 10 : distance;
times = typeof times == "undefined" ? 3 : times;
var jTarget = $(this);
jTarget.css('position','relative');
for(var iter=0;iter<(times+1);iter++){
jTarget.animate({ left: ((iter%2==0 ? distance : distance*-1))}, interval);
}
return jTarget.animate({ left: 0},interval);
}
You would then use it like a regular plugin:
$("#your-element").shake(100,10,3);
Or use the default values (100, 10, 3):
$("#your-element").shake();
It's actually already implemented this way under the covers, you can see exactly how in jquery.effects.shake.js, if you wanted to copy only that functionality you can.
Another approach to think about: if you're using multiple effects, I'd recommend downloading jQuery UI with only the effects you want. For this effect, without copying the functionality yourself, you would just need jquery.effects.core.js and jquery.effects.shake.js.
This is probably irrelevant now but I've ported jQ UI's shake effect as a standalone jQuery plugin. All you need is jQuery and it will work exactly like the one provided in jQ UI.
For those who want to use the effect without actually bloating their project with unnecessary jQ UI core files.
$('#element').shake({...});
It can be found here with instruction: https://github.com/ninty9notout/jquery-shake
Thought I'd leave this here for future reference.
This is a more clean and smooth way to do the animation.
jQuery.fn.shake = function(shakes, distance, duration) {
if(shakes > 0) {
this.each(function() {
var $el = $(this);
var left = $el.css('left');
$el.animate({left: "-=" + distance}, duration, function(){
$el.animate({left: "+=" + distance * 2}, duration, function() {
$el.animate({left: left}, duration, function() {
$el.shake(shakes-1, distance, duration); });});
});
});
}
return this;
};
I don't understand all the complexity being thrown into reproducing the shake effect with solely animate. Here's my solution in just a couple lines.
function shake(div,interval=100,distance=10,times=4){
$(div).css('position','relative');
for(var iter=0;iter<(times+1);iter++){
$(div).animate({ left: ((iter%2==0 ? distance : distance*-1))}, interval);
}//for
$(div).animate({ left: 0},interval);
}//shake
EDIT: Updated code to return element to original position. Still believe this is the lightest and best solution to the problem.
I wrote some time ago a few simple jquery animations:
https://github.com/yckart/jquery-custom-animations
/**
* #param {number} times - The number of shakes
* #param {number} duration - The speed amount
* #param {string} easing - The easing method
* #param {function} complete - A callback function
*/
jQuery.fn.shake =
jQuery.fn.wiggle = function (times, duration, easing, complete) {
var self = this;
if (times > 0) {
this.animate({
marginLeft: times-- % 2 === 0 ? -15 : 15
}, duration, easing, function () {
self.wiggle(times, duration, easing, complete);
});
} else {
this.animate({
marginLeft: 0
}, duration, easing, function () {
if (jQuery.isFunction(complete)) {
complete();
}
});
}
return this;
};
This is not perfect, but functional
// Example: $('#<% =ButtonTest.ClientID %>').myshake(3, 120, 3, false);
jQuery.fn.myshake = function (steps, duration, amount, vertical) {
var s = steps || 3;
var d = duration || 120;
var a = amount || 3;
var v = vertical || false;
this.css('position', 'relative');
var cur = parseInt(this.css(v ? "top" : "left"), 10);
if (isNaN(cur))
cur = 0;
var ds = d / s;
if (v) {
for (i = 0; i < s; i++)
this.animate({ "top": cur + a + "px" }, ds).animate({ "top": cur - a + "px" }, ds);
this.animate({ "top": cur }, 20);
}
else {
for (i = 0; i < s; i++)
this.animate({ "left": cur + a }, ds).animate({ "left": cur - a + "px" }, ds);
this.animate({ "left": cur }, 20);
}
return this;
}
Based on #el producer solution, I added some multiply logic and make it look like a random shake.
jQuery.fn.shake = function (interval, distance, times) {
interval = typeof interval == "undefined" ? 100 : interval;
distance = typeof distance == "undefined" ? 10 : distance;
times = typeof times == "undefined" ? 3 : times;
var jTarget = $(this);
jTarget.css('position', 'relative');
for (var iter = 0; iter < (times + 1) ; iter++) {
jTarget.animate({ top: ((iter % 2 == 0 ? distance * Math.random() : distance * Math.random() * -1)), left: ((iter % 2 == 0 ? distance * Math.random() : distance * Math.random() * -1)) }, interval);
}
return jTarget.animate({ top: 0 , left: 0 }, interval);
}
Position had to be absolute on my side, so I changed it to:
jQuery.fn.shake = function(interval, distance, times) {
interval = typeof interval == "undefined" ? 100 : interval;
distance = typeof distance == "undefined" ? 10 : distance;
times = typeof times == "undefined" ? 3 : times;
var jTarget = $(this);
for (var iter=0;iter<(times+1);iter++) {
jTarget.animate({ 'padding-left': ((iter%2==0 ? distance : distance*-1))}, interval);
}
return jTarget.animate({ 'padding-left': 0 } ,interval);
}

Categories