├── .gitignore ├── package.json ├── bower.json ├── marquee3000.d.ts ├── LICENSE ├── marquee3k-noUMD.min.js ├── marquee3k.min.js ├── marquee3k-noUMD.js ├── marquee3k.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | npm-debug.log 3 | 4 | package.json 5 | 6 | .eslintrc.json 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marquee3000", 3 | "version": "1.1.1", 4 | "description": "Internet marquees in HD!", 5 | "keywords": ["marquee", "marquees"], 6 | "types": "marquee3k.d.ts", 7 | "main": "marquee3k.js", 8 | "repository": "https://github.com/ezekielaquino/Marquee3000", 9 | "homepage": "https://ezekielaquino.github.io/Marquee3000/", 10 | "author": "Ezekiel Aquino ", 11 | "dependencies": {} 12 | } 13 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marquee3000", 3 | "description": "The smoothest javascript marquees for the new millennium", 4 | "main": "marquee3k.js", 5 | "authors": [ 6 | "Ezekiel Aquino " 7 | ], 8 | "license": "MIT", 9 | "keywords": [ 10 | "marquee", 11 | "marquees" 12 | ], 13 | "homepage": "https://github.com/ezekielaquino/Marquee3000", 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /marquee3000.d.ts: -------------------------------------------------------------------------------- 1 | interface Options { 2 | selector: string 3 | } 4 | 5 | declare class Marquee3k { 6 | constructor(element: HTMLElement, options: Options) 7 | animate(): void 8 | repopulate(difference: number, isLarger: boolean): void 9 | static refresh(index: number): void 10 | static pause(index: number): void 11 | static play(index: number): void 12 | static toggle(index: number): void 13 | static refreshAll(): void 14 | static playAll(): void 15 | static toggleAll(): void 16 | static init(options?: Options): void 17 | } 18 | 19 | declare module 'marquee3000' { 20 | export = Marquee3k 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ezekiel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /marquee3k-noUMD.min.js: -------------------------------------------------------------------------------- 1 | "use strict";let animationId=0;class Marquee3k{constructor(t,e){this.element=t,this.selector=e.selector,this.speed=t.dataset.speed||.25,this.pausable=t.dataset.pausable,this.reverse=t.dataset.reverse,this.paused=!1,this.parent=t.parentElement,this.parentProps=this.parent.getBoundingClientRect(),this.content=t.children[0],this.innerContent=this.content.innerHTML,this.wrapStyles="",this.offset=0,this._setupWrapper(),this._setupContent(),this._setupEvents(),this.wrapper.appendChild(this.content),this.element.appendChild(this.wrapper)}_setupWrapper(){this.wrapper=document.createElement("div"),this.wrapper.classList.add("marquee3k__wrapper"),this.wrapper.style.whiteSpace="nowrap"}_setupContent(){this.content.classList.add(`${this.selector}__copy`),this.content.style.display="inline-block",this.contentWidth=this.content.offsetWidth,this.requiredReps=this.contentWidth>this.parentProps.width?2:Math.ceil((this.parentProps.width-this.contentWidth)/this.contentWidth)+1;for(let t=0;t{this.pausable&&(this.paused=!0)}),this.element.addEventListener("mouseleave",()=>{this.pausable&&(this.paused=!1)})}_createClone(){const t=this.content.cloneNode(!0);t.style.display="inline-block",t.classList.add(`${this.selector}__copy`),this.wrapper.appendChild(t)}animate(){if(!this.paused){const t=this.reverse?this.offset<0:this.offset>-1*this.contentWidth,e=this.reverse?-1:1,s=this.reverse?-1*this.contentWidth:0;t?this.offset-=this.speed*e:this.offset=s,this.wrapper.style.whiteSpace="nowrap",this.wrapper.style.transform=`translate(${this.offset}px, 0) translateZ(0)`}}_refresh(){this.contentWidth=this.content.offsetWidth}repopulate(t,e){if(this.contentWidth=this.content.offsetWidth,e){const e=Math.ceil(t/this.contentWidth)+1;for(let t=0;t{clearTimeout(s),s=setTimeout(()=>{const t=ithis.parentProps.width?2:Math.ceil((this.parentProps.width-this.contentWidth)/this.contentWidth)+1;for(let t=0;t{this.pausable&&(this.paused=!0)}),this.element.addEventListener("mouseleave",()=>{this.pausable&&(this.paused=!1)})}_createClone(){const t=this.content.cloneNode(!0);t.style.display="inline-block",t.classList.add(`${this.selector}__copy`),this.wrapper.appendChild(t)}animate(){if(!this.paused){const t=this.reverse?this.offset<0:this.offset>-1*this.contentWidth,e=this.reverse?-1:1,s=this.reverse?-1*this.contentWidth:0;t?this.offset-=this.speed*e:this.offset=s,this.wrapper.style.whiteSpace="nowrap",this.wrapper.style.transform=`translate(${this.offset}px, 0) translateZ(0)`}}_refresh(){this.contentWidth=this.content.offsetWidth}repopulate(t,e){if(this.contentWidth=this.content.offsetWidth,e){const e=Math.ceil(t/this.contentWidth)+1;for(let t=0;t{clearTimeout(n),n=setTimeout(()=>{const t=r this.parentProps.width ? 2 : Math.ceil((this.parentProps.width - this.contentWidth) / this.contentWidth) + 1; 47 | 48 | for (let i = 0; i < this.requiredReps; i++) { 49 | this._createClone(); 50 | } 51 | 52 | if (this.reverse) { 53 | this.offset = this.contentWidth * -1; 54 | } 55 | 56 | this.element.classList.add('is-init'); 57 | } 58 | 59 | _setupEvents() { 60 | this.element.addEventListener('mouseenter', () => { 61 | if (this.pausable) this.paused = true; 62 | }); 63 | 64 | this.element.addEventListener('mouseleave', () => { 65 | if (this.pausable) this.paused = false; 66 | }); 67 | } 68 | 69 | _createClone() { 70 | const clone = this.content.cloneNode(true); 71 | clone.style.display = 'inline-block'; 72 | clone.classList.add(`${this.selector}__copy`); 73 | this.wrapper.appendChild(clone); 74 | } 75 | 76 | animate() { 77 | if (!this.paused) { 78 | const isScrolled = this.reverse ? this.offset < 0 : this.offset > this.contentWidth * -1; 79 | const direction = this.reverse ? -1 : 1; 80 | const reset = this.reverse ? this.contentWidth * -1 : 0; 81 | 82 | if (isScrolled) this.offset -= this.speed * direction; 83 | else this.offset = reset; 84 | 85 | this.wrapper.style.whiteSpace = 'nowrap'; 86 | this.wrapper.style.transform = `translate(${this.offset}px, 0) translateZ(0)`; 87 | } 88 | } 89 | 90 | _refresh() { 91 | this.contentWidth = this.content.offsetWidth; 92 | } 93 | 94 | repopulate(difference, isLarger) { 95 | this.contentWidth = this.content.offsetWidth; 96 | 97 | if (isLarger) { 98 | const amount = Math.ceil(difference / this.contentWidth) + 1; 99 | 100 | for (let i = 0; i < amount; i++) { 101 | this._createClone(); 102 | } 103 | } 104 | } 105 | 106 | static refresh(index) { 107 | MARQUEES[index]._refresh(); 108 | } 109 | 110 | static pause(index) { 111 | MARQUEES[index].paused = true; 112 | } 113 | 114 | static play(index) { 115 | MARQUEES[index].paused = false; 116 | } 117 | 118 | static toggle(index) { 119 | MARQUEES[index].paused = !MARQUEES[index].paused; 120 | } 121 | 122 | static refreshAll() { 123 | for (let i = 0; i < MARQUEES.length; i++) { 124 | MARQUEES[i]._refresh(); 125 | } 126 | } 127 | 128 | static pauseAll() { 129 | for (let i = 0; i < MARQUEES.length; i++) { 130 | MARQUEES[i].paused = true; 131 | } 132 | } 133 | 134 | static playAll() { 135 | for (let i = 0; i < MARQUEES.length; i++) { 136 | MARQUEES[i].paused = false; 137 | } 138 | } 139 | 140 | static toggleAll() { 141 | for (let i = 0; i < MARQUEES.length; i++) { 142 | MARQUEES[i].paused = !MARQUEES[i].paused; 143 | } 144 | } 145 | 146 | static init(options = { selector: 'marquee3k' }) { 147 | if (animationId) window.cancelAnimationFrame(animationId); 148 | 149 | window.MARQUEES = []; 150 | const marquees = Array.from(document.querySelectorAll(`.${options.selector}`)); 151 | let previousWidth = window.innerWidth; 152 | let timer; 153 | 154 | for (let i = 0; i < marquees.length; i++) { 155 | const marquee = marquees[i]; 156 | const instance = new Marquee3k(marquee, options); 157 | MARQUEES.push(instance); 158 | } 159 | 160 | animate(); 161 | 162 | function animate() { 163 | for (let i = 0; i < MARQUEES.length; i++) { 164 | MARQUEES[i].animate(); 165 | } 166 | 167 | animationId = window.requestAnimationFrame(animate); 168 | } 169 | 170 | window.addEventListener('resize', () => { 171 | clearTimeout(timer); 172 | 173 | timer = setTimeout(() => { 174 | const isLarger = previousWidth < window.innerWidth; 175 | const difference = window.innerWidth - previousWidth; 176 | 177 | for (let i = 0; i < MARQUEES.length; i++) { 178 | MARQUEES[i].repopulate(difference, isLarger); 179 | } 180 | 181 | previousWidth = this.innerWidth; 182 | }, 250); 183 | }); 184 | } 185 | } 186 | 187 | export default Marquee3k; -------------------------------------------------------------------------------- /marquee3k.js: -------------------------------------------------------------------------------- 1 | /** 2 | * MARQUEE 3000 MARQUEE 3000 MARQUEE 3000 MARQUEE 3000 MARQUEE 3000 3 | * http://github.com/ezekielaquino/marquee3000 4 | * Marquees for the new millennium v1.0 5 | * MIT License 6 | */ 7 | 8 | ;(function(root, factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | define([], factory); 11 | } else if (typeof exports === 'object') { 12 | module.exports = factory(); 13 | } else { 14 | root.Marquee3k = factory(); 15 | } 16 | }(this, function() { 17 | 'use strict'; 18 | 19 | let animationId = 0; 20 | 21 | class Marquee3k { 22 | constructor(element, options) { 23 | 24 | if (element.children.length === 0) { 25 | throw new Error("Encountered a marquee element without children, please supply a wrapper for your content"); 26 | } 27 | 28 | this.element = element; 29 | this.selector = options.selector; 30 | this.speed = element.dataset.speed || 0.25; 31 | this.pausable = element.dataset.pausable === 'true' ? true : false; 32 | this.reverse = element.dataset.reverse === 'true' ? true : false; 33 | this.paused = false; 34 | this.parent = element.parentElement; 35 | this.parentProps = this.parent.getBoundingClientRect(); 36 | this.content = element.children[0]; 37 | this.innerContent = this.content.innerHTML; 38 | this.wrapStyles = ''; 39 | this.offset = 0; 40 | 41 | this._setupWrapper(); 42 | this._setupContent(); 43 | this._setupEvents(); 44 | 45 | this.wrapper.appendChild(this.content); 46 | this.element.appendChild(this.wrapper); 47 | } 48 | 49 | _setupWrapper() { 50 | this.wrapper = document.createElement('div'); 51 | this.wrapper.classList.add('marquee3k__wrapper'); 52 | this.wrapper.style.whiteSpace = 'nowrap'; 53 | } 54 | 55 | _setupContent() { 56 | this.content.classList.add(`${this.selector}__copy`); 57 | this.content.style.display = 'inline-block'; 58 | this.contentWidth = this.content.offsetWidth; 59 | 60 | this.requiredReps = this.contentWidth > this.parentProps.width ? 2 : Math.ceil((this.parentProps.width - this.contentWidth) / this.contentWidth) + 1; 61 | 62 | for (let i = 0; i < this.requiredReps; i++) { 63 | this._createClone(); 64 | } 65 | 66 | if (this.reverse) { 67 | this.offset = this.contentWidth * -1; 68 | } 69 | 70 | this.element.classList.add('is-init'); 71 | } 72 | 73 | _setupEvents() { 74 | this.element.addEventListener('mouseenter', () => { 75 | if (this.pausable) this.paused = true; 76 | }); 77 | 78 | this.element.addEventListener('mouseleave', () => { 79 | if (this.pausable) this.paused = false; 80 | }); 81 | } 82 | 83 | _createClone() { 84 | const clone = this.content.cloneNode(true); 85 | clone.style.display = 'inline-block'; 86 | clone.classList.add(`${this.selector}__copy`); 87 | this.wrapper.appendChild(clone); 88 | } 89 | 90 | animate() { 91 | if (!this.paused) { 92 | const isScrolled = this.reverse ? this.offset < 0 : this.offset > this.contentWidth * -1; 93 | const direction = this.reverse ? -1 : 1; 94 | const reset = this.reverse ? this.contentWidth * -1 : 0; 95 | 96 | if (isScrolled) this.offset -= this.speed * direction; 97 | else this.offset = reset; 98 | 99 | this.wrapper.style.whiteSpace = 'nowrap'; 100 | this.wrapper.style.transform = `translate(${this.offset}px, 0) translateZ(0)`; 101 | } 102 | } 103 | 104 | _refresh() { 105 | this.contentWidth = this.content.offsetWidth; 106 | } 107 | 108 | repopulate(difference, isLarger) { 109 | this.contentWidth = this.content.offsetWidth; 110 | 111 | if (isLarger) { 112 | const amount = Math.ceil(difference / this.contentWidth) + 1; 113 | 114 | for (let i = 0; i < amount; i++) { 115 | this._createClone(); 116 | } 117 | } 118 | } 119 | 120 | static refresh(index) { 121 | MARQUEES[index]._refresh(); 122 | } 123 | 124 | static pause(index) { 125 | MARQUEES[index].paused = true; 126 | } 127 | 128 | static play(index) { 129 | MARQUEES[index].paused = false; 130 | } 131 | 132 | static toggle(index) { 133 | MARQUEES[index].paused = !MARQUEES[index].paused; 134 | } 135 | 136 | static refreshAll() { 137 | for (let i = 0; i < MARQUEES.length; i++) { 138 | MARQUEES[i]._refresh(); 139 | } 140 | } 141 | 142 | static pauseAll() { 143 | for (let i = 0; i < MARQUEES.length; i++) { 144 | MARQUEES[i].paused = true; 145 | } 146 | } 147 | 148 | static playAll() { 149 | for (let i = 0; i < MARQUEES.length; i++) { 150 | MARQUEES[i].paused = false; 151 | } 152 | } 153 | 154 | static toggleAll() { 155 | for (let i = 0; i < MARQUEES.length; i++) { 156 | MARQUEES[i].paused = !MARQUEES[i].paused; 157 | } 158 | } 159 | 160 | static init(options = { selector: 'marquee3k' }) { 161 | if (animationId) window.cancelAnimationFrame(animationId); 162 | 163 | window.MARQUEES = []; 164 | const marquees = Array.from(document.querySelectorAll(`.${options.selector}`)); 165 | let previousWidth = window.innerWidth; 166 | let timer; 167 | 168 | for (let i = 0; i < marquees.length; i++) { 169 | const marquee = marquees[i]; 170 | const instance = new Marquee3k(marquee, options); 171 | MARQUEES.push(instance); 172 | } 173 | 174 | animate(); 175 | 176 | function animate() { 177 | for (let i = 0; i < MARQUEES.length; i++) { 178 | MARQUEES[i].animate(); 179 | } 180 | animationId = window.requestAnimationFrame(animate); 181 | } 182 | 183 | window.addEventListener('resize', () => { 184 | clearTimeout(timer); 185 | 186 | timer = setTimeout(() => { 187 | const isLarger = previousWidth < window.innerWidth; 188 | const difference = window.innerWidth - previousWidth; 189 | 190 | for (let i = 0; i < MARQUEES.length; i++) { 191 | MARQUEES[i].repopulate(difference, isLarger); 192 | } 193 | 194 | previousWidth = this.innerWidth; 195 | }, 250); 196 | }); 197 | } 198 | } 199 | 200 | return Marquee3k; 201 | 202 | })); 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MARQUEE3000 2 | 3 | **Note 16.6.2020** 4 | This module is now a human toddler's age! I'm super happy that it has been of use to a lot of people and it's seen in use creatively around some really cool websites on the web in the last years. Apologies if I don't offer support aside from tending to reports related to breaking bugs. I am however thinking of revisiting this module sometime soon (fingers crossed) because 3 years in js land `is` a millenium. 5 | ___ 6 | 7 | Marquees for the new millennium ✨ 8 | 9 | Super smooth and versatile javaScript plugin with no dependencies. 10 | 11 | 👄 12 | 13 | Note: Marquees were a bit neglected, no? There are tons, 14 | but they're slow and you can't really do much with them. 15 | So much you can do with the inherently strong typographic 16 | look of em. So this is me trying to update them for 2017. 17 | 18 | A GSAP dependent version is available (WIP): [MARQUEE3G](http://github.com/ezekielaquino/Marquee3G) 19 | 20 | # New Version : Important! 21 | 22 | Please change how you initialize form Marquee3k() -> Marquee3k.init() !!! 23 | 24 | ## New in 1.0.6 25 | 26 | - fixes bug when reverse scrolling is true 27 | - refresh single marquee instance / all marquees 28 | - access Marquee instances globally 29 | 30 | ## Features 31 | - ~3kb minified with no dependencies 32 | - Turn any element into a smooth-as-butter marquee 33 | - Text, images++* it'll do it 34 | - Style marquees as usual with CSS– get creative! 35 | - Set speed and direction 36 | - Have a ton without any slowdown 37 | - Responsive! 38 | 39 | ## Demo 40 | [MARQUEE3000 DEMO MARQUEE3000 DEMO MARQUEE3000 DEMO](https://ezekielaquino.com/2019/marquee) 41 | 42 | ## Usage 43 | 44 | 1. Include Marquee3000 in your html file. Download zip or install via `bower install marquee3000` || `npm install marquee3000` 45 | 46 | ```javascript 47 | const Marquee3k = require('marquee3000'); 48 | // or 49 | import Marquee3k from 'marquee3000'; 50 | ``` 51 | 52 | 53 | ```html 54 | 55 | ``` 56 | 57 | 2. Create an element with a `.marquee3k` class. You can pass different options such as speed, orientation and direction (optional). See below for options. 58 | 59 | ```html 60 |
64 | 66 |

Some marquee content

67 |
68 | ``` 69 | 70 | 3. Fill it up with text or images etc. (still finding out what you can do with it) 71 | 72 | 4. In your js file or `