1 | /** |
2 | * author Remy Sharp |
3 | * url http://remysharp.com/tag/marquee |
4 | * https://remysharp.com/2008/09/10/the-silky-smooth-marquee/ |
5 | */ |
6 | |
7 | (function ($) { |
8 | $.fn.marquee = function (klass) { |
9 | var newMarquee = [], |
10 | last = this.length; |
11 | |
12 | // works out the left or right hand reset position, based on scroll |
13 | // behavior, current direction and new direction |
14 | function getReset(newDir, marqueeRedux, marqueeState) { |
15 | var behavior = marqueeState.behavior, width = marqueeState.width, dir = marqueeState.dir; |
16 | var r = 0; |
17 | if (behavior == 'alternate') { |
18 | r = newDir == 1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : width; |
19 | } else if (behavior == 'slide') { |
20 | if (newDir == -1) { |
21 | r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] : width; |
22 | } else { |
23 | r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : 0; |
24 | } |
25 | } else { |
26 | r = newDir == -1 ? marqueeRedux[marqueeState.widthAxis] : 0; |
27 | } |
28 | return r; |
29 | } |
30 | |
31 | // single "thread" animation |
32 | function animateMarquee() { |
33 | var i = newMarquee.length, |
34 | marqueeRedux = null, |
35 | $marqueeRedux = null, |
36 | marqueeState = {}, |
37 | newMarqueeList = [], |
38 | hitedge = false; |
39 | |
40 | while (i--) { |
41 | marqueeRedux = newMarquee[i]; |
42 | $marqueeRedux = $(marqueeRedux); |
43 | marqueeState = $marqueeRedux.data('marqueeState'); |
44 | |
45 | if ($marqueeRedux.data('paused') !== true) { |
46 | // TODO read scrollamount, dir, behavior, loops and last from data |
47 | marqueeRedux[marqueeState.axis] += (marqueeState.scrollamount * marqueeState.dir); |
48 | |
49 | // only true if it's hit the end |
50 | hitedge = marqueeState.dir == -1 ? marqueeRedux[marqueeState.axis] <= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState) : marqueeRedux[marqueeState.axis] >= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState); |
51 | |
52 | if ((marqueeState.behavior == 'scroll' && marqueeState.last == marqueeRedux[marqueeState.axis]) || (marqueeState.behavior == 'alternate' && hitedge && marqueeState.last != -1) || (marqueeState.behavior == 'slide' && hitedge && marqueeState.last != -1)) { |
53 | if (marqueeState.behavior == 'alternate') { |
54 | marqueeState.dir *= -1; // flip |
55 | } |
56 | marqueeState.last = -1; |
57 | |
58 | $marqueeRedux.trigger('stop'); |
59 | |
60 | marqueeState.loops--; |
61 | if (marqueeState.loops === 0) { |
62 | if (marqueeState.behavior != 'slide') { |
63 | marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState); |
64 | } else { |
65 | // corrects the position |
66 | marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir * -1, marqueeRedux, marqueeState); |
67 | } |
68 | |
69 | $marqueeRedux.trigger('end'); |
70 | } else { |
71 | // keep this marquee going |
72 | newMarqueeList.push(marqueeRedux); |
73 | $marqueeRedux.trigger('start'); |
74 | marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState); |
75 | } |
76 | } else { |
77 | newMarqueeList.push(marqueeRedux); |
78 | } |
79 | marqueeState.last = marqueeRedux[marqueeState.axis]; |
80 | |
81 | // store updated state only if we ran an animation |
82 | $marqueeRedux.data('marqueeState', marqueeState); |
83 | } else { |
84 | // even though it's paused, keep it in the list |
85 | newMarqueeList.push(marqueeRedux); |
86 | } |
87 | } |
88 | |
89 | newMarquee = newMarqueeList; |
90 | |
91 | if (newMarquee.length) { |
92 | setTimeout(animateMarquee, 25); |
93 | } |
94 | } |
95 | |
96 | // TODO consider whether using .html() in the wrapping process could lead to loosing predefined events... |
97 | this.each(function (i) { |
98 | var $marquee = $(this), |
99 | width = $marquee.attr('width') || $marquee.width(), |
100 | height = $marquee.attr('height') || $marquee.height(), |
101 | $marqueeRedux = $marquee.after('<div ' + (klass ? 'class="' + klass + '" ' : '') + 'style="display: block-inline; width: ' + width + 'px; height: ' + height + 'px; overflow: hidden;"><div style="float: left; white-space: nowrap;">' + $marquee.html() + '</div></div>').next(), |
102 | marqueeRedux = $marqueeRedux.get(0), |
103 | hitedge = 0, |
104 | direction = ($marquee.attr('direction') || 'left').toLowerCase(), |
105 | marqueeState = { |
106 | dir : /down|right/.test(direction) ? -1 : 1, |
107 | axis : /left|right/.test(direction) ? 'scrollLeft' : 'scrollTop', |
108 | widthAxis : /left|right/.test(direction) ? 'scrollWidth' : 'scrollHeight', |
109 | last : -1, |
110 | loops : $marquee.attr('loop') || -1, |
111 | scrollamount : $marquee.attr('scrollamount') || this.scrollAmount || 2, |
112 | behavior : ($marquee.attr('behavior') || 'scroll').toLowerCase(), |
113 | width : /left|right/.test(direction) ? width : height |
114 | }; |
115 | |
116 | // corrects a bug in Firefox - the default loops for slide is -1 |
117 | if ($marquee.attr('loop') == -1 && marqueeState.behavior == 'slide') { |
118 | marqueeState.loops = 1; |
119 | } |
120 | |
121 | $marquee.remove(); |
122 | |
123 | // add padding |
124 | if (/left|right/.test(direction)) { |
125 | $marqueeRedux.find('> div').css('padding', '0 ' + width + 'px'); |
126 | } else { |
127 | $marqueeRedux.find('> div').css('padding', height + 'px 0'); |
128 | } |
129 | |
130 | // events |
131 | $marqueeRedux.bind('stop', function () { |
132 | $marqueeRedux.data('paused', true); |
133 | }).bind('pause', function () { |
134 | $marqueeRedux.data('paused', true); |
135 | }).bind('start', function () { |
136 | $marqueeRedux.data('paused', false); |
137 | }).bind('unpause', function () { |
138 | $marqueeRedux.data('paused', false); |
139 | }).data('marqueeState', marqueeState); // finally: store the state |
140 | |
141 | // todo - rerender event allowing us to do an ajax hit and redraw the marquee |
142 | |
143 | newMarquee.push(marqueeRedux); |
144 | |
145 | marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState); |
146 | $marqueeRedux.trigger('start'); |
147 | |
148 | // on the very last marquee, trigger the animation |
149 | if (i+1 == last) { |
150 | animateMarquee(); |
151 | } |
152 | }); |
153 | |
154 | return $(newMarquee); |
155 | }; |
156 | }(jQuery)); |
Travelled to 3 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx
No comments. add comment
Snippet ID: | #1032505 |
Snippet name: | marquee.jquery.js (<marquee> handler) |
Eternal ID of this version: | #1032505/2 |
Text MD5: | 2b038fa3855b9bfdb5010206711007fe |
Author: | stefan |
Category: | javascript |
Type: | Document |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-09-11 22:33:56 |
Source code size: | 7597 bytes / 156 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 162 / 59 |
Version history: | 1 change(s) |
Referenced in: | [show references] |