Appearance
轮播图
走马灯
对应关键代码
vue
<template>
<div class="container">
<div class="carousel" :style="{ transform: `translateX(-${activeIndex}00%)` }">
<div class="item" v-for="item in imgList" :key="item">
<a href=""><img :src="`https://picsum.photos/id/${item}/500/300/`" alt=""></a>
</div>
</div>
<div class="indicator">
<span :class="index === activeIndex ? 'active' : ''" v-for="(item, index) in imgList" :key="item"
@click="() => onIndicatorClick(index)"></span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const imgList = ref<any[]>(Array.from({ length: 4 }, (_, index) => `${index + 10}`))
const activeIndex = ref(0)
const onIndicatorClick = (idx: number) => {
activeIndex.value = idx
}
</script>
<style scoped lang="less">
.container {
width: 500px;
height: 300px;
margin: 10px auto;
overflow: hidden;
position: relative;
.carousel {
width: 100%;
height: 100%;
display: flex;
transition: all 0.5s;
img {
width: 500px;
height: 300px;
max-width: unset;
}
}
.indicator {
position: absolute;
bottom: 10px;
display: flex;
left: 50%;
transform: translateX(-50%);
span {
width: 16px;
height: 16px;
border: 2px solid #ccc;
border-radius: 50%;
margin: 0 4px;
&.active {
border-color: white;
background-color: white;
}
}
}
}
</style>
<template>
<div class="container">
<div class="carousel" :style="{ transform: `translateX(-${activeIndex}00%)` }">
<div class="item" v-for="item in imgList" :key="item">
<a href=""><img :src="`https://picsum.photos/id/${item}/500/300/`" alt=""></a>
</div>
</div>
<div class="indicator">
<span :class="index === activeIndex ? 'active' : ''" v-for="(item, index) in imgList" :key="item"
@click="() => onIndicatorClick(index)"></span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const imgList = ref<any[]>(Array.from({ length: 4 }, (_, index) => `${index + 10}`))
const activeIndex = ref(0)
const onIndicatorClick = (idx: number) => {
activeIndex.value = idx
}
</script>
<style scoped lang="less">
.container {
width: 500px;
height: 300px;
margin: 10px auto;
overflow: hidden;
position: relative;
.carousel {
width: 100%;
height: 100%;
display: flex;
transition: all 0.5s;
img {
width: 500px;
height: 300px;
max-width: unset;
}
}
.indicator {
position: absolute;
bottom: 10px;
display: flex;
left: 50%;
transform: translateX(-50%);
span {
width: 16px;
height: 16px;
border: 2px solid #ccc;
border-radius: 50%;
margin: 0 4px;
&.active {
border-color: white;
background-color: white;
}
}
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
3D渐变的轮播图
く
く
对应关键代码
vue
<template>
<div class="wrapper">
<div class="carousel">
<div class="indicator prev" @click="() => onIndicatorClick('prev')">く</div>
<div class="carousel_list">
<img class="carousel_item" v-for="(item, index) in imgList" :key="item"
:src="`https://picsum.photos/id/${item}/300/200`" :style="carouselItemStyle(index)" />
</div>
<div class="indicator next" @click="() => onIndicatorClick('next')">く</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, StyleValue } from 'vue'
const imgList = ref<any[]>(Array.from({ length: 7 }, (_, index) => `${index + 10}`))
// 当前轮播图index
const activeIndex = ref(3)
const carouselItemStyle = computed(() => {
// 每张轮播图之间的间隙
const xOffsetStep = 80
// 缩放的递减倍率
const scaleStep = 0.8
// 透明度的递减倍率
const opacityStep = 0.9
// transform: translateX(-200px) scale(0.6) rotateY(-45deg);
return (idx: number): StyleValue => {
// 倍数
const multiple = Math.abs(activeIndex.value - idx)
// 符号
const sign = Math.sign(idx - activeIndex.value)
let xOffset = (idx - activeIndex.value) * xOffsetStep
// 紧挨着当前图片的图片太紧凑,给个基础距离
if (idx !== activeIndex.value) {
xOffset = xOffset + 60 * sign
}
const scale = scaleStep ** multiple
const rotateY = idx === activeIndex.value ? 0 : 45 * -sign
return {
transform: `translateX(${xOffset}px) scale(${scale}) rotateY(${rotateY}deg)`,
zIndex: imgList.value.length - multiple,
opacity: opacityStep ** multiple
}
}
})
const onIndicatorClick = (type: "prev" | "next") => {
if (type === 'prev' && activeIndex.value > 0) {
activeIndex.value = activeIndex.value - 1
}
if (type === 'next' && activeIndex.value < imgList.value.length - 1) {
activeIndex.value = activeIndex.value + 1
}
}
</script>
<style scoped lang="less">
.wrapper {
background-color: black;
padding: 48px 0;
.carousel {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
.carousel_list {
width: 300px;
height: 200px;
position: relative;
.carousel_item {
position: absolute;
transition: transform 0.5s;
max-width: unset;
height: unset;
}
}
.indicator {
font-size: 36px;
color: #dadada;
font-weight: bold;
cursor: pointer;
z-index: 999;
line-height: 36px;
padding: 12px 0;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 2px;
user-select: none;
&.prev {
margin-left: 24px;
}
&.next {
margin-right: 24px;
transform: rotateY(180deg);
}
}
}
}
</style>
<template>
<div class="wrapper">
<div class="carousel">
<div class="indicator prev" @click="() => onIndicatorClick('prev')">く</div>
<div class="carousel_list">
<img class="carousel_item" v-for="(item, index) in imgList" :key="item"
:src="`https://picsum.photos/id/${item}/300/200`" :style="carouselItemStyle(index)" />
</div>
<div class="indicator next" @click="() => onIndicatorClick('next')">く</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, StyleValue } from 'vue'
const imgList = ref<any[]>(Array.from({ length: 7 }, (_, index) => `${index + 10}`))
// 当前轮播图index
const activeIndex = ref(3)
const carouselItemStyle = computed(() => {
// 每张轮播图之间的间隙
const xOffsetStep = 80
// 缩放的递减倍率
const scaleStep = 0.8
// 透明度的递减倍率
const opacityStep = 0.9
// transform: translateX(-200px) scale(0.6) rotateY(-45deg);
return (idx: number): StyleValue => {
// 倍数
const multiple = Math.abs(activeIndex.value - idx)
// 符号
const sign = Math.sign(idx - activeIndex.value)
let xOffset = (idx - activeIndex.value) * xOffsetStep
// 紧挨着当前图片的图片太紧凑,给个基础距离
if (idx !== activeIndex.value) {
xOffset = xOffset + 60 * sign
}
const scale = scaleStep ** multiple
const rotateY = idx === activeIndex.value ? 0 : 45 * -sign
return {
transform: `translateX(${xOffset}px) scale(${scale}) rotateY(${rotateY}deg)`,
zIndex: imgList.value.length - multiple,
opacity: opacityStep ** multiple
}
}
})
const onIndicatorClick = (type: "prev" | "next") => {
if (type === 'prev' && activeIndex.value > 0) {
activeIndex.value = activeIndex.value - 1
}
if (type === 'next' && activeIndex.value < imgList.value.length - 1) {
activeIndex.value = activeIndex.value + 1
}
}
</script>
<style scoped lang="less">
.wrapper {
background-color: black;
padding: 48px 0;
.carousel {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
.carousel_list {
width: 300px;
height: 200px;
position: relative;
.carousel_item {
position: absolute;
transition: transform 0.5s;
max-width: unset;
height: unset;
}
}
.indicator {
font-size: 36px;
color: #dadada;
font-weight: bold;
cursor: pointer;
z-index: 999;
line-height: 36px;
padding: 12px 0;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 2px;
user-select: none;
&.prev {
margin-left: 24px;
}
&.next {
margin-right: 24px;
transform: rotateY(180deg);
}
}
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109