Open Challenge: how would you do this?
By AlexanderUPDATE: Great effort everyone! So many ways to skin a cat, apparently. Math.sin was the solution for me, but it was great to see other solutions, especially Pål’s crazy GTween one! Here’s what I ended up with:
var summerAlpha: Number = Math.abs(Math.sin(years * Math.PI * pos)); //or this for speed: var season: Number = Math.sin(years * Math.PI * pos); var summerAlpha: Number = (season < 0) ? -season : season;
In a recent project, I had to solve the following problem:
There is a timeline that spans several years. The timeline is scrollable, with the scroller returning a normalized value between 0 and 1. So, if the timeline is 2 years long, the year 1 scroll values would fall between 0 and 0.5. Now, depending on the timeline value, the page background should reflect the current year’s season (red background image in summer, blue background image in winter, and a mix in between). So, if the timeline is again 2 years long, the background would have to transition from blue to red (0 to 0.25), from red to blue (0.25 to 0.5), from blue to red (0.5 to 0.75), and finally red back to blue again (0.75 to 1). Hint to start: you can fade the 2 images together
How would you do this? (I’ll post my solution in 48 hours)


Is the length of the timeline a static two years?
Could you put the red image above the blue and use alpha to mix them and use modulo to get the value for a single year?
Like this:
alpha = ((scrollvalue*2)%1)*2
alpha = alpha > 1 ? 2-alpha : alpha
redimage.alpha = alpha
I just did this quickly - I suspect the modulo fomula could be refined. Alpha grows to 1, then down to zero, back to 1 befor ending on zero. Seems right?
Good try Torbjørn! The timeline doesn’t neccessarily need to be fixed at 2 years, but in this example it is. You are right on target with the alpha, but it looks like the output from your algorithm is off. Here are the official results from your try:
[INFO] 14:43:55.136 :: pos: 0 ::((pos*2)%1)*2: 0 ::target: 0
[INFO] 14:43:55.140 :: pos: 0.05 ::((pos*2)%1)*2: 0.2 ::target: 0.31
[INFO] 14:43:55.142 :: pos: 0.1 ::((pos*2)%1)*2: 0.4 ::target: 0.59
[INFO] 14:43:55.146 :: pos: 0.15 ::((pos*2)%1)*2: 0.6 ::target: 0.81
[INFO] 14:43:55.149 :: pos: 0.2 ::((pos*2)%1)*2: 0.8 ::target: 0.95
[INFO] 14:43:55.151 :: pos: 0.25 ::((pos*2)%1)*2: 1 ::target: 1
[INFO] 14:43:55.152 :: pos: 0.3 ::((pos*2)%1)*2: 1.2 ::target: 0.95
[INFO] 14:43:55.157 :: pos: 0.35 ::((pos*2)%1)*2: 1.4 ::target: 0.81
[INFO] 14:43:55.160 :: pos: 0.4 ::((pos*2)%1)*2: 1.6 ::target: 0.59
[INFO] 14:43:55.162 :: pos: 0.45 ::((pos*2)%1)*2: 1.8 ::target: 0.31
[INFO] 14:43:55.164 :: pos: 0.5 ::((pos*2)%1)*2: 2 ::target: 0
[INFO] 14:43:55.167 :: pos: 0.55 ::((pos*2)%1)*2: 0.2 ::target: 0.31
[INFO] 14:43:55.170 :: pos: 0.6 ::((pos*2)%1)*2: 0.4 ::target: 0.59
[INFO] 14:43:55.171 :: pos: 0.65 ::((pos*2)%1)*2: 0.6 ::target: 0.81
[INFO] 14:43:55.173 :: pos: 0.7 ::((pos*2)%1)*2: 0.8 ::target: 0.95
[INFO] 14:43:55.175 :: pos: 0.75 ::((pos*2)%1)*2: 1 ::target: 1
[INFO] 14:43:55.183 :: pos: 0.8 ::((pos*2)%1)*2: 1.2 ::target: 0.95
[INFO] 14:43:55.190 :: pos: 0.85 ::((pos*2)%1)*2: 1.4 ::target: 0.81
[INFO] 14:43:55.192 :: pos: 0.9 ::((pos*2)%1)*2: 1.6 ::target: 0.59
[INFO] 14:43:55.194 :: pos: 0.95 ::((pos*2)%1)*2: 1.8 ::target: 0.31
@Torbjørn Oops!! I forgot to add the second line. Looks like your method would work fine. Well done!
[INFO] 15:12:16.819 :: pos: 0 ::test: 0 ::target: 0
[INFO] 15:12:16.822 :: pos: 0.05 ::test: 0.2 ::target: 0.31
[INFO] 15:12:16.824 :: pos: 0.1 ::test: 0.4 ::target: 0.59
[INFO] 15:12:16.827 :: pos: 0.15 ::test: 0.6 ::target: 0.81
[INFO] 15:12:16.828 :: pos: 0.2 ::test: 0.8 ::target: 0.95
[INFO] 15:12:16.831 :: pos: 0.25 ::test: 1 ::target: 1
[INFO] 15:12:16.833 :: pos: 0.3 ::test: 0.8 ::target: 0.95
[INFO] 15:12:16.835 :: pos: 0.35 ::test: 0.6000000000000001 ::target: 0.81
[INFO] 15:12:16.838 :: pos: 0.4 ::test: 0.3999999999999999 ::target: 0.59
[INFO] 15:12:16.840 :: pos: 0.45 ::test: 0.19999999999999996 ::target: 0.31
[INFO] 15:12:16.842 :: pos: 0.5 ::test: 0 ::target: 0
[INFO] 15:12:16.845 :: pos: 0.55 ::test: 0.2 ::target: 0.31
[INFO] 15:12:16.847 :: pos: 0.6 ::test: 0.4 ::target: 0.59
[INFO] 15:12:16.848 :: pos: 0.65 ::test: 0.6 ::target: 0.81
[INFO] 15:12:16.851 :: pos: 0.7 ::test: 0.8 ::target: 0.95
[INFO] 15:12:16.853 :: pos: 0.75 ::test: 1 ::target: 1
[INFO] 15:12:16.860 :: pos: 0.8 ::test: 0.8 ::target: 0.95
[INFO] 15:12:16.862 :: pos: 0.85 ::test: 0.6000000000000001 ::target: 0.81
[INFO] 15:12:16.863 :: pos: 0.9 ::test: 0.3999999999999999 ::target: 0.59
[INFO] 15:12:16.865 :: pos: 0.95 ::test: 0.19999999999999996 ::target: 0.31
Anyone else? I’ve got another way….
As modulo is not in my vocabulary (or at least it wasn’t… My programs usually aren’t very math heavy.) I chose another solution:
(The blue image have to be above the red, btw)
var years:int = 2;
var season:Number = (sliderValue*years) - (Math.floor(sliderValue*years));
var newAlpha:Number = Math.abs(1.0 - (season*2));
blue_mc.alpha = newAlpha;
// you can also fade the red oposite to the blue by adding
red_mc.alpha = 1 - newAlpha;
I certainly do not claim this to be the most elegant way to do it, but it seems to work as intented in my little test program at least.
@Roger Good work, but it looks like your method might have problems at 0.5 and 1, at least according to the official test set up
[INFO] 16:49:45.727 :: pos: 0 ::test: 0 ::target: 0
[INFO] 16:49:45.731 :: pos: 0.05 ::test: 0.19999999999999996 ::target: 0.31
[INFO] 16:49:45.733 :: pos: 0.1 ::test: 0.4 ::target: 0.59
[INFO] 16:49:45.735 :: pos: 0.15 ::test: 0.6000000000000001 ::target: 0.81
[INFO] 16:49:45.736 :: pos: 0.2 ::test: 0.8 ::target: 0.95
[INFO] 16:49:45.738 :: pos: 0.25 ::test: 1 ::target: 1
[INFO] 16:49:45.740 :: pos: 0.3 ::test: 0.8 ::target: 0.95
[INFO] 16:49:45.741 :: pos: 0.35 ::test: 0.6000000000000001 ::target: 0.81
[INFO] 16:49:45.743 :: pos: 0.4 ::test: 0.40000000000000013 ::target: 0.59
[INFO] 16:49:45.744 :: pos: 0.45 ::test: 0.20000000000000018 ::target: 0.31
[INFO] 16:49:45.746 :: pos: 0.5 ::test: 2.220446049250313e-16 ::target: 0
[INFO] 16:49:45.747 :: pos: 0.55 ::test: 0.19999999999999973 ::target: 0.31
[INFO] 16:49:45.749 :: pos: 0.6 ::test: 0.3999999999999999 ::target: 0.59
[INFO] 16:49:45.751 :: pos: 0.65 ::test: 0.6000000000000001 ::target: 0.81
[INFO] 16:49:45.752 :: pos: 0.7 ::test: 0.8000000000000003 ::target: 0.95
[INFO] 16:49:45.755 :: pos: 0.75 ::test: 0.9999999999999996 ::target: 1
[INFO] 16:49:45.757 :: pos: 0.8 ::test: 0.7999999999999994 ::target: 0.95
[INFO] 16:49:45.758 :: pos: 0.85 ::test: 0.5999999999999992 ::target: 0.81
[INFO] 16:49:45.760 :: pos: 0.9 ::test: 0.399999999999999 ::target: 0.59
[INFO] 16:49:45.761 :: pos: 0.95 ::test: 0.19999999999999885 ::target: 0.31
[INFO] 16:49:45.763 :: pos: 1 ::test: 8.881784197001252e-16 ::target: 0
How aboot this? ( Assuming the red image is on top )
redImage.alpha = Math.pow(Math.sin(Math.PI*pos),2);
Usually I also like to use math solutions for problems like this. But in this case I would like to bring on one of my favorite toys, the GTweenTimeline! It’s a great tool that all Flash developers need to look into. So here is the G-solution for this problem:
timeline = new GTweenTimeline();
timeline.addTween(0/4,new GTween(red,1/4,{alpha:1}));
timeline.addTween(1/4,new GTween(red,1/4,{alpha:0}));
timeline.addTween(2/4,new GTween(red,1/4,{alpha:1}));
timeline.addTween(3/4,new GTween(red,1/4,{alpha:0}));
timeline.autoPlay = false;
Later you can just use timeline.gotoAndStop() to skip to 0 through 1.
But GTweenTimeline is so much more powerful than this, you could tween all other properties of the pictures or i.e. fade inn/out leaves in the fall.
PS. Here is my results, with the no easing as above (number formatting applied):
[INFO] 21:04:40.703 :: pos 0 : 0
[INFO] 21:04:40.718 :: pos 0.05 : 0.2
[INFO] 21:04:40.718 :: pos 0.1 : 0.4
[INFO] 21:04:40.718 :: pos 0.15 : 0.6
[INFO] 21:04:40.718 :: pos 0.2 : 0.8
[INFO] 21:04:40.718 :: pos 0.25 : 1
[INFO] 21:04:40.718 :: pos 0.3 : 0.8
[INFO] 21:04:40.718 :: pos 0.35 : 0.6
[INFO] 21:04:40.718 :: pos 0.4 : 0.4
[INFO] 21:04:40.718 :: pos 0.45 : 0.2
[INFO] 21:04:40.718 :: pos 0.5 : 0
[INFO] 21:04:40.718 :: pos 0.55 : 0
[INFO] 21:04:40.734 :: pos 0.6 : 0.4
[INFO] 21:04:40.734 :: pos 0.65 : 0.6
[INFO] 21:04:40.734 :: pos 0.7 : 0.8
[INFO] 21:04:40.734 :: pos 0.75 : 1
[INFO] 21:04:40.734 :: pos 0.8 : 0.64
[INFO] 21:04:40.734 :: pos 0.85 : 0.48
[INFO] 21:04:40.734 :: pos 0.9 : 0.32
[INFO] 21:04:40.734 :: pos 0.95 : 0.16
[INFO] 21:04:40.734 :: pos 1 : 0
@Alexander: Hmm… I was a bit too quick there, obviously..
Tempting as it is I don´t have time too look more into it atm, thought. :/
And when you need norwegian seasons you do this:
summerBitmap.alpha = Math.pow(Math.sin(Math.PI* value *years),6)
( http://flash.apt.no/wp-content/uploads/2009/05/picture-1.png )
@Thomas @hp Math.sin was also the way I ended up solving it, but it looks like combining it with Math.pow will give you the same problems that @Roger had, namely that you will get out of bounds values at the year’s end (0.5 & 1).
Using hp’s algorithm:
[INFO] 09:06:45.854 :: pos: 0 ::test: 0 ::target: 0
[INFO] 09:06:45.858 :: pos: 0.05 ::test: 0.0008707514062631436 ::target: 0.31
[INFO] 09:06:45.860 :: pos: 0.1 ::test: 0.04123937851565786 ::target: 0.59
[INFO] 09:06:45.861 :: pos: 0.15 ::test: 0.28037924859373686 ::target: 0.81
[INFO] 09:06:45.863 :: pos: 0.2 ::test: 0.7400106214843418 ::target: 0.95
[INFO] 09:06:45.867 :: pos: 0.25 ::test: 1 ::target: 1
[INFO] 09:06:45.869 :: pos: 0.3 ::test: 0.7400106214843425 ::target: 0.95
[INFO] 09:06:45.870 :: pos: 0.35 ::test: 0.28037924859373686 ::target: 0.81
[INFO] 09:06:45.872 :: pos: 0.4 ::test: 0.04123937851565791 ::target: 0.59
[INFO] 09:06:45.873 :: pos: 0.45 ::test: 0.0008707514062631527 ::target: 0.31
[INFO] 09:06:45.875 :: pos: 0.5 ::test: 3.307100200773103e-92 ::target: 0
[INFO] 09:06:45.876 :: pos: 0.55 ::test: 0.0008707514062631348 ::target: 0.31
[INFO] 09:06:45.878 :: pos: 0.6 ::test: 0.041239378515657815 ::target: 0.59
[INFO] 09:06:45.879 :: pos: 0.65 ::test: 0.28037924859373675 ::target: 0.81
[INFO] 09:06:45.881 :: pos: 0.7 ::test: 0.7400106214843418 ::target: 0.95
[INFO] 09:06:45.882 :: pos: 0.75 ::test: 1 ::target: 1
[INFO] 09:06:45.884 :: pos: 0.8 ::test: 0.7400106214843408 ::target: 0.95
[INFO] 09:06:45.886 :: pos: 0.85 ::test: 0.280379248593736 ::target: 0.81
[INFO] 09:06:45.888 :: pos: 0.9 ::test: 0.041239378515657295 ::target: 0.59
[INFO] 09:06:45.889 :: pos: 0.95 ::test: 0.000870751406263119 ::target: 0.31
[INFO] 09:06:45.891 :: pos: 1 ::test: 1.289967041377515e-89 ::target: 0
@Alexander, Yeah, hitting a perfect zero is nice but 3.307100200773103e-92 is seriously close to it, and only non zero because Math.PI is not accurate enough.
So a rounding operation resolves it.
Number(3.307100200773103e-92).toFixed(20) //: 0.00000000000000000000 (String)
int(3.307100200773103e-92*1000000)/1000000 //: 0 (number)
@Alexander Am I missing something obvious?
A printout gives me (numbers rounded)
[INFO] pos: 0.00, result: 0.00
[INFO] pos: 0.05, result: 0.20
[INFO] pos: 0.10, result: 0.40
[INFO] pos: 0.15, result: 0.60
[INFO] pos: 0.20, result: 0.80
[INFO] pos: 0.25, result: 1.00
[INFO] pos: 0.30, result: 0.80
[INFO] pos: 0.35, result: 0.60
[INFO] pos: 0.40, result: 0.40
[INFO] pos: 0.45, result: 0.20
[INFO] pos: 0.50, result: 0.00
[INFO] pos: 0.55, result: 0.20
[INFO] pos: 0.60, result: 0.40
[INFO] pos: 0.65, result: 0.60
[INFO] pos: 0.70, result: 0.80
[INFO] pos: 0.75, result: 1.00
[INFO] pos: 0.80, result: 0.80
[INFO] pos: 0.85, result: 0.60
[INFO] pos: 0.90, result: 0.40
[INFO] pos: 0.95, result: 0.20
[INFO] pos: 1.00, result: 0.01
..which seems fairly correct?
@Roger @hp @Thomas Rounding errors, rounding errors, everyone is a winner!
One-tenth of a dollar
One-tenth of a dollar
We got service after sales
Step right up!
Step riiiiiight up!
In the solution you ended up using - won’t the alpha ‘curve’ look like a bouncing ball rather than an undulating sin curve when you use Math.abs? Would it not be better to transpose the sin result: (Sin+1)/2 - to make it go from 0 to 1? I can’t imagine that it would make a big difference visually in this case, but as we’re discussing nitty-gritty stuff… what do you think?