Run function every time animation repeats - gsap

Here's a script that animates a bouncing square:
http://jsfiddle.net/YH9nM/18/
var count = 1,
tM = new TimelineLite(),
element = $('#boxy');
function log(){
console.log('just bounced');
element.html('I\'ve bounced: ' + count + ' times!');
count++;
}
tM.from( element, 2, { top:'-=60px', ease: Linear.easeNone, repeat: -1, onRepeat: log() }, 0 );
However, that onRepeat option is not behaving as I'd expect. Rather than triggering the log function every time the animation repeats, it runs it once when the animation starts the first time.
It's behaving exactly as the onStart option would. Why is this happening? How can I make the div count how many times it's bounced ad infinitum?

You're running the log function when you define your tween, by using log(), and are assigning the return value of log to onRepeat (which is undefined since you don't return anything). You want the onRepeat var to be assigned to the log function
change
onRepeat: log()
to
onRepeat: log
You can see this in action here.

Patrick did actually answer the question, but here's what you want to do if you wish to pass parameters to a function you wish to run onRepeat:
onRepeatParams: ['as','many','parameters','as',1,true,'ly','wants'];
I also believe the most efficient way to access the element the timeline is applied to is through using the self keyword like this:
onRepeatParams: ["{self}"];
and then in the function doing this:
$(element.target).html('I\'ve bounced: ' + count + ' times!');
Here's what I mean in context:
var count = 1,
tM = new TimelineLite(),
element = $('#boxy');
function log(element){
console.log('just bounced');
$(element.target).html('I\'ve bounced: ' + count + ' times!');
count++;
}
tM.from( element, 2, { top:'-=60px', ease: Linear.easeNone, repeat: -1, onRepeat: log, onRepeatParams: ["{self}"] }, 0 );
http://jsfiddle.net/YH9nM/23/

Related

Need a counter column for a datatable with server side processing

I need to add one extra column to the datatable. This column will have incrementing serial no like 1, 2, 3, 4 etc...
I found this example, but this is not working for server side processing and I want searching-sorting working(if possible) which is also not here.
https://datatables.net/examples/api/counter_columns.html
Notes:
1) Datatable uses server side processing.
2) Sorting and searching should work on that counter column. (if possible)
3) Would be good, if I can achieve it completely on the client side using js. I don't want to make any code at server side for this(if possible).
4) Pagination should update counter no serially means if the previous page has last counter no 15, then next page should start with counter 16.
By the way I also checked this:
"Column Index" on a server-side processed DataTable
But accepted answer of this question violates my 4th requirement.
Any help would be appreciated.
Thanks,
Parth Vora
If anyone still has this problem, I solved it with draw event listener and DataTable's page.info() method. My code:
table.on('draw.dt', function () {
var info = table.page.info();
table.column(0, { search: 'applied', order: 'applied', page: 'applied' }).nodes().each(function (cell, i) {
cell.innerHTML = i + 1 + info.start;
});
});
table.on('draw.dt', function () {
var info = table.page.info();
table.column(0, { search: 'applied', order: 'applied', page: 'applied' }).nodes().each(function (cell, i) {
cell.innerHTML = i + 1 + info.start;
});
});
IT WILL WORK FINE "Thank You Brother Your Code usefull..Mansur Anorboev "

Vue Object isn't returning updated value despite seeing the value updated

I'm writing tests for Vue.js and I'm trying to write the test to ensure that when some props are changed for pagination, the resulting values within the component are updated properly.
So when I console.log the component, I see the correctly updated values, but then when I try to literally grab that attribute it gives me the old and outdated value. Look at rangeList in the following screenshot to see what I mean:
Here is my code so that you see how what is generating this output.
pagComp.$refs.component.limit = 10;
pagComp.$refs.component.pages = 145;
console.log(pagComp.$refs.component);
console.log('RangList: ' + pagComp.$refs.component.rangeList.length);
Here is the code that modifies rangeList:
createListOfRanges() {
let range = [];
for(let i = 0; i < this.pages; i++) {
range.push(i);
}
this.rangeList = [];
while(range.length > 0) {
this.rangeList.push(range.splice(0, this.rangeLength));
}
this.correctLastRange();
},
Finally, there are two places this function is called: when the component is being created, and when the pages attribute changes. Here is the watcher:
watch: {
pages(val) {
this.createListOfRanges();
}
},
I see some issues with this part of your code:
while(range.length > 0) {
this.rangeList.push(range.splice(0, this.rangeLength));
}
range.splice(..) returns an array, which is getting pushed into this.rangeList
Forget about that for a minute. Look at the following example:
x = [1, 2, 3, 4]
x.splice(0, 2) // result: [1, 2]
As you can see above, splice returns an array, not an element. Now, in the same example above:
x = [1, 2, 3, 4]
y = [10, 11]
y.push(x.splice(0, 2))
Check the value of y. It will be [10, 11, [1, 2] ]. It is an array within another array. It does not look very meaningful here. You can do the above x and y experiments directly in your developer console.
In your case, your x happens to be the local range array within createListOfRanges method, and your y is this.rangeList that belongs to your Vue component.
Can you check your app logic at around that area, and see if that is really what you want to do?
For debugging Vue.js apps, you also need Vue devtools: https://github.com/vuejs/vue-devtools - it is much better than console.log()
While #Mani is right on the line of code giving you issues is your push to rangeList.
createListOfRanges() {
let range = [];
for(let i = 0; i < this.pages; i++) {
range.push(i);
}
this.rangeList = [];
while(range.length > 0) {
this.rangeList.push(range.splice(0, this.rangeLength));
}
this.correctLastRange();
},
pushing the result of the splice just makes a single element with all the elements of range in it.
try changing it to
this.rangeList.push(range.shift());
Though your function could be simplified by pushing the for(let i = 0; i < this.pages; i++) { i value directly to rangeList unless that's a simplification.
This JSFiddle shows what I'm talking about.
I appreciate the answers above, however they aren't what the issue was.
The problem was with Vue's lifecycle. I'm not 100% sure why, but when the page and limit variables are changed it takes another moment for the page watcher (shown above) to get executed and update the component. So thus it wouldn't show up in my tests. So what I did was use nextTick like so, which fixed the problem.
pagVue.limit = 10; // limit and pages change together every time automatically
pagVue.pages = 145;
Vue.nextTick(() => {
expect(pagination.rangeList.length).toBe(25);
})

Vue.js: error setting a computed property

in the following code (jsbin available here) I have two input elements, a range and a text, bound together via a computed property.
var vm = new Vue({
el: '#main-container',
data: {
sliderValue: 100,
},
computed: {
actualValue: {
get: function() {
if (this.sliderValue <= 100) {
return this.sliderValue;
} else {
return Math.round(this.sliderValue * 12.5 - 1150);
}
},
/* set won't work for val > 100*/
set: function(val) {
if (val <= 100) {
this.sliderValue = val;
} else {
this.sliderValue = Math.round((val + 1150)/12.5);
}
}
}
},
methods: {
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<div id="main-container">
<input type="range" v-model="sliderValue" min=1 max=132>
<input type="text" v-model="actualValue">
<p>Slider: {{sliderValue}}</p>
<p>Actual: {{actualValue}}</p>
</div>
The range goes from 1 to 132, and its range is mapped [1..500] in the text input, with a simple transformation (basically it's a linear mapping with two different slopes for [1..100] and [101..132]) using the actualValue computed property.
Getting actualValue works as expected: dragging the slider correctly updates the input text with appropriate values in the range [1..500].
I'm not able to find a way to set actualValue, though. I'd like to be able to type a value in the text input, and make the slider's thumb update accordingly to the inverse transformation (val + 1150)/12.5.
It works as long as the typed number is in the range [1..100], but it "explodes" for numbers >100, e.g. 101 makes the sliderValue jump at 80892 and actualValue is then re-calculated as 1010000. As far as I understand, it's a looping-feedback scenario.
I've tried also alternative approaches (watch; v-on:change in the text input; using a third variable) to no avail.
Thanks in advance for any suggestion!
It's an amazing puzzle, and challenged me for a long time!
Look at the screenshot below. Your sliderValue and actualValue are strings, not integers. When you set actualValue as 101, you are actually setting it as a string value of "101"
Now, your sliderValue = ((actualValue + 1150)/12.5)
"101" + 1150 = "1011150" (another string!, try it in the developer console)
That messes up your entire calculation. Now you know how to fix it :-)
And you need to get that Vue devtools from here: https://github.com/vuejs/vue-devtools
EDIT: Response to comment #3
Here is the modified jsBin: http://jsbin.com/mahejozeye/1/edit?html,js,output
The only difference is introduction of two console.log statements in your map2 function. This helps you identify if your non-linear mapping function is working correctly or not. If you keep your developer console open, you will see what is happening in this function.
Case 1: When you set the value radius = 25 using the text box, your sliderRadius gets set to 111.55518394648828 and your radius again gets re-calculated as 25. So it comes around in a full circle and everything is stable here.
Case 2: When you set the value radius = 55, your sliderRadius gets set to 173.03607214428857 through your non-linear map2() function, which resets radius to 51.29869180420927
Clearly there is a circular dependency issue. Your sliderRadius and radius are very much dependent on each other and therefore radius is unable to take the value between 51 and 58.
You need to evaluate why it happens, as it has a lot to do with the non-linear mapping function that you have defined. The moment radius can take stable values at 55 (through the map2 function), then your current problem will be resolved.
The simplest fix is to set your input type to number:
<input type="number" v-model="actualValue">
or you can convert your value to an integer with something like:
set: function(val) {
var intVal = parseInt(val, 10);
if (!isNaN(intVal)) {
if (intVal <= 100) {
this.sliderValue = Math.max(1, intVal);
} else {
this.sliderValue = Math.min(132, Math.round((intVal + 1150) / 12.5));
}
}
}

on click events attached to domconstruct.placed nodes being handled by the wrong handler in dojo

consider the following:
gridID = datagridID;
//column headers
domConstruct.place("<div class=\"gridheaderrow\" data-type =\"BolingerGridHeaderRow\" ></div>", gridID, "first");
var node = query("div[data-type=\"BolingerGridRow\"]", gridID);
var headerNode = query("div[data-type=\"BolingerGridHeaderRow\"]", gridID);
var cells = query("div[data-type=\"BolingerGridCell\"]", node[0]);
for (var i = 0; i < cells.length; i++)
{
var columnname;
columnname = attr.get(cells[i], "data-columnname");
var headernode = domConstruct.place("<div class=\"gridheadercell\" data-type=\"BolingerGridHeaderCell\">" + columnname + "</div>", headerNode[0], "last");
var sortup = domConstruct.place("<div id=column'" + i + "' data-columnupid = '" + i + "' data-type='sortuparrow' style='display:inline; cursor:pointer'>&#x25B2</div>", headernode, "last");
var sortdown = domConstruct.place("<div id=column'" + i + "' data-columndownid = '" + i + "' data-type='sortdownarrow' style='display:inline; cursor:pointer'>&#x25BC</div>", headernode, "last");
}
for (var i = 0; i < cells.length; i++)
{
var sortupnode = query("[data-columnupid = '" + i + "']", gridID)[0];
var sortdownnode = query("[data-columndownid = '" + i + "']", gridID)[0];
on(sortupnode, "click", function (e) {
var num = attr.get(sortupnode, "data-columnupid");
sort(true, num);
});
on(sortdownnode, "click", function (e) {
var num = attr.get(sortdownnode, "data-columndownid");
sort(false, num);
});
}
This code places little up and down arrows above each column and attaches on click events to them, which calls the sort function. I'm quite sure I'm attaching the events each up or down arrow once. Yet, no matter what arrow I click on the handler that handles it belongs to the arrow of the last column. Why is this? I'm figuring it has something to do with attaching handlers to nodes I just placed. Thoughts?
Variables do not have block scope in JavaScript. You are expecting that each iteration through your second for loop has its own sortupnode and sortdownnode variables, but in fact each time through the loop, the same variable is being redeclared and its value is being replaced. Your on handlers are continuing to reference the same sortupnode and sortdownnode variables, which by the time they run, will always reference the very last nodes iterated.
In this case the absolute simplest fix would likely be to replace sortupnode and sortdownnode inside your event handlers with this, which should reference the element that the handler fired for. However, you should be able to avoid this issue completely and hook up these event handlers much more efficiently using event delegation. Something along the lines of:
on(document.getElementById(gridID), '[data-columnupid]:click', function (event) {
// Inside delegated event handlers registered with dojo/on,
// `this` references the element that matched the selector
var num = this.getAttribute('data-columnupid');
sort(true, num);
});
Addendum
In response to your second comment: the problem you are facing has no direct correlation to event handling; it is purely related to how scope works in JavaScript.
To attempt to better illustrate how the variables in your loops are actually working, bear in mind that this:
for (var i = 0; i < ...; i++) {
var foo = ...;
...
}
... is essentially equivalent to this, because JavaScript variables do not have block scope:
var i;
var foo;
for (i = 0; i < ...; i++) {
foo = ...;
...
}
That is to say, the variable foo exists in the scope of the surrounding function, not the for loop. The same foo variable has its value modified each time through the loop.
Any code that looks at foo after the loop finishes running will see the last value foo was assigned in the loop. You are defining event handler callbacks in each iteration through your loop which have access to foo from the containing function's scope, but those callbacks are only actually called way later when the user performs an action. "Way later" = after the loop finished running = foo is always going to be the value it was set to during the last iteration.

Rally Analytics set the startindex for a query

I have a query for the Rally Analytics which returns a data set larger than the pagesize. So I want to do another query to return the remainder data set. I tried setting a startindex value but that does not work, StartIndex stays at 0.
this.query = {
find:Ext.encode(requestedQuery.find),
StartIndex:20000,
pagesize:20000 //MAX_PAGESIZE
};
_queryAnalyticsApi:function () {
Ext.Ajax.request({
url:"https://rally1.rallydev.com/analytics/1.27/" + this.workspace + "/artifact/snapshot/query.js?" + Ext.Object.toQueryString(this.query) +
"&fields=" + JSON.stringify(this.requestedFields) + "&sort={_ValidFrom:1}",
method:"GET",
//need to change this to a POST
success:function (response) {
this._afterQueryReturned(JSON.parse(response.responseText));
},
scope:this
});
},
that works, it was confusing because the attribute of the result set is called StartIndex. It would be nice if the granularity (i.e. day, week) could be defined and handled on the server first, so it wouldn't have to return such a large dataset.
The parameter you'll want to use is called start. Also, on subsequent pages it is important to include a filter using the ETLDate returned from the first page of data so your results are consistent in time. We have created a SnapshotStore in the AppSDK 2.0 that handles all this complexity for you. Look for it soon!