Skip to content

Instantly share code, notes, and snippets.

@eric1234
Last active October 5, 2016 10:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save eric1234/548690 to your computer and use it in GitHub Desktop.
Save eric1234/548690 to your computer and use it in GitHub Desktop.
<marquee> element lives! - Emulating the behavior of the old marquee element with CoffeeScript/JavaScript
# https://gist.github.com/eric1234/548690
class @Marquee
constructor: (@element, @options={}) ->
@clean @element
@stopped = true
# Set defaults
@options['duration'] or= 30
@options['wrapAround'] = true unless 'wrapAround' of @options
@options['direction'] or= 'forward'
# If everything fits don't bother with all this mess
return if this.element.scrollWidth == this.element.clientWidth
# Create wrapping container to scroll the marquee in.
# Adapted from Prototype's wrap method.
@container = document.createElement 'div'
@container.className = 'marquee-wrapper'
@element.parentNode.replaceChild @container, @element if @element.parentNode
@container.appendChild @element
# Move some of the relevant marquee styles to this wrapper.
@container.style.width = @element.clientWidth+'px'
@container.style.height = @element.clientHeight+'px'
@container.style.overflow = 'hidden'
# Keep in mind how big it was so we know when to reset
@originalWidth = @element.scrollWidth
# Make it look like it is wrapping around by duping content
if @options.wrapAround
trimmed = element.innerHTML.trim()
@element.innerHTML = trimmed + trimmed + trimmed
# Size restrictions/clipping moved to wrapper, remove from element
@element.style.width = @element.scrollWidth+'px'
@element.style.height = @element.scrollHeight+'px'
@element.style.overflow = 'auto'
@container.scrollLeft = @originalWidth if @options.wrapAround
# Will start the marquee moving.
#
# If no argument is given it will move until it gets to the end of the
# content. If wrapAround is enabled it will reset and do another move to the
# end of the content creating a never-ending scrolling of the content.
#
# If a distance is given then it will only move that far then stop.
#
# If a distance is given then the onFinish is called when the
# animation ends. If no distance is given then onFinish is called
# every time the animation resets.
move: (distance=null, onFinish=->) ->
@stopped = false
to = if distance?
distance
else
@originalWidth
ending_position = if @options.direction is 'forward'
@container.scrollLeft + to
else
@container.scrollLeft - to
if @options.wrapAround
if ending_position > 2 * @originalWidth
@container.scrollLeft -= @originalWidth
ending_position -= @originalWidth
if ending_position < 0
@container.scrollLeft += @originalWidth
ending_position += @originalWidth
start_position = @container.scrollLeft
callback = if @options.wrapAround && !distance
=>
onFinish()
unless @stopped
@container.scrollLeft = start_position
@animation = Marquee.tween @container, @options.duration, ending_position, callback
else
=>
@stopped = true
onFinish()
@animation = Marquee.tween @container, @options.duration, ending_position, callback
# Stops all animation so you can issue new move commands.
stop: ->
@stopped = true
Marquee.stop @animation if @animation
clean: (node) ->
to_remove = []
for c in node.childNodes
if c.nodeType is 8 || (c.nodeType is 3 && !/S/.test(c.nodeValue))
to_remove.push c
else if c.nodeType is 1
@clean c
for n in to_remove
n.parentNode.removeChild n
# To actually do the moving we farm this out to a callback. This way the actual
# movement can be handled by a library like morpheus which is designed as a
# single high-performance loop using constant speed animation and the
# requestAnimationFrame standard.
#
# This should be replaced by a function that actually does the work.
Marquee = @Marquee
Marquee.tween = (element, duration, target_scroll, oncomplete) ->
# By default implement a stupid "animation" that just move it without
# any in-between stages.
element.scrollLeft = target_scroll
oncomplete()
Marquee.stop = (animation) ->
# Adapted from
# http://stackoverflow.com/questions/498970/#answer-8522376
unless String::trim
String::trim = -> @replace /^\s+|\s+$/g,''
<!DOCTYPE html>
<html>
<head>
<style>
#test {
font-size: 3em;
width: 10em;
height: 1.5em;
overflow: hidden;
white-space: nowrap;
}
</style>
</head>
<div id="test">
The quick brown fox jumps over the lazy dog
</div>
<script src="marquee.js"></script>
<script src="morpheus.js"></script>
<script>
Marquee.tween = function(element, duration, end, onComplete) {
morpheus.tween(duration*1000, function(position) {
element.scrollLeft = position
}, onComplete, function(p) {return p}, element.scrollLeft, end)
}
marquee = new Marquee(document.getElementById('test'));
marquee.move();
</script>
</html>
@eric1234
Copy link
Author

Updated again. Replaced timeout animation with hook for real animation library to do tweening.

@eric1234
Copy link
Author

eric1234 commented Aug 8, 2012

Minor bug fixes and provided default "animation"

@leefish
Copy link

leefish commented Nov 10, 2012

Hi, I hope it is ok to ask here, but I was looking for a prototype marquee and this looked great - except I see you updated to coffeescript. Would you still have the old prototype code and be willing to repost it?

Thanks

@eric1234
Copy link
Author

The old code is still available under a previous revision. But the new code will work with prototype/scriptaculous. All you need to do is provide the Script.aculo.us tween effect in the callback and you can use the latest code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment