js堆叠式卡片轮播图效果 vue

发布时间 2023-06-26 14:55:00作者: 如意酱

参考链接:https://www.jq22.com/jquery-info24050

说明:此组件为渲染曲线图的卡片堆叠轮播图,可以渲染不止5个,数量自己决定。核心代码js文件在后面。个人感觉这个的过渡效果最自然,最丝滑~

效果如图:

 

html部分:(此代码用到了之前写的折线图组件,这里不再赘述了)

 <div class="stacked-cards stacked-cards-slide">
     <ul>
         <li v-for="item in scrollList" :key="item.id">
             <div class="echarts-wrap">
                 <echartLine
                      :ref="'scrollEchart' + item.id"
                      :xAxis="timeList"
                      :config="scrollConfig"
                      :dataList="item.dataList"
                      :smooth="false"
                  ></echartLine>
             </div>
         </li>
     </ul>
</div>

引用js:

import stackedCards from '@/assets/js/stackedCards.min.js';
 
data中:
查看代码
 data() {
    return {
      //卡片x轴
      timeList: ['14:30', '14:45', '15:15', '15:30', '15:45', '16:00', '16:15'],
      //轮播数据
      scrollList: [
        {
          id: 1,
          title: 'PLC设备',
          dataList: [
            [4, 7, 11, 14, 19, 24, 28, 30],
            [36, 30, 32, 36, 39, 32, 39, 35],
            [60, 62, 61, 64, 66, 64, 70, 71],
            [84, 87, 81, 84, 89, 84, 88, 80]
          ]
        },
        {
          id: 2,
          title: 'OPC-UA设备',
          dataList: [
            [4, 17, 11, 14, 19, 24, 20, 24],
            [35, 44, 55, 50, 49, 44, 40, 41],
            [10, 24, 21, 34, 29, 34, 38, 21],
            [84, 87, 81, 84, 89, 84, 88, 80]
          ]
        },
        {
          id: 3,
          title: '仪器仪表',
          img: require('@/assets/img/front/card-yiqi.png'),
          dataList: [
            [32, 34, 35, 44, 39, 44, 38, 39],
            [20, 24, 21, 24, 29, 27, 28, 31],
            [6, 10, 12, 16, 19, 21, 20, 18],
            [4, 7, 8, 9, 10, 11, 12, 10]
          ]
        },
        {
          id: 4,
          title: '物联网关',
          dataList: [
            [4, 7, 6, 4, 9, 10, 8, 10],
            [11, 10, 12, 16, 19, 22, 20, 25],
            [34, 27, 31, 24, 29, 34, 28, 10],
            [20, 24, 21, 24, 25, 30, 33, 36]
          ]
        },
        {
          id: 5,
          title: '物联网关',
          dataList: [
            [4, 7, 6, 4, 9, 10, 8, 10],
            [94, 90, 88, 80, 79, 74, 68, 60],
            [11, 10, 12, 16, 19, 22, 20, 25],
            [20, 24, 21, 24, 25, 30, 33, 36]
          ]
        }
      ],
      scrollConfig: {
        text: ['宕机次数', '繁忙度', '健康度', '可用性'],
        color: ['#14BD48', '#F1A751', '#2B80FF', '#04CDF4']
      },
    };
  },
在mounted中调用轮播初始化方法:
 methods: {
    //轮播图初始化
    scrollInit() {
      var stackedCardSlide = new stackedCards.stackedCards({
        selector: '.stacked-cards-slide',
        layout: 'slide',
        transformOrigin: 'center'
      });
      stackedCardSlide.init();
    },
    // 采集设备健康数据分析
    getScroll() {
      this.$nextTick(() => {
        this.scrollList.forEach(val => {
          this.$refs['scrollEchart' + val.id][0].getData();
        });
      });
    }
  }
 

样式:

 /* js堆叠卡片轮播图插件stackedCards */
