vue+原生js+element-ui实现轮播列表动画(无限轮播)

效果展示

实现思路

  1. 用element-ui中的el-row和el-col来构建列表,若无element-ui,也可用类似布局组件或直接用div元素实现
  2. 将滚动区域封装,使用绝对定位与相对定位的方式,并将超出滚动区域部分hidden隐藏
  3. 下述业务代码有每分钟刷新一次列表的需求,所以用两个interval,若纯轮播则一个interval即可
  4. 通过改变子元素相对于父元素的top值,并设置transition: all 1s;即可出现如图所示效果
  5. 当轮播结束后再次首尾相连,即可实现无限轮播

关键代码

流畅过渡

.table {
   position: absolute;
   transition: all 1s;
}

js动画与无限轮播

startAnimation() {
  let flag = 0;
  if (this.isInterval) {
    let top = 0;
    if (this.$refs.table) {
      this.$refs.table.style.top = top + "rem";
    }
    let tmp = JSON.parse(JSON.stringify(this.dataList));
    this.tableInterval = setInterval(() => {
    if (flag === this.step) {
       this.dataList = this.dataList.concat(tmp);
         flag = 0;
       } else {
         flag++;
       }
       top = top - 1.65;
       if (this.$refs.table) {
          this.$refs.table.style.top = top + "rem";
       }
     }, 3000);
}

实现代码(以实际使用为例)

<template>
  <div class="situation-risk-event-e">
    <qz-loading-animation v-if="isLoading"></qz-loading-animation>
    <div class="situation-risk-event" v-if="!isLoading">
      <div class="situation-risk-event__header">
        <el-row>
          <el-col :span="5"><span class="name">时间</span></el-col>
          <el-col :span="3"><span class="name">风险等级</span></el-col>
          <el-col :span="14"><span class="name">风险描述</span></el-col>
          <el-col :span="2"><span class="name">操作</span></el-col>
        </el-row>
      </div>
      <div class="situation-risk-event__content">
        <div class="table" ref="table">
          <el-row v-for="(row, index) in dataList" :key="index">
            <el-col :span="5"
              ><span class="text">{{ row.time }}</span></el-col
            >
            <el-col :span="3">
              <div :class="'level__' + row.level">
                {{ levelMap[row.level] }}
              </div></el-col
            >
            <el-col :span="14"
              ><div class="text">
                <risk-desc
                  :overviewInfo="row.desc"
                  :mode="true"
                ></risk-desc></div
            ></el-col>
            <el-col :span="2"
              ><qz-icon
                class="icon-chakan-lv"
                @click.native="
                  $linkTo({
                    path: PAGE_URL_EXCEPTION_DETAIL,
                    query: { type: row.policyId, riskId: row.riskInfoId },
                    type: '_blank',
                  })
                "
              ></qz-icon
            ></el-col>
          </el-row>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { PAGE_URL_EXCEPTION_DETAIL } from "@/constant/page-url-constants"
import { getRiskEvent } from "@/service/situation-service"
import { handleRiskDesc } from "@/utils/risk-desc";
import RiskDesc from "@/pages/risk/packages/risk-desc.vue"
export default {
  components: { RiskDesc },
  data() {
    return {
      PAGE_URL_EXCEPTION_DETAIL,
      isLoading: false,
      isInterval: false,
      getInterval: null,
      tableInterval: null,
      levelMap: {
        1: "低",
        2: "中",
        3: "高"
      },
      dataList: [],
      step: 0
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.init();
      this.getInterval = setInterval(() => {
        this.init();
      }, 60000);
    })
  },
  methods: {
    // 初始化
    async init() {
      this.isLoading = true;
      clearInterval(this.tableInterval);
      this.tableInterval = null;
      this.dataList = [];
      await getRiskEvent().then(res => {
        this.isInterval = res.data.length > 3 ? true : false;
        this.step = res.data.length - 3;
        res.data.forEach(item => {
          this.dataList.push({
            time: moment(item.firstTime).format('MM/DD HH:mm:ss'),
            level: item.level,
            desc: handleRiskDesc(false, item),
            riskInfoId: item.riskInfoId,
            policyId: item.policyId
          })
        });
      }).catch(() => {
        this.$message.error("获取风险事件数据失败")
      });
      this.isLoading = false;
      this.startAnimation();
    },
    // 开始动画
    startAnimation() {
      let flag = 0;
      if (this.isInterval) {
        let top = 0;
        if (this.$refs.table) {
          this.$refs.table.style.top = top + "rem";
        }
        let tmp = JSON.parse(JSON.stringify(this.dataList));
        this.tableInterval = setInterval(() => {
          if (flag === this.step) {
            this.dataList = this.dataList.concat(tmp);
            flag = 0;
          } else {
            flag++;
          }
          top = top - 1.65;
          if (this.$refs.table) {
            this.$refs.table.style.top = top + "rem";
          }
        }, 3000);
      }
    }
  },
  destroyed() {
    clearInterval(this.getInterval);
    this.getInterval = null;
    clearInterval(this.tableInterval);
    this.tableInterval = null;
  }
}
</script>

<style lang="less">
.level-style() {
  height: 0.9rem;
  width: 0.9rem;
  color: #000000;
  padding: 0 0.1rem;
  text-align: center;
  line-height: 1rem;
  font-size: 0.5rem;
  border-radius: 0.1rem;
  font-weight: 500;
}
.situation-risk-event {
  &-e {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  padding: 2.25rem 1.3rem 1rem;
  &__content {
    position: relative;
    height: 11vh;
    overflow: hidden;
    .table {
      position: absolute;
      transition: all 1s;
    }
  }
  .el-row {
    height: 1.65rem;
    padding: 0.3rem 0;
    border-bottom: 0.1rem solid #2b2f45;
  }
  &__header {
    .name {
      color: #fff;
      font-size: 0.7rem;
    }
  }
  i {
    color: #50c3d1;
    font-size: 0.7rem;
    cursor: pointer;
  }
  .level {
    &__1 {
      .level-style();
      background: #419f8a;
    }
    &__2 {
      .level-style();
      background: #f8c155;
    }
    &__3 {
      .level-style();
      background: #fa4f4d;
    }
  }
  .text {
    color: rgba(255, 255, 255, 0.6);
    padding-right: 1rem;
    font-size: 0.7rem;
  }
}
</style>

你可能感兴趣的:(Vue-cli,JS,CSS,javascript,vue.js,ui)