Skip to content

Commit 3fc790a

Browse files
committed
started rafactoring to allow multiple animations to be applied to the same layer
1 parent f8fe81a commit 3fc790a

File tree

3 files changed

+308
-233
lines changed

3 files changed

+308
-233
lines changed

jcp-animator.js

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/**
2+
* Animation handler class
3+
*
4+
* Manages animation events & timing between a Viewport and Layer.
5+
* This level of abstraction allows multiple animations to exist on
6+
* and update a layer simultaneously - these animations may or may not be tied
7+
* to the same input event.
8+
*
9+
* @requires jcparallax.js
10+
* @requires jcp-viewport.js
11+
* @requires jcp-layer.js
12+
* @author Sam Pospischil <[email protected]>
13+
*/
14+
jcparallax.Animator = function(layer, options)
15+
{
16+
var defaults = {
17+
movementRangeX : true, // autodetect
18+
movementRangeY : true,
19+
20+
inputEvent : 'mousemove',
21+
inputHandler : 'mousemove',
22+
23+
animHandler : 'position',
24+
};
25+
26+
this.layer = layer;
27+
this.viewport = layer.viewport;
28+
options = $.extend(true, defaults, options);
29+
30+
// set default range calculation callbacks for builtin animHandlers passed as strings
31+
if (options.movementRangeX === true || options.movementRangeY === true && !$.isFunction(options.animHandler)) {
32+
// infer layer movement range calculators for builtin animation handlers
33+
switch (options.animHandler) {
34+
case 'position':
35+
case 'padding':
36+
case 'margins':
37+
case 'background':
38+
case 'stretch':
39+
switch (options.inputHandler) {
40+
case 'scroll':
41+
options.movementRangeX = jcparallax.Layer.rangeCalculators.scrollWidth;
42+
options.movementRangeY = jcparallax.Layer.rangeCalculators.scrollHeight;
43+
break;
44+
default:
45+
options.movementRangeX = jcparallax.Layer.rangeCalculators.width;
46+
options.movementRangeY = jcparallax.Layer.rangeCalculators.height;
47+
break;
48+
}
49+
break;
50+
case 'textShadow':
51+
options.movementRangeX = jcparallax.Layer.rangeCalculators.fontSize;
52+
options.movementRangeY = jcparallax.Layer.rangeCalculators.lineHeight;
53+
break;
54+
case 'opacity':
55+
options.movementRangeX = [0, 0];
56+
options.movementRangeY = jcparallax.Layer.rangeCalculators.opacity;
57+
break;
58+
}
59+
}
60+
if (typeof options.movementRangeX == 'string') {
61+
options.movementRangeX = jcparallax.Layer.rangeCalculators[options.movementRangeX];
62+
}
63+
if (typeof options.movementRangeY == 'string') {
64+
options.movementRangeY = jcparallax.Layer.rangeCalculators[options.movementRangeY];
65+
}
66+
67+
// interpret animation handler
68+
if ($.isFunction(options.animHandler)) { // custom handler callback
69+
this.animHandler = options.animHandler;
70+
} else { // single callback from the predefined set
71+
this.animHandler = jcparallax.Animator.animHandlers[options.animHandler];
72+
}
73+
74+
this.options = options;
75+
76+
// refresh target element coordinates
77+
this.refreshCoords();
78+
79+
// bind input events
80+
this.bindEvent(options.inputEvent, options.inputHandler);
81+
};
82+
83+
$.extend(jcparallax.Animator.prototype, {
84+
85+
inputEvent : null, // DOM input event this animation is bound to
86+
inputHandler : null, // input event handler callback to output range normalised value
87+
animHandler : null, // animation update handler callback
88+
89+
minX : null,
90+
minY : null,
91+
rangeX : null, // scaling factors for the animation over input 0-1
92+
rangeY : null, // call refreshCoords() to update from the callbacks supplied in options
93+
94+
// last input sampling coordinates
95+
lastSampledX : 0,
96+
lastSampledY : 0,
97+
lastProcessedX : 0,
98+
lastProcessedY : 0,
99+
100+
/**
101+
* Binds the DOM event responsible for handling our updates
102+
* @param {string} eventName name of the DOM event to bind to for updates
103+
* @param {function} handler Callback for handling the event.
104+
* Accepts the bound layer element, x position (0 <= x <= 1), y position and event object as parameters.
105+
*/
106+
bindEvent : function(eventName, handler)
107+
{
108+
var that = this;
109+
eventName += jcparallax.eventNamespace;
110+
111+
// infer handler from predefined set if a string
112+
if (typeof handler == 'string') {
113+
handler = jcparallax.Viewport.inputHandlers[handler];
114+
}
115+
116+
// detach old callback first if present
117+
if (this.inputHandler && this.inputEvent) {
118+
this.viewport.element.off(this.inputEvent, this.inputHandler);
119+
}
120+
121+
// create new callback & bind it to the viewport
122+
this.inputHandler = function(e) {
123+
handler.call(that, that.viewport.element, e);
124+
};
125+
this.inputEvent = eventName;
126+
this.viewport.element.on(eventName, this.inputHandler);
127+
},
128+
129+
/**
130+
* Updates the sampled input position for calculating the effect.
131+
* This method should be called for every event encountered to update
132+
* the X and Y values, which are then applied at the next frame interval.
133+
*/
134+
updateLastSamplePos : function(xVal, yVal)
135+
{
136+
this.lastSampledX = xVal;
137+
this.lastSampledY = yVal;
138+
},
139+
140+
/**
141+
* Refreshes all computed coordinates from our movement range handler
142+
* callbacks after layer DOM element is modified externally.
143+
*/
144+
refreshCoords : function()
145+
{
146+
var xRange, yRange;
147+
148+
if ($.isFunction(this.options.movementRangeX)) {
149+
xRange = this.options.movementRangeX.call(this.layer, this.layer.element, this.viewport);
150+
} else {
151+
xRange = this.options.movementRangeX;
152+
}
153+
if ($.isFunction(this.options.movementRangeY)) {
154+
yRange = this.options.movementRangeY.call(this.layer, this.layer.element, this.viewport);
155+
} else {
156+
yRange = this.options.movementRangeY;
157+
}
158+
159+
this.minX = parseFloat(xRange[0]);
160+
this.rangeX = parseFloat(xRange[1] - xRange[0]);
161+
this.minY = parseFloat(yRange[0]);
162+
this.rangeY = parseFloat(yRange[1] - yRange[0]);
163+
},
164+
165+
/**
166+
* Generate a CSS object for modification of our layer,
167+
* using our last sampled input values or the ones provided.
168+
* Input values should be 0 < x < 1.
169+
*
170+
* @return {object} CSS properties to set on the layer element
171+
*/
172+
makeCss : function(xVal, yVal)
173+
{
174+
// when positions not passed, just redraw
175+
if (xVal === undefined && yVal === undefined) {
176+
xVal = this.lastSampledX;
177+
yVal = this.lastSampledY;
178+
}
179+
180+
if (xVal == this.lastProcessedX && yVal == this.lastProcessedY) {
181+
return {}; // no change in input
182+
}
183+
this.lastProcessedX = xVal;
184+
this.lastProcessedY = yVal;
185+
186+
return this.animHandler.call(this, xVal, yVal);
187+
}
188+
});
189+
190+
//------------------------------------------------------------------------------
191+
// Layer animation handlers
192+
//------------------------------------------------------------------------------
193+
194+
jcparallax.Animator.animHandlers = {
195+
196+
// standard css attributes - minimal support
197+
198+
position : function(xVal, yVal)
199+
{
200+
return {
201+
left : this.minX + (xVal * this.rangeX),
202+
top : this.minY + (yVal * this.rangeY),
203+
};
204+
},
205+
206+
padding : function(xVal, yVal)
207+
{
208+
209+
},
210+
211+
margins : function(xVal, yVal)
212+
{
213+
214+
},
215+
216+
background : function(xVal, yVal)
217+
{
218+
return {
219+
'background-position' : (this.minX + (xVal * this.rangeX)) + 'px ' + (this.minY + (yVal * this.rangeY)) + 'px',
220+
};
221+
},
222+
223+
stretch : function(xVal, yVal)
224+
{
225+
226+
},
227+
228+
// CSS3 attributes
229+
230+
textShadow : function(xVal, yVal)
231+
{
232+
233+
},
234+
235+
opacity : function(xVal, yVal)
236+
{
237+
238+
}
239+
};

0 commit comments

Comments
 (0)