Several implementations of scrolling animation (marquee animation)

In large-screen visualization applications, scrolling animation (marquee effect) is also a common data display method. This chapter makes a small summary of subtitle scrolling and list scrolling effects. Combined with the Vue framework, the specific display effects are as follows, from The selection techniques from left to right are: marquee tag, css3-animation animation attribute, css3-animation animation attribute, Javascript to realize scrolling animation

 Text scrolling implementation function description:

  1. Text infinite scrolling display, support mouse hovering pause function
  2. Text scrolling speed controllable
  3. Support dynamic rendering of interface data, if the data changes, the animation will restart

List scrolling implementation function description:

  1. List infinite scroll display, support mouse hovering pause function
  2. List scrolling speed controllable
  3. Support interface data dynamic rendering, data change, animation restart
  4. Support mouse scrolling operation, you can slide up to the beginning of the animation or slide down to the end of the animation
  5. The number of list items displayed per page is controllable

Analysis of function realization:

 There are no more than three ways to achieve the effect of conventional animation: one is to use the native html tag - marquee to realize the scrolling effect; the other is to realize the animation effect through css3 related animation properties; the third is to use javascript combined with a timer to achieve it through polling The animation effect of the dynamic change of related attributes; of course, each implementation method has its own advantages and disadvantages, and you can choose the technology according to your needs. This chapter briefly explains the relevant implementation principles.

Here, static data is used to simulate interface data, and the specific data format is as follows

// data.js
export let datas1 = `Vue 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty中的
访问器属性中的 get和 set方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 
get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 
Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新
虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到
真实 DOM 树上`

export let datas2 = `《意见》明确,保障性租赁住房主要解决符合条件的新市民、青年人等群体的住房困难问题,
以建筑面积不超过70平方米的小户型为主,租金低于同地段同品质市场租赁住房租金;由政府给予政策支持,充分发挥
市场机制作用,引导多主体投资、多渠道供给,主要利用存量土地和房屋建设,适当利用新供应国有建设用地建设。
城市人民政府要坚持供需匹配,科学确定“十四五”保障性租赁住房建设目标和政策措施,制定年度建设计划,
并向社会公布;加强对保障性租赁住房建设、出租和运营管理的全过程监督,强化工程质量安全监管。
城市人民政府对本地区发展保障性租赁住房负主体责任,省级人民政府负总责。`

export let datas3 = [
    {
        content: "内容一",
        bgColor: "#08a6e9",
    },
    {
        content: "内容二",
        bgColor: "#22d4c8",
    },
    {
        content: "内容三",
        bgColor: "#ffa06a",
    },
    {
        content: "内容四",
        bgColor: "#0f96f0",
    },
    {
        content: "内容五",
        bgColor: "#ffa66a",
    }
];

export let datas4 = [
    {
        content: "content-1",
        bgColor: "#08a6e9",
    },
    {
        content: "content-2",
        bgColor: "#22d4c8",
    },
    {
        content: "content-3",
        bgColor: "#ffa06a",
    },
    {
        content: "content-4",
        bgColor: "#0f96f0",
    },
    {
        content: "content-5",
        bgColor: "#ffa66a",
    }
];

1. The marquee tag implements text scrolling animation (corresponding to the first one in the picture)

1. Use in components

<!-- 基于原生html-marquee标签实现滚动效果 -->
<template>
  <div class="container">
    <marquee
      scrollamount="4"
      hspace="0"
      vspace="0"
      behavior="scroll"
      direction="up"
    >
      <p class="content">{
   
   { content }}</p>
    </marquee>
  </div>
</template>
<script>
import { datas1 } from "./data";
export default {
  data() {
    return {
      content: datas1,
    };
  },
};
</script>
<style scoped>
.container {
  width: 300px;
  height: 300px;
  padding: 10px;
  outline: 1px dashed #ccc;
  overflow: hidden;
}
.content {
  font-size: 14px;
  line-height: 30px;
  text-indent: 2em;
}
</style>

2. The implementation of the marquee tag is very simple, you only need to add the corresponding attributes, which is very friendly to developers. But it is an outdated label, and it cannot achieve seamless scrolling effect, and it is difficult to cope with complex demand scenarios, so there are not many application scenarios in the project.

2. css3-animation realizes text scrolling effect (corresponding to the second picture)

1. First encapsulate a text scrolling component, the code is as follows

<template>
  <div class="font-wrap" ref="topbox">
    <div :class="['font-list', dynamicCls]" :style="dynamicSty">
      <div ref="fontbox" v-text="content"></div>
      <div v-if="doubleData" v-text="content"></div>
    </div>
  </div>