.stacked-cards {
    position: relative;
    padding-top: 40px;
    padding-bottom: 15px;
    ul {
        position: relative;
        max-width: 60%;
        margin: 0 auto;
        padding-left: 0;
        li {
            width: 65%;
            height: 300px;
            margin-top: 5px;
            padding: 10px;
            box-sizing: border-box;
            cursor: pointer;
            border-radius: 8px;
            box-shadow: 0 3px 6px 0px #022b59;
            list-style: none;
            background: linear-gradient(0deg, #022b59, #2d5c91);
            position: absolute;
            left: 50%;
            transition: 0.5s ease transform;

            span {
                width: 100px;
                height: 35px;
                display: block;
                margin: 0 auto;

                font-size: 19px;
                font-family: PingFang SC;
                font-weight: 600;
                color: #ffffff;
                line-height: 35px;
                text-align: center;
                text-shadow: 0 0 5px #009cff, 0 0 5px #009cff;
            }
            .echarts-wrap {
                width: 100%;
                padding: 0 10px 0 5px;
                box-sizing: border-box;
                height: calc(100% - 35px);
            }
        }
       
        li:after {
            bottom: 0;
            content: '';
            left: 0;
            position: absolute;
            right: 0;
            top: 0;
        }

        li.active:after {
            display: none;
        }
        li img {
            position: relative;
            display: block;
            max-width: 100%;
            height: auto;
            z-index: 4;
        }

        li.active {
            cursor: default;
            box-shadow: 0 0 10px 0px rgb(23, 23, 37);
            transition: 0.5s ease transform;
        }
    }
}

 核心代码js文件

/* js堆叠卡片轮播图插件stackedCards */
(function () {
  bind = function (fn, me) {
    return function () {
      return fn.apply(me, arguments)
    }
  };
  this.stackedCards = function () {
    stackedCards.prototype.defaults = {
      layout: "slide",
      onClick: undefined,
      transformOrigin: "center"
    };

    function stackedCards(options) {
      if (options == null) {
        options = {}
      }
      this.draw = bind(this.draw, this);
      this.config = this.extend(options, this.defaults)
    }
    stackedCards.prototype.init = function () {
      this.element = window.document.documentElement;
      if ((ref = document.readyState) === "interactive" || ref === "complete") {
        this.draw()
      } else {
        document.addEventListener("DOMContentLoaded", this.draw)
      }
    };
    stackedCards.prototype.draw = function () {
      var me = this;
      var selector = this.config.selector;
      this.els = document.querySelectorAll(selector + " li");
      var els = this.els;
      this.parent = els[0].parentNode;
      var getItemHeight = els[0].getBoundingClientRect().height;
      els[0].parentNode.style.height = parseInt(getItemHeight) + "px";
      var lenAdjust = els.length % 2 == 0 ? -2 : -1;
      var oneHalf = (els.length + lenAdjust) / 2;
      var activeTransform = "translate(" + -50 + "%, 0%)  scale(1)";
      this.detectSwipe();
      Array.prototype.forEach.call(els, function (el) {
        el.style.transformOrigin = me.config.transformOrigin;
        el.addEventListener("click", function () {
          var clickedEl = el;
          var nextCnt = 0;
          var prevCnt = 0;
          do {
            var next = clickedEl.nextElementSibling;
            nextCnt = nextCnt + 1
          } while (clickedEl = clickedEl.nextElementSibling);
          clickedEl = el;
          do {
            var prev = clickedEl.previousElementSibling;
            prevCnt = prevCnt + 1
          } while (clickedEl = clickedEl.previousElementSibling);
          me.reCalculateTransformsOnClick(nextCnt - 1, prevCnt - 1);
          me.loopNodeList(els, function (el) {
            el.classList.remove("active")
          });
          el.classList.add("active");
          el.classList.add(me.config.layout);
          el.style.zIndex = els.length * 5;
          el.style.transform = activeTransform;
          if (me.config.onClick !== undefined) {
            me.config.onClick(el)
          }
        })
      });
      els[oneHalf].click()
    };
    stackedCards.prototype.reCalculateTransformsOnClick = function (nextCnt, prevCnt) {
      var z = 10;
      var els = this.nodelistToArray(this.els);
      var scale = 1,
        translateX = 0,
        rotateVal = 0,
        rotate = "";
      var rotateNegStart = 0;
      var transformArr = [];
      var zIndexArr = [];
      var relArr = [];
      var layout = this.config.layout;
      var maxCntDivisor = Math.max(prevCnt, nextCnt);
      var prevDivisor = 100 / maxCntDivisor;
      var nextDivisor = 100 / maxCntDivisor;
      if (prevCnt > nextCnt) {
        scale = 0 + 100 / (prevCnt + 1) / 100
      } else {
        scale = 1 - prevCnt * (1 / (nextCnt + 1))
      }
      var rotatePrevStart = prevCnt * 10 / prevCnt * prevCnt * -1;
      var rotateNextStart = nextCnt * 10 / nextCnt;
      for (var i = 0; i < prevCnt; i++) {
        switch (layout) {
          case "slide":
            if (i > 0) {
              scale = scale + 100 / (maxCntDivisor + 1) / 100
            }
            translateX = -50 - prevDivisor * (prevCnt - i);
            rotate = "rotate(0deg)";
            break;
          case "fanOut":
            rotateVal = rotatePrevStart;
            if (i > 0) {
              scale = scale + 100 / (maxCntDivisor + 1) / 100
            }
            translateX = -50 - prevDivisor * (prevCnt - i);
            rotate = "rotate(" + rotateVal + "deg)";
            rotatePrevStart = rotatePrevStart + prevCnt * 10 / prevCnt;
            break;
          default:
            translateX = (150 - prevDivisor * 2 * i) * -1;
            rotate = "rotate(0deg)"
        }
        var styleStr = "translate(" + translateX + "%, 0%)  scale(" + scale + ") " + rotate;
        z = z + 1;
        els[i].style.transform = styleStr;
        els[i].style.zIndex = z
      }
      z = z - 1;
      var j = 0;
      rotateNegStart = 0;
      scale = 1;
      for (var i = prevCnt + 1; i < nextCnt + prevCnt + 1; i++) {
        j = j + 1;
        switch (layout) {
          case "slide":
            scale = scale - 100 / (maxCntDivisor + 1) / 100;
            translateX = (50 - nextDivisor * j) * -1;
            rotate = "rotate(0deg)";
            break;
          case "fanOut":
            rotateVal = rotateNextStart;
            scale = scale - 100 / (maxCntDivisor + 1) / 100;
            translateX = (50 - nextDivisor * j) * -1;
            rotate = "rotate(" + rotateVal + "deg)";
            rotateNextStart = rotateNextStart + nextCnt * 10 / nextCnt;
            break;
          default:
            translateX = (50 - prevDivisor * 2 * i) * -1;
            rotate = "rotate(0deg)"
        }
        z = z - 1;
        var styleStr = "translate(" + translateX + "%, 0%)  scale(" + scale + ") " + rotate;
        els[i].style.transform = styleStr;
        els[i].style.zIndex = z
      }
    };
    stackedCards.prototype.detectSwipe = function () {
      var me = this;
      var regionEl = document.querySelector(me.config.selector);
      me.detectSwipeDir(regionEl, function (swipedir) {
        var activeEl = document.querySelector(me.config.selector + " li.active");
        if (swipedir == "left") {
          activeEl.nextElementSibling.click()
        } else if (swipedir == "right") {
          activeEl.previousElementSibling.click()
        }
      })
    };
    stackedCards.prototype.extend = function (custom, defaults) {
      var key, value;
      for (key in defaults) {
        value = defaults[key];
        if (custom[key] == null) {
          custom[key] = value
        }
      }
      return custom
    };
    stackedCards.prototype.nodelistToArray = function (nodelist) {
      var results = [];
      var i, element;
      for (i = 0; i < nodelist.length; i++) {
        element = nodelist[i];
        results.push(element)
      }
      return results
    };
    stackedCards.prototype.loopNodeList = function (els, callback, scope) {
      for (var i = 0; i < els.length; i++) {
        callback.call(scope, els[i])
      }
    };
    stackedCards.prototype.scrolledIn = function (el, offset) {
      if (typeof el == "undefined") return;
      var elemTop = el.getBoundingClientRect().top;
      var elemBottom = el.getBoundingClientRect().bottom;
      var scrolledInEl = elemTop >= 0 && elemTop <= window.innerHeight;
      return scrolledInEl
    };
    stackedCards.prototype.detectSwipeDir = function (el, callback) {
      var touchsurface = el,
        swipedir, startX, startY, distX, distY, threshold = 75,
        restraint = 100,
        allowedTime = 300,
        elapsedTime, startTime, handleswipe = callback || function (swipedir) {};
      touchsurface.addEventListener("touchstart", function (e) {
        var touchobj = e.changedTouches[0];
        swipedir = "none";
        dist = 0;
        startX = touchobj.pageX;
        startY = touchobj.pageY;
        startTime = (new Date).getTime();
        e.preventDefault()
      }, false);
      touchsurface.addEventListener("touchmove", function (e) {}, false);
      touchsurface.addEventListener("touchend", function (e) {
        var touchobj = e.changedTouches[0];
        distX = touchobj.pageX - startX;
        distY = touchobj.pageY - startY;
        elapsedTime = (new Date).getTime() - startTime;
        if (elapsedTime <= allowedTime) {
          if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint) {
            swipedir = distX < 0 ? "left" : "right"
          } else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint) {
            swipedir = distY < 0 ? "up" : "down"
          }
        }
        handleswipe(swipedir);
        e.preventDefault()
      }, false)
    };
    return stackedCards
  }()
}).call(this);
View Code