Last active
October 5, 2016 10:48
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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,'' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.