I know the typical way for doing animations in Vue.js, adding the transition="my-animation" on the HTML element.
I'm wondering if can I call explicitly that animation form code and not just depending on v-show / v-if.
I'd like to "shake" a component (A) each time another one (B) is clicked. For that I use a pulse transition (from animated.css).
For the moment B dispatches a message each time is clicked.
A receives the message and sets it's property animate to true. Then A shakes thanks to, in the HTML:
<div id="A" class="animated"
transition="pulse"
v-show="show"
v-bind:class="{ 'pulse': animate }"
>
Onces it's being animated, won't do it anymore, since A's animateprop is already set to true. I'd need to reset it to false so on next click the component could shake again.
I've tried with the transition hooks:
Vue.transition('pulse', {
afterLeave: function (el) {
this.animate = false;
},
})
It won't animate (shake) again since the element already has the class. You need to remove it first.
There are probably a couple of ways to accomplish this, using setTimeout to set animate to false is a very easy approach.
Using Vue's class binding (ex. :class="{'bounce animated': animated}"), run the animation by setting the animated property to true, then remove the class the animation by setting it back to false after the amount of time it takes to finish.
Here is a fiddle with exactly what you want to do, using setTimeout to set animate back to false after a 1s animation.
https://jsfiddle.net/crabbly/xcLrbtzj/
Related
I'm using Waypoints and their Sticky shortcut to 'stick' an element with the id stick-this to the top of the viewport once it gets scrolled past. I am having some difficulty positioning the element past another fixed element on the screen, however.
There is a <div> with a class .header which always remains fixed. I am trying to position the top of the new element to the height() of the .header element so they are 'stacked' on top of one another and both visible. This is the code I am using to accomplish this:
var sticky = new Waypoint.Sticky({
element: $('#stick-this')[0],
handler: function() {
$(".stuck").css({ "top" : $(".header").height() });
}
})
So, essentially, once the #stick-this is scrolled past, it becomes sticky with a position:fixed class and the top is dynamically determined by the height() of .header.
This works great until I scroll back up, and the top style is still applied to this element, in spite of the stuck class not being applied anymore.
So when I scroll past, the element ends up like this
<div id="stick-this" class="stuck" style="top:70px /*or whatever the height() ends up being */">
and when I scroll back up the element ends up like this with the top property still in place
<div id="stick-this" class="" style="top:70px /*I need this back to 0px */">
Is it possible to have a function called when the "sticky" is removed, so that the inline style property can be set to top:0px or something like that?
For anyone else struggling with this, I ended up dynamically writing the CSS when the sticky element's class is initiated and inserting it into the head:
var sticky = new Waypoint.Sticky({
element: $('#stick-this')[0],
offset: $('.header').outerHeight(true),
handler: function(direction) {
$("<style>")
.prop("type", "text/css")
.html("\
.stuck {\
position: fixed;\
top:" + $(".header").height() + "px;\
}")
.appendTo("head");
}
})
so, the class will be added with the correct top positioning, and once the class is removed, the top property is inherently returned back to 0px.
It's important to have the \ after each line break in the .html() portion of this code in order for it to work.
This question builds on the (correct) answer provided to this. I simply haven't been able to get any further..
With the help of an interpolator function, d3.js's tween allows smooth graphical transition between existing and new (ie to be set) DOM element values. At the simplest level, for a given animation we have a target element, an start state, an end state, a transition, a tween function and an interpolator.
Now, say I want every so often to programmatically update the contents of an input (text field) element. The value to be entered is non-interpolable (either the text is submitted, or it is not. There is no in-between state). In providing a closure (allowing for text retrieval at the scheduled transition time), tween would seem to be a good vehicle for the updates. Either I replace the interpolator with a fixed value, ensure the start and end values are identical, or find some other way of forcing it to fire at t=1. That's the theory..
To this end, in my case each property (not value) is modified in it's own update call, into which are passed transition, element index and parent element selection.
First cut:
an outer, 'governing' transition with delay values staggered using a multiple of the current element's index
playback_transition = d3.transition()
.delay(function(d, i, j) {
return (time_interval * i);
})
.duration(function() {
return 1; // the minimum
});
within a call to playback_transition.each() pass the transition as a parameter to a dependent animation by means of an update() interface
within this dependent animation, apply the transition and tween to the current element(s):
input // a UI dialog element
.transition()
.tween(i.toString(), setChordname( waveplot.chordname ));
Where:
function setChordname(newValue) {
return function() {
var i = newValue; // a string
return function(t) {
this.value = i;
inputChanged.call(this);
};
};
};
and
function inputChanged() {
if (!this.value) return;
try {
var chord = chordify.chordObjFromChordName(this.value);
purge(); // rid display of superceded elements
plotChord(chord, options); // calculate & draw chord using new input property
} catch (e) {
console.log(e.toString());
}
}
PROBLEM
While setChordname always fires (each chord is in turn correctly found and it's value stored), of the scheduled returned functions, only the first fires and results in display of the associated waveform. For all subsequent return function occurrences, it is as if they had never been scheduled.
From the display:
direct user update to the input field still works fine
only the first of setChordname's return functions fire, but, for this initial chord, carries right through, correctly displaying the cluster of associated chord and note waves.
From this, we can say that the problem has nothing to do with the integrity of the waveplotting functions.
From the console
transitions are accumulating correctly.
chord supply is all good
associated (ie initial) tween fires at t=1. (specifically, tween appears to accept omission of an interpolator function).
looking at the output of transition.toSource(), though the associated outer index increases by single figure leaps, tween itself is always paired with an empty pair of curly brackets.
transition = [[{__transition__:{8:{tween:{}, time:1407355314749, eas..
For the moment, apart from this and the initial execution, the tween factory return function behaviour is a mystery.
From Experiment
Neither of the following have any impact:
Extending the period before the initial transition takes effect
Extending (by a multiple) each staggered transition delay
Furthermore
the same transition configuration used in a different scenario works fine.
These seem to eliminate timing issues as a possible cause, leaving the focus more on the integrity of the tween setup, or conditions surrounding waveplot append/remove.
Afraid it might be interfering with input property text submission via the tween, I also tried disabling a parallel event listener (listening for 'change' events, triggering a call to inputChanged()). Apart from no longer being able to enter own chordnames by hand, no impact.
In addition to 'change', I tried out a variety of event.types ('submit', 'input', 'click' etc). No improvement.
The single most important clue is (to my mind) that only the first setChordname() return function is executed. This suggests that some fundamental rule of tween usage is being breached. The most likely candidate seems to be that the return value of tween **must* be an interpolator.
3 related questions, glad of answers to any:
Anything blatently wrong in this approach?
For a shared transition scenario such as this, do you see a better approach to transitioning a non-interpolable (and normally user-supplied) input property than using tween ?
Provided they are staggered in time, multiple transitions may be scheduled on the same element - but what about multiple tweens? Here, as the staggered transition/tween combos are operating on only one element, they seem likely to be passed identical data (d) and index(i) in every call. Impact?
I'm now able to answer my own question. Don't be put off by the initial couple of paragraphs: there are a couple of valuable lessons further down..
Ok, there were one or two trivial DOM-to-d3 reworking issues in my adoption of the original code. Moreover, an extra returned function construct managed to find it's way into this:
Was:
function setChordname(newValue) {
return function() { <--- Nasty..
var i = newValue;
return function(t) {
this.value = i;
inputChanged.call(this);
};
};
};
Should have been:
function setChordname(newValue) {
var i = newValue;
return function(t) {
this.value = i;
inputChanged.call(this);
};
};
The fundamental problem, however, was that the transition -passed in as a parameter to an update() function- seems in this case to have been blocked or ignored.
Originally (as documented in the question) defined as:
input // a UI dialog element
.transition()
.tween(i.toString(), setChordname( waveplot.chordname ));
..but should have been defined as:
transition
.select("#chordinput") // id attribute of the input element
.tween(i.toString(), setChordname( waveplot.chordname ));
My guess is that the first version tries to create a new transition (with no delay or duration defined), whereas the second uses the transition passed in through the update() interface.
Strange is that:
what worked for another dependent animation did not for this.
the staggered delays and their associated durations were nevertheless accepted by the original version, allowing me to be misled by console logs..
Just to round this topic off, I can point out the the following (event-based) approach seems to work just as well as the tween variant with non-interpolable values documented above. I can switch freely between the two with no apparent difference in the resulting animations:
transition
.select("#chordinput") // id attribute of the input element
.each("start", setChordname( waveplot.chordname ));
Thug
I am testing SVG performance using RaphaelJS library.
My code works, can be found here: JSFiddle
When you type in textbox "1" and press "add", a rectangle will be generated on screen and 4 animations will loop on it- moving right, down, left, up (also rotating, scaling and changing colour).
Performance seems to be ok. But add another element on stage and the performance gets knocked down to minimum in 3-4 seconds. Checked in Chrome timeline, the thing that is getting stacked up is "Animation Frame Fired - > Install Timer".
Perhaps I am doing loop incorrectly? Altough next animation starts when the previous ends, through callback function. Or is it Raphael itself? Should I try doing this with SVG and SMIL? (Raphael uses VML)
Any ideas?
------------------------------------UPDATE--------------------------------------
With RaphaelJS I did bad animation loop hooks, see answer below.
But another problem that does occur - add 1 element 10 times and you can see how animations get distorted, not finishing their full cycle, or add 10 elements 1 time and after few seconds you can see delayed animations on some of the elements.
I made SMIL version JSFiddle (no Raphael here), animations do not lag, delay, but they get syncronized. Can anyone explain why? And how to make those animations NOT sync, but unique?
I think the problem is you are recursively calling animations on a set.
So at the end of each animation, each element in the set calls an animation for the set again, so it spirals and grinds to a halt. You can get around this, by using 'this' instead of the set 'rectangles'.
//define 4 animations
var move_up = Raphael.animation({fill: "green", transform: "t0,0r360s1,1"}, 400, function(){ this.attr({"transform" : "t0,0"}); this.animate(move_right); });
var move_left = Raphael.animation({fill: "yellow", transform: "t0,100r270s0.5,0.5"}, 400, function(){ this.animate(move_up); });
var move_down = Raphael.animation({fill: "red", transform: "t100,100r180s1,1"}, 400, function(){ this.animate(move_left); });
var move_right = Raphael.animation({fill: "blue", transform: "t100,0r90s1.5,1.5"}, 400, function(){this.animate(move_down); });
jsfiddle
Is there an equivalent of an 'on interrupt' function when using dojo.fx animations?
On my site, I clear an area by sliding the div off the screen to the left and then clearing the innerHTML. So that the div is available for future use in the same place, I set its final position to be equal to its original position (though it's empty and invisible, so the user doesn't see). I do this by referencing the position when the function is fired to avoid hard coding the position in.
function clear (node) {
// get the current position
var position = domGeom.position (node, true);
// slide the calendar off the screen
var anim = dojo.fx.slideTo ({
node: node,
left: -2000,
unit: "px"
});
// attach on onEnd function
dojo.connect (anim, "onEnd", function (n1, n2) {
node.innerHTML = "";
// Reset the node's position
domStyle.set (node, "left", position.x + "px");
});
anim.play();
}
The problem is that if the user clicks 'clear' while the function is in progress, for example if they accidentally double click, then the 'position' variable takes the value of the node mid-animation. If I later put some content in the node, it's in the wrong place, or sometimes completely off screen.
What I was thinking of doing was adding a listener to the animation so that if it was ever interrupted, it would stop the animation and instead execute the 'onEnd' function immediately, so that the second clear function would have the correct position variable.
Unfortunately, I can't find such a listener; could anyone point me in the right direction, or suggest an alternative solution?
You might be interrupting the flow of events in your page, but you're not really 'interrupting' in Animation terms: there's no way an Animation could give you an event to tell you that Clear has been pressed, I'm afraid.
If you want the animation to be stopped when the user presses Clear, you'll have to stop the animation manually when the user presses Clear: Dojo can't automatically detect there's any link between the two things, and indeed there may well be other animations that you want to continue even if the user does press Clear.
So, you'll probably be looking to call .stop on the animation when appropriate.
I would like to fire a function when a user is scrolling and reaches a certain element, so that I can mark that element "read" (think of how posts were marked read after viewing them in the now defunct Google Reader). So, for example, imagine a page like the following:
<div id="1">Node 1</div>
<div id="2">Node 2</div>
<div id="3">Node 3</div>
<div id="4">Node 4</div>
Assuming only the first two <divs> fit within the viewport, I would like to use Dojo's event listener and fire an event when div 3 came into view and again when div 4 came into view. (In addition to my goal of recording when an element has been read, this would also allow me to easily implement infinite scrolling.)
The jQuery Waypoints tool seems to do exactly what I want, but since most of my code uses Dojo, I really don't want to use jQuery just for this one task. I'd like to find a prewritten tool or simple method for doing this that isn't dependent on a library/toolkit other than Dojo (I'm fine with it using Dojo or no library at all).
UPDATE: It appears Skrollr also does what I am looking for, but only for animation -- that is, it can animate an element based on its relationship to the viewport. This doesn't exactly help, though, since my goal isn't animation but to activate a function (that will make an AJAX call via Dojo) when an element comes within the viewpoint.
Thanks to a few pointers from a friend, I was able to put together a functional solution to this question. Assuming all the divs are within another div with the id articles, when the user scrolls the bottom of the element at least 50px into the viewport, I then apply a "read" class to the element.
posts = dojo.query("#articles > div");
dojo.connect(window, 'onscroll', function(){
var vs = win.getBox();
var output;
var readCount;
for (var i = 0; i < posts.length; i++) {
var includeScroll = false;
var locInfo = domGeom.position(posts[i], includeScroll)
var fullyOnScreen = locInfo.y + locInfo.h + 50;
if ((fullyOnScreen > 0) && (fullyOnScreen < vs.h)) {
//Apply "read" class to div if it has been scrolled to.
domClass.add(posts[i], "read");
}
if (locInfo.y > vs.h) {
break;
}
}
});
Remaining question: While this solves my goal, I'm not sure it is the most efficient to poll the location of every div in or above the viewport every time the user scrolls -- even multiple times per scroll event. Are there ways to make this more efficient? It seems to run OK on my Core 2 Duo system, but I don't want to be sloppy just because it doesn't seem laggy.
I thought about calculating the position of all of the divs from the absolute top just once on start up and then doing some comparisons between the viewport and the top each time I scroll. This would probably be more efficient, but I'm not sure if the efficiency gains justify the added complexity of the code.