2009/4/12 Leo Meyerovich <lmey...@gmail.com>:
> InsertDomB / insertValueB are the exception. Nesting them within code
> returns us to the inverted style we're trying to avoid, so I'm not sure
> we should endorse it.
Could you give more details on what an "inverted style" is? I don't
quite get it.
> Again, perhaps there is a clear use case?
I was tinkering with Flapjax-based animation library, the goal being animating
elements of existing web-pages. (No supplanting of default browser behavior,
just adding some eye-candy via unobtrusive scripting).
(Basically, all I need is "reactive CSS properties" with proper units and a way
to easily assign them to elements.)
Since I don't know what kind of API to settle on, I have tried to find
prior work
similar to the task at hand. I found "Composing reactive animations" by Conal
Elliott, which describes an interface very much like I posted.
ATM, using the library is tedious:
> var elem = document.getElementById('foo');
> var a = anim(power(3.0))(-30.0, 0.0);
> var posB = liftB(a, moveB(clicksE(elem), 25, 250));
> insertValueB(liftB(function(x) {return x + "em";}, posB), elem, 'style', 'left');
>
Implementation details:
> var anim = function(f) {
> return function(a,b) {
> return function(x) { return a + (b - a) * f(x); }
> }
> }
> // an example easing function
> var power = function(y) { return function(x) { return Math.pow(x,y); } }
>
> // moveB :: EventStream a * [Behavior] Int * [Behavior] Int -> Behavior Float
> // very much like what Arjun showed me months ago
> function moveB(ev,step,duration) {
> var timerB = timerB(step);
> var startB = startsWith(snapshotE(ev,timeB), 0);
> return liftB(function(a,b,d) {return Math.min((b - a)/d, 1.0);},
> startB, timeB, duration);
> }
I tried to abstract some details out, by defining a bunch of functions analogous
to:
> // one function for one animatable css property :)
> var left = function(b, elem) {
> insertValueB(b, elem, 'style', 'left');
> return elem;
> }
(and fiddling with anim function), so they could be composed and abstracted out,
just like in Fran. Then I noticed that insertValueB doesn't return anything, and
an inquiry popped up.
Cheers,
Artyom Shalkhakov.
PS sorry for posting so much code
Wow. Thanks for clarifiying your terminology.
> An animation and widget library would make a lot of sense :) We've all
> been rolling our own individually so far it seems, and I don't know if
> there's enough consensus to make an 'official' one.
Well, I've looked into CSS3 Transitions and Animation modules, and it
seems to me
that it would be not too hard to implement these proposals using
Flapjax (in fact,
I already wrote a fraction of it). They are very limited in scope, though.
Also, I can volunteer to write an 'official' animation library that
nobody would be
happy with. :)
> E.g., how can we define separate wriggling and stretching animations, and then provide
> combinators for sequencing the two or even performing both at the same time?
Good question. Is anybody willing to answer?
> The proposal to mix data flow and assignments seems like a step
> backwards (though not a huge one!). E.g., it would suggest the following
> is idiomatic Flapjax:
>
> timer_b().times(200).insertValue_b('foo').triple(300).insertValue_b('bar');
>
> Not needing names is often nice... but it can introduce noise when
> trying to understand a particular value, as in this example.
Right. I don't understand what this code means.
> Furthermore, rather than seeing assignments on the left, they're nestled
> into arbitrary terms! Can you show how you used your function 'left'
> that makes code clearer with such a shortform?
Unfortunately, no, because I haven't actually used it yet. I wanted answers
to my questions first.
> I agree that writing multiple 'insertValue' statements isn't ideal.
> However, you could use one insertValue statement to bind multiple ones
> by passing in an object with fields! E.g., insertValueB({x:timer_b(), y:
> timer_b()}, widget, 'style'). Similarly, I think an animation library
> can be defined by passing around the property object ( {x:.., y:...}),
> where adding an animation creates a new one, with the last composition
> being chosen to be inserted. This style might also leave a lot of room
> for optimization, just like what happened for inplace DOM updates :)
This answers my question and yet leaves animation code pure, great!
Thanks a lot.
Cheers,
Artyom Shalkhakov.
Thanks for posting the code. Leo explained the issue very nicely.
In terms of the inversion issue, you can also read about it in the
tech report that I just mentioned:
http://www.cs.brown.edu/research/pubs/techreports/reports/CS-09-04.html
The description early in section 2 should make Leo's point through a
different example.
-----
We'd love to see your animation library. What to do about wriggling
and stretching? I'd say let's not think about it too much; give it a
try and toss out code; we'd all be happy to try it out and comment!
-----
[Along these lines, there's an nice "reactive widget library" embedded
in one of our applications. We should extract and document that, too.]
Shriram
2009/4/14 Shriram Krishnamurthi <s...@cs.brown.edu>:
> Thanks for posting the code. Leo explained the issue very nicely.
>
> In terms of the inversion issue, you can also read about it in the
> tech report that I just mentioned:
>
> http://www.cs.brown.edu/research/pubs/techreports/reports/CS-09-04.html
A good read, thanks!
> The description early in section 2 should make Leo's point through a
> different example.
I guess you're talking about subsection 2.1, which is shows the structure
of a typical JavaScript program. (Oops, I've missed the bug.)
> We'd love to see your animation library. What to do about wriggling
> and stretching? I'd say let's not think about it too much; give it a
> try and toss out code; we'd all be happy to try it out and comment!
Okay, will do, but most of it is contained within the code snippet that I
posted, anyway. I need to ship my work first, so expect a delay.
> [Along these lines, there's an nice "reactive widget library" embedded
> in one of our applications. We should extract and document that, too.]
I'm looking forward to this.
Cheers,
Artyom Shalkhakov.
An exception should be raised, perhaps?
Cheers,
Artyom Shalkhakov.
insertValueB({style: {left: mouseLeftB(document), top:
mouseTopB(document)}}, 'blah');
Since behaviors always have values, when either mouseLeftB or
mouseTopB changes, we can recompute the entire style object and insert
it into the DOM.
However, with event streams:
insertValueE({style: {left: mouseLeftE(document), top:
mouseTopE(document)}}, 'blah');
when a mouseLeft event fires, there may not be a corresponding
mouseTop event. So, we can't compute a new value for style. (We'd
have to do some exceedingly clever partial update to just set
style.left but not style.right. We're not that clever and that is not
consistent in general.)
In the examples you gave:
> insertValueB works with a hash table as its first argument. The
> following example code works:
>
> clicks.mapE( function(){ insertValueB({style: {height: twentyRamp
> ().startsWith(0)}}, elt)} );
Flapjax treats hash tables of constants as constant behaviors and
hashtables with behaviors as reactive objects. The alternative would
involve writing constantB(10), constantB("hello"), etc. That's why
this example works.
However, Flapjax does not implicitly convert objects and constant
values to event streams. So, in this function:
> clicks.mapE( function(){ insertValueE({style: {height: twentyRamp
> ()}}, elt) } );
the argument is an object, not an event streams. However, here:
> clicks.mapE( function(){ insertValueE(twentyRamp(), elt, 'style',
> 'height') });
twentyRamp() is an event stream so it works.
Arjun