產品說,咱們作一個轉盤活動吧,須要輪播中獎信息。 固然這需求徹底沒有問題。
產品說,你聽我說完。css
沒問題吧。
額, 等等,沒多大問題。 那個誰,這個任務教你啦。css3
而後,個人同事開始蒐羅實現方案,不少都是勻速走的。
同事甚至和產品討論要不要換成跑馬燈,嘻嘻, 開玩笑。
說個笑話,csdn上有很多的這樣代碼,可是下載要積分,我能夠說,日了*狗麼!。git
一個下午天氣晴,有涼風,心情還好。 因而花了一點時間思考了一種方案。github
關於移動端動畫,無非是純js控制,js Animation API(兼容不理想), css動畫,canvas, webgl以及雜交方案。 關於本需求,前兩種應該比較適合,成本低,容易實現。web
純js實現控制比較傳統的方案,要啓用定時器setTimeout/setInterval/requestAnimation等等,我很煩這。canvas
採用css3 + js雜交方案,有戲靠譜。 既然有三個階段,那麼我就把你拆成三段動畫, 隨你去配置,隨你去high。 固然你也能夠用一段動畫,經過設置來控制距離。app
總體的思路ide
改進動畫
const contents = [ "隊列1:春天 - first", "隊列1:夏天", "隊列1:秋天", "隊列1:冬天", "隊列1:夏夏湉", "隊列1:求七天", "隊列1:Who are You - last" ]; const contents2 = [ "隊列2:這是怎麼回事 - first", "隊列2:誰是最可賴的人", "隊列2:壯士一去不復返", "隊列2:誰來拯救你", "隊列2:家福樂團購有沒有 - last" ] const el = document.querySelector("#box"); let upSlide = new UpSlide({ el }); upSlide.start(contents); document.getElementById('btnChange').addEventListener("click", () => { upSlide.start(contents); }) document.getElementById('btnChange2').addEventListener("click", () => { upSlide.start(contents2); })
等等這個有點用, 源碼呢
上滑跑馬燈源碼webgl
再貼出源碼,這樣文章長一點
const DEFAULT_OPTION = { inTime: 1000, pauseTime: 1500, outTime: 1000, className: "upslide-item", animationClass: "upslide-item-animation", animationInClass: "slideup-animation-in", animationPauseClass: "slideup-animation-pause", animationOutClass: "slideup-animation-out", pauseOnFocus: false }; const DELETING_CLASS_NAME = "__deleting__"; function clearSiblings(el) { const parent = el.parentElement; // 移除前面節點 while (el.previousElementSibling) { parent.removeChild(el.previousElementSibling); } // 移除後面的節點 while (el.nextElementSibling) { parent.removeChild(el.nextElementSibling); } } class UpSlide { constructor(options) { this.el = options.el; this.options = Object.assign({}, DEFAULT_OPTION, options); this.changeStatus = 0; this.currentContents = null; const { inTime, pauseTime, outTime } = this.options; this.totalTime = inTime + pauseTime + outTime; this.inPausePercent = (inTime + pauseTime) / this.totalTime; this.animationstartEvent = this.animationstartEvent.bind(this); this.animationendEvent = this.animationendEvent.bind(this); this.mouseenterEvent = this.mouseenterEvent.bind(this); this.mouseleaveEvent = this.mouseleaveEvent.bind(this); this.init(); } createItems(datas, baseDelay = 0) { const { className, animationInClass, animationClass, inTime } = this.options; const { totalTime, inPausePercent } = this; const fragment = document.createDocumentFragment(); datas.forEach((c, i) => { const newEl = document.createElement("div"); newEl.dataset.isLast = i === datas.length - 1 ? 1 : 0; newEl.innerText = c; newEl.className = className + " " + animationClass; newEl.style.animationName = animationInClass; newEl.style.animationDelay = baseDelay + i * totalTime * inPausePercent + "ms"; newEl.style.animationDuration = inTime + "ms"; fragment.appendChild(newEl); }); return fragment; } animationstartEvent(e) { const { totalTime, inPausePercent } = this; const { animationInClass } = this.options; // 開啓新的輪迴 if (e.animationName === animationInClass && e.target.dataset.isLast == 1) { this.innerStart(this.currentContents, totalTime * inPausePercent); } } animationendEvent(e) { const { animationInClass, animationPauseClass, animationOutClass, className, animationClass, pauseTime, outTime } = this.options; const { changeStatus } = this; const el = e.target; const parent = el.parentElement; const animationName = e.animationName; switch (animationName) { case animationInClass: el.style.animationName = animationPauseClass; el.style.animationDuration = pauseTime + "ms"; el.style.animationDelay = "0ms"; break; case animationPauseClass: el.style.animationName = animationOutClass; el.style.animationDuration = outTime + "ms"; el.style.animationDelay = "0ms"; // 切換 if (changeStatus === 1) { clearSiblings(el); // 標記 el.classList.add(DELETING_CLASS_NAME); // 切換 this.innerStart(this.currentContents, 0); this.changeStatus = 0; } break; case animationOutClass: e.target.classList.remove(animationClass); e.target.style.animationDelay = ""; if (el.classList.contains(DELETING_CLASS_NAME)) { parent.removeChild(el); } // 輪迴結束-清除節點 if (e.target.dataset.isLast == 1) { const parent = e.target.parentElement; const delItems = parent.querySelectorAll( `.${className}:not(.${animationClass})` ); if (delItems.length > 0) { for (let i = delItems.length - 1; i >= 0; i--) { parent.removeChild(delItems[i]); } } } break; default: break; } } mouseenterEvent() { const { className } = this.options; this.el.querySelectorAll("." + className).forEach(el => { el.style.animationPlayState = "paused"; }); } mouseleaveEvent() { const { className } = this.options; this.el.querySelectorAll("." + className).forEach(el => { el.style.animationPlayState = "running"; }); } init() { const { el } = this; el.addEventListener("animationstart", this.animationstartEvent); el.addEventListener("animationend", this.animationendEvent); const { pauseOnFocus } = this.options; if (pauseOnFocus === true) { el.addEventListener("mouseenter", this.mouseenterEvent); el.addEventListener("mouseleave", this.mouseleaveEvent); } } innerStart(content, delay = 0) { this.currentContents = content; const c = this.createItems(content, delay); this.el.appendChild(c); } start(content, delay = 0) { if (this.currentContents != null) { this.changeStatus = 1; this.currentContents = content; return; } this.innerStart(content, delay); } destroy() { this.el.removeEventListener("animationstart", this.animationstartEvent); this.el.removeEventListener("animationend", this.animationendEvent); const { pauseOnFocus } = this.options; if (pauseOnFocus === true) { el.removeEventListener("mouseenter", this.mouseoverEvent); el.removeEventListener("mouseleave", this.mouseleaveEvent); } this.el.innerHTML = null; this.el = null; this.options = null; } }