</template>
<script>
export default {
  name: "FontScrollIndex",
  props: {
    content: {
      default: "",
    },
    pageDuration: {
      default: 6,
    },
  },
  data() {
    return {
      doubleData: false, // 是否需要双份数据
      fontBox: null, // 文字容器
      topBox: null, // 文字父级容器
      countFlag: 0, // 切换clss类标识符
      dynamicCls: "", // 动态class类,切换使用解决数据更新后动画不重新开始问题
      dynamicSty: {},
    };
  },
  mounted() {
    this.fontBox = this.$refs.fontbox;
    this.topBox = this.$refs.topbox;
    this.$nextTick(() => {
      this.calcHeight();
    });
  },
  methods: {
    calcHeight() {
      // 判断内容是否超出容器,如果超出容器、追加一份数据
      if (this.fontBox.offsetHeight > this.topBox.offsetHeight) {
        this.doubleData = true;
        this.countFlag += 1;
        // 切换动画类名
        this.dynamicCls = `scroll-cls${this.countFlag % 2}`;
        // 动态计算动画时长
        this.dynamicSty = {
          animationDuration: `${
            (this.fontBox.offsetHeight / this.topBox.offsetHeight) *
            this.pageDuration
          }s`,
        };
      } else {
        this.doubleData = false;
        this.dynamicCls = "";
        this.dynamicSty = {
          animationDuration: "0s",
        };
      }
    },
  },
  watch: {
    // 监听内容变化
    content(val) {
      this.$nextTick(() => {
        this.calcHeight();
      });
    },
  },
};
</script>
<style scoped>
.font-wrap {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.scroll-cls0 {
  animation: translateY1 8s 0.5s linear infinite;
}
.scroll-cls1 {
  animation: translateY2 8s 0.5s linear infinite;
}
.font-list:hover {
  animation-play-state: paused;
}
@keyframes translateY1 {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(-50%);
  }
}
@keyframes translateY2 {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(-50%);
  }
}
</style>

 2. Component call

<!-- 基于CSS3-animation动画属性实现文字滚动效果-仅用于字幕滚动 -->
<template>
  <div class="container-wrapper">
    <!-- 滚动组件,如果需要给滚动组件文字添加样式,添加到container的div上即可 -->
    <div class="container">
      <!-- 组件接受两个参数,content:滚动的文字内容;pageDuration滚动一页需要的时间 -->
      <font-scroll :content="content" :pageDuration="10"></font-scroll>
    </div>
  </div>
</template>
<script>
import { datas1, datas2 } from "./data";
import FontScroll from "./FontScroll";
export default {
  components: { FontScroll },
  name: "FontScrollIndex",
  data() {
    return {
      content: datas1,
    };
  },
  mounted() {
    // 模拟数据发生变化
    setTimeout(() => {
      this.content = datas2;
    }, 10000);
  },
};
</script>
<style scoped>
.container-wrapper {
  display: flex;
  justify-content: space-around;
  width: 300px;
  height: 300px;
}
.container {
  width: 300px;
  height: 300px;
  padding: 10px;
  margin: 0 auto;
  outline: 1px dashed #ccc;
  font-size: 14px;
  line-height: 30px;
  text-indent: 2em;
}
</style>

3. The implementation of animation is a bit complicated, for example: the number of data lists is sometimes large and sometimes small, which requires dynamic calculation of animation duration; secondly, to achieve seamless scrolling, dynamic calculation of content height is required to determine whether to add data, double data or single data ; In addition, when the list data changes, the list animation restarts and special processing is required. But the advantages are also very obvious. There is no need for developers to pay attention to animation performance issues, the animation effect is smooth, and the whole css control only needs a small amount of javascript to control. The implementation method is widely used in actual development and is recommended.

3. css3-animation realizes the list scrolling effect (corresponding to the third picture)

1. Component packaging

<template>
  <div class="page-box">
    <!--
      为实现无缝滚动效果,需注意事项:
      1.内容容器(item-list)不留margin和padding
      2.数据列表项-li也不留margin和padding、紧挨排列
      3.将需要展示的内容放至class为item-content的标签下
    -->
    <ul :class="['item-list', dynamicCls]" :style="dynamicSty">
      <li v-for="(item, index) in itemList" :key="index">
        <div class="item-content" :style="{ 'background-color': item.bgColor }">
          {
   
   { item.content }}
        </div>
      </li>
    </ul>
  </div>
</template>
<script>
import { datas3, datas4 } from "./data";
export default {
  name: "PageScroll",
  data() {
    return {
      singleDuration: 2, // 每一条滚动多久
      pageSize: 4, // 每页显示几个
      itemList: [], // 渲染数据列表
      dbData: datas3, // 模拟接口数据
      countFlag: 0, // 切换clss类标识符
      dynamicCls: "", // 动态class类,切换使用解决数据更新后动画不重新开始问题
      dynamicSty: {}, // 动态样式
    };
  },
  mounted() {
    this.calcAttr();
    // 模拟接口数据更新
    setTimeout(() => {
      this.dbData = datas4;
      this.calcAttr();
    }, 10000);
  },
  methods: {
    // 动态计算动画相关属性
    calcAttr() {
      let length = this.dbData.length;
      // 如果大于pageSize,则启用动画
      if (length > this.pageSize) {
        this.countFlag += 1;
        this.itemList = [...this.dbData, ...this.dbData];
        // 切换动画类名
        this.dynamicCls = `scroll-box${this.countFlag % 2}`;
        // 动态设置样式-开启动画
        this.dynamicSty = {
          height: (100 / this.pageSize) * length * 2 + "%", // 动态计算容器高度
          "animation-iteration-count": "infinite",
          "animation-duration": `${length * this.singleDuration}s`,
        };
      } else {
        this.itemList = this.dbData;
        // 动态设置样式-关闭动画
        this.dynamicSty = {
          height: (100 / this.pageSize) * length + "%", // 动态计算容器高度
          "animation-iteration-count": "0",
          "animation-duration": "0s",
        };
      }
    },
  },
};
</script>
<style scoped>
.page-box {
  width: 300px;
  height: 300px;
  overflow: hidden;
}
.item-list {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
  animation-timing-function: linear;
}

