上滑跑马灯

news/2024/11/14 20:22:30/

前言

产品说,我们做一个转盘活动吧,需要轮播中奖信息。 当然这需求完全没有问题。
产品说,你听我说完。

  1. 是从下往上轮播
  2. 如何数据没有更新,就反复轮播。
  3. 如果数据有更新,要无缝更新。
  4. 进入时间1s,暂停1S,出去时间1s.

没问题吧。
额, 等等,没多大问题。 那个谁,这个任务教你啦。

方案

然后,我的同事开始搜罗实现方案,很多都是匀速走的。
同事甚至和产品讨论要不要换成跑马灯,嘻嘻, 开玩笑。
说个笑话,csdn上有不少的这样代码,但是下载要积分,我可以说,日了*狗么!。

一个下午天气晴,有凉风,心情还好。 于是花了一点时间思考了一种方案。

关于移动端动画,无非是纯js控制,js Animation API(兼容不理想), css动画,canvas, webgl以及杂交方案。 关于本需求,前两种应该比较适合,成本低,容易实现。

纯js实现控制比较传统的方案,要启用定时器setTimeout/setInterval/requestAnimation等等,我很烦这。

采用css3 + js杂交方案,有戏靠谱。 既然有三个阶段,那么我就把你拆成三段动画, 随你去配置,随你去high。 当然你也可以用一段动画,通过设置来控制距离。

整体的思路

  1. 我把每个需要滚动的每个元素独立起来,每个元素有三段动画, in , pause , out. 怎么衔接, 通过animationend事件。
  2. 那么不同元素又怎么衔接,通过animationDelay来延时启动动画, 启动后依旧是走上面的三段动画。
  3. 怎么轮回播放,当然你可以利用原来的节点,重新修改属性,设置延时启动。 我这里采用比较简单的,直接删除了原有,然后重新创建。 当然重新创建是有讲究的,你有很多选择,只要控制好衔接的事件,我这里是在最后一个节点开始第一阶段运动的时候,重新创建新节点,最后节点第三阶段运动结束,清除之前运动完毕的节点
  4. 关于无缝更新,当然要让最后一个运动的元素运动完, 所以我在第二个阶段 pause阶段执行新的节点创建,并设置好相关的延时。
  5. 关于暂停能力, animationPlayState提供这个能力,paused和running。 暂停的手,animationDelay也会停止计时,非常的棒。 因为懒,只实现了PC的暂停能力。移动端嘛,添加touch,tap,pointer事件就好。 因为懒,所以。

改进

  1. 每个元素三个动画,是有点消耗。可以用一个大的容器放好所有的元素,然后animationPlayState来控制,蛮不错的。 如何控制每个阶段的计时呢,当然可以用js,我有个想法,就是起一个三段动画,在这个事件里面去控制animationPlayState。
  2. 节点也没回收利用,是有点浪费。
  3. 移动端的支持呢?
  4. 不太重要的其他......

效果呢?

upSlide.gif

怎么使用呢?

        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


http://www.ppmy.cn/news/608917.html

相关文章

GeforceRTX系列参数对比

GeforceRTX系列参数对比

数据结构算法 简单的面试思考题

目录 简单的面试思考题 思考题一 思考题二 思考题三 简单的面试思考题 思考题一 有64瓶疫苗, 其中一瓶不小心混入了有害物质, 现在要利用小白鼠找出那一瓶! 注意:小白鼠只要喝一点点混入有害物质的在30分钟就是死亡, 那么现在只剩下30分 钟了(只能进行一次实验), 问最少…

Python类的定义和使用

Python 是面向对象语言,所以类(class) 这个概念还是有的, 因为类(Class)是面向对象程序设计(OOP,Object-Oriented Programming)实现信息封装的基础 1 类的作用: 用来描述具有相同的属性和方法的对象的集合 2 类的使…

【USACO06JAN POJ3179】Corral the Cows

POJ 洛谷 分析 离散化前缀和二分 这题和激光炸弹很像&#xff0c;但由于坐标范围较大&#xff0c;需要用到二分。 代码 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define il inline #define maxn 10007 #defin…

NVIDIA数据中心深度学习产品性能

NVIDIA数据中心深度学习产品性能 在现实世界的应用程序中部署AI&#xff0c;需要训练网络以指定的精度融合。这是测试AI系统的最佳方法-准备将其部署在现场&#xff0c;因为网络随后可以提供有意义的结果&#xff08;例如&#xff0c;对视频流正确执行图像识别&#xff09;。不…

003_如何学好英语?

一、 (1)实用在线词典推荐 剑桥&#xff08;中英/英英&#xff09;https://dictionary.cambridge.org/dictionary/牛津&#xff08;英英&#xff0c;可以查找同义词&#xff09;https://en.oxforddictionaries.com/definition/on_earth朗文&#xff08;英英&#xff09;https:/…

大数据必学语言Scala(三十二):scala高级用法 样例类

文章目录 样例类 定义样例类 样例类方法 样例对象 样例类 样例类是一种特殊类,它可以用来快速定义一个用于保存数据的类(类似于Java POJO类),而且它会自动生成apply方法,允许我们快速地创建样例类实例对象。后面,在并发编程和spark、flink这些框架也都会经常使用它。…

Python xlrd 读取excel表格 常用用法整理

xlrd 的使用 #!/usr/bin/python# # -*- coding: utf-8 -*- import xlrd import sys reload(sys) sys.setdefaultencoding("utf-8") # 打开excel table xlrd.open_workbook(/home/hly/hly/test.xls) # excel 地步表格的名称 sheetName table.sheet_names() print(…