js堆叠式卡片轮播图效果 vue
参考链接: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);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通