上滑跑马灯
前言
產品說,我們做一個轉盤活動吧,需要輪播中獎信息。 當然這需求完全沒有問題。
產品說,你聽我說完。
- 是從下往上輪播
- 如何數據沒有更新,就反復輪播。
- 如果數據有更新,要無縫更新。
- 進入時間1s,暫停1S,出去時間1s.
沒問題吧。
額, 等等,沒多大問題。 那個誰,這個任務教你啦。
方案
然后,我的同事開始搜羅實現方案,很多都是勻速走的。
同事甚至和產品討論要不要換成跑馬燈,嘻嘻, 開玩笑。
說個笑話,csdn上有不少的這樣代碼,但是下載要積分,我可以說,日了*狗么!。
一個下午天氣晴,有涼風,心情還好。 于是花了一點時間思考了一種方案。
關于移動端動畫,無非是純js控制,js Animation API(兼容不理想), css動畫,canvas, webgl以及雜交方案。 關于本需求,前兩種應該比較適合,成本低,容易實現。
純js實現控制比較傳統的方案,要啟用定時器setTimeout/setInterval/requestAnimation等等,我很煩這。
采用css3 + js雜交方案,有戲靠譜。 既然有三個階段,那么我就把你拆成三段動畫, 隨你去配置,隨你去high。 當然你也可以用一段動畫,通過設置來控制距離。
整體的思路
- 我把每個需要滾動的每個元素獨立起來,每個元素有三段動畫, in , pause , out. 怎么銜接, 通過animationend事件。
- 那么不同元素又怎么銜接,通過animationDelay來延時啟動動畫, 啟動后依舊是走上面的三段動畫。
- 怎么輪回播放,當然你可以利用原來的節點,重新修改屬性,設置延時啟動。 我這里采用比較簡單的,直接刪除了原有,然后重新創建。 當然重新創建是有講究的,你有很多選擇,只要控制好銜接的事件,我這里是在最后一個節點開始第一階段運動的時候,重新創建新節點,最后節點第三階段運動結束,清除之前運動完畢的節點
- 關于無縫更新,當然要讓最后一個運動的元素運動完, 所以我在第二個階段 pause階段執行新的節點創建,并設置好相關的延時。
- 關于暫停能力, animationPlayState提供這個能力,paused和running。 暫停的手,animationDelay也會停止計時,非常的棒。 因為懶,只實現了PC的暫停能力。移動端嘛,添加touch,tap,pointer事件就好。 因為懶,所以。
改進
- 每個元素三個動畫,是有點消耗。可以用一個大的容器放好所有的元素,然后animationPlayState來控制,蠻不錯的。 如何控制每個階段的計時呢,當然可以用js,我有個想法,就是起一個三段動畫,在這個事件里面去控制animationPlayState。
- 節點也沒回收利用,是有點浪費。
- 移動端的支持呢?
- 不太重要的其他......
效果呢?
怎么使用呢?
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);}) 源碼呢?
等等這個有點用, 源碼呢
上滑跑馬燈源碼
再貼出源碼,這樣文章長一點
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;}
}
轉載于:https://www.cnblogs.com/cloud-/p/11402598.html
總結
- 上一篇: redis连接
- 下一篇: 【USACO06JAN POJ3179