# 内聚旋转效果轮播图
1
2
3
4
5
很多灵感来自:ElementUI-Carousel
比如: main 组件和 item 组件之间绑定、z-index 轮播图层叠展示等。。
# 思路
- 由 item 自己控制在哪个位置显示,通过 translateX 移动位置,scale 放大缩小,使用户能够聚焦于激活的 item。
- 比如 activeIndex 激活的项目,处于正中间,z-index 为 3 最大,translateX 为 0,scale 为 1。默认所有 item 都处于正中间,通过 flex 布局居中。
- 每个 item 都需要判断当前 index 和 activeIndex 所处位置, 如果为 -1 则表示应该移动到 activeItem 的左边。如果为 1 则表示应该移动到 activeItem 的右边。否者移动到正中间隐藏,z-index 为 1,scale 为 0.83。
# 代码例子
# 父组件
<template>
<div class="carousel">
<div class="wrapper">
<slot></slot>
</div>
<div class="action">
<button @click="prev">prev</button>
<button @click="next">next</button>
</div>
</div>
</template>
<script>
export default {
name: 'Carousel',
props: {
itemWidth: {
type: String,
default: '400px',
},
itemHeight: {
type: String,
default: '200px',
},
},
provide() {
return {
width: this.itemWidth,
height: this.itemHeight,
};
},
data() {
return {
activeIndex: 0,
items: [],
};
},
methods: {
updatePositions() {
this.items.forEach((child) => child.updatePosition());
},
prev() {
this.activeIndex = this.decreaseIndex(
this.activeIndex,
this.items.length
);
this.updatePositions();
},
next() {
this.activeIndex = this.increaseIndex(
this.activeIndex,
this.items.length
);
this.updatePositions();
},
increaseIndex(index, max) {
return (index + 1) % max;
},
decreaseIndex(index, max) {
const sw = index % max;
const rever = (max - sw) % max;
return (max - rever - 1) % max;
},
updateItems() {
this.items = this.$children.filter(
(comp) => comp.$options.name === 'CarouselItem'
);
},
},
mounted() {
this.updateItems();
this.updatePositions();
},
updated() {
this.updateItems();
this.updatePositions();
},
};
</script>
<style scoped>
.carousel {
position: relative;
}
.wrapper {
width: 100%;
height: 100%;
position: relative;
border: 1px solid red;
display: flex;
justify-content: center;
align-items: center;
}
.action {
position: absolute;
width: 100%;
display: flex;
justify-content: center;
}
</style>
# 子组件
<template>
<div
class="carousel-item"
ref="itemRef"
:style="{ width: this.width, height: this.height }"
>
<slot></slot>
</div>
</template>
<script>
export default {
name: 'CarouselItem',
inject: ['height', 'width'],
methods: {
updatePosition() {
const [direct, distance] = this.calcTranslate();
switch (direct) {
case 0:
this.setTranslate(0, 1, 3);
break;
case -1:
this.setTranslate(-distance, 0.83, 2);
break;
case 1:
this.setTranslate(distance, 0.83, 2);
break;
default:
this.setTranslate(0, 0.83, 1);
break;
}
},
calcTranslate() {
const activeIndex = this.$parent.activeIndex;
const index = this.$parent.items.indexOf(this);
const length = this.$parent.items.length;
const selfWidth = this.$refs.itemRef.offsetWidth;
const containerWidth = this.$parent.$el.offsetWidth;
const direct = this.calcDirection(activeIndex, index, length);
const distance = Math.round(containerWidth / 2 - selfWidth / 2);
return [direct, distance];
},
calcDirection(activeIndex, index, length) {
if (activeIndex === index) {
return 0;
} else if (this.increaseIndex(activeIndex, length) === index) {
return 1;
} else if (this.decreaseIndex(activeIndex, length) === index) {
return -1;
} else {
return 2;
}
},
setTranslate(distance, scale, zIndex) {
this.$refs.itemRef.style.transform = `translateX(${distance}px) scale(${scale})`;
this.$refs.itemRef.style['z-index'] = zIndex;
},
increaseIndex(index, max) {
return (index + 1) % max;
},
decreaseIndex(index, max) {
const sw = index % max;
const rever = (max - sw) % max;
return (max - rever - 1) % max;
},
},
created() {
this.$parent && this.$parent.updatePositions();
},
};
</script>
<style scoped>
.carousel-item {
position: absolute;
transition: all 0.5s;
cursor: pointer;
}
</style>