.item-list:hover {
  animation-play-state: paused;
}

.item-list > li {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.item-content {
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 14px;
  color: #fff;
  width: 80%;
  height: 80%;
  border-radius: 6px;
}
.scroll-box0 {
  animation: box-move0 8s 0.5s linear infinite;
}
.scroll-box1 {
  animation: box-move1 8s 0.5s linear infinite;
}
@keyframes box-move0 {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(-50%);
  }
}
@keyframes box-move1 {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(-50%);
  }
}
</style>

 2. The implementation method of list scrolling-css3 is roughly the same as the implementation principle of css3-text version. For the specific implementation method, please refer to the above code and note text description. This method is implemented through the displacement of the list container and is suitable for most scenarios. If there are special requirements, such as: the list can be slid by the mouse, it needs to be controlled by javascript.

4. Javascript realizes the scrolling effect (corresponding to the fourth picture)

1. This implementation scheme is to add the mouse wheel sliding operation on the basis of "Case 3", and realize scrolling display by dynamically changing the scrollTop property. Compared with CSS control, this control method has stronger plasticity

2. The code is as follows:

<!-- 基于javaScript scrollTop实现滚动效果 -->
<template>
  <div class="container" @mouseenter="stopScroll" @mouseleave="startScroll">
    <!--
      为实现无缝滚动效果,需注意事项:
      1.内容容器(container-wrapper)不留margin和padding
      2.数据列表项-li也不留margin和padding、紧挨排列
      3.将需要展示的内容放至class为item-content的标签下
    -->
    <ul class="container-wrapper" ref="box">
      <li v-for="(item, index) in itemList" :key="index">
        <div class="item-content" :style="{ 'background-color': item.bgColor }">
          {
   
   { item.content }}
        </div>
      </li>
    </ul>
  </div>
</template>
<script>
import { datas3, datas4 } from "./data";
export default {
  data() {
    return {
      box: null, // 内容容器
      Timer: null, // 记录定时器id
      dbData: datas3, // 模拟接口数据
      itemList: [], // 渲染数据列表
      pageSize: 4, // 每页显示几个
    };
  },
  mounted() {
    this.box = this.$refs.box;
    this.calcAttr();
    // 模拟接口数据更新
    setTimeout(() => {
      this.dbData = datas4;
      this.calcAttr();
    }, 10000);
  },
  beforeDestroy() {
    // 组件销毁前务必关闭动画,释放资源
    this.stopScroll();
  },
  methods: {
    calcAttr() {
      // 关闭滚动动画
      this.stopScroll();
      // 动画重新开始
      this.box.parentNode.scrollTop = 0;
      // 根据数据长度及每页展示数量,动态计算容器高度
      let length = this.dbData.length;
      if (length > this.pageSize) {
        // 超过一页,需要给与双份数据
        this.box.style.height = `${(100 / this.pageSize) * length * 2}%`;
        this.itemList = [...this.dbData, ...this.dbData];
      } else {
        // 不超过一页,单份数据
        this.box.style.height = `${(100 / this.pageSize) * length}%`;
        this.itemList = this.dbData;
      }
      // 务必等待dom重新渲染之后执行滚动动画
      this.$nextTick(() => {
        this.startScroll();
      });
    },
    startScroll() {
      this.stopScroll();
      // 判断是否滚动了一半,如果超过一半,拉回去重新开始
      if (this.box.offsetHeight > this.box.parentNode.offsetHeight) {
        this.box.parentNode.scrollTop++;
        if (this.box.parentNode.scrollTop >= this.box.offsetHeight / 2) {
          this.box.parentNode.scrollTop = 0;
        }
        // 定时滚动
        this.Timer = requestAnimationFrame(this.startScroll);
      }
    },
    stopScroll() {
      if (this.Timer) {
        cancelAnimationFrame(this.Timer);
        this.Timer = null;
      }
    },
  },
};
</script>
<style scoped>
.container {
  width: 300px;
  height: 300px;
  outline: 1px solid #ccc;
  overflow: auto;
}
.container-wrapper {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  list-style: none;
}
.container-wrapper > li {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.item-content {
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 14px;
  color: #fff;
  width: 80%;
  height: 80%;
  border-radius: 6px;
}
</style>

Summary: Each implementation scheme has its own usage scenarios. You can choose the technology based on actual needs, and master the principle of scrolling animation implementation, and you can be handy in actual development.

Guess you like

Origin blog.csdn.net/qq_40289557/article/details/126270333