Vue3炫酷商品卡牌 组件设计

Vue3炫酷商品卡牌 组件设计

感谢来自BinaryMoon-CSS 艺术之暗系魔幻卡牌的博文。💕

演示

代码

接口类型

language-typescript
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
export interface CourseBaseVO {

/**
* 主键
*/
id: string | number;

/**
* 机构ID
*/
companyId: string | number;

/**
* 课程名称
*/
name: string;

/**
* 大分类
*/
mt: string;

/**
* 小分类
*/
st: string;

/**
* 课程图片
*/
pic: string;

/**
* 是否收费
*/
charge: boolean;

/**
* 原价
*/
originalPrice: number;

/**
* 现价
*/
price: number;

/**
* 评分
*/
star: number;

/**
* UNPUBLISHED(1, "未发布"), UNDER_REVIEW(2, "审核中"), REVIEW_FAILED(3, "审核不通过"), REVIEW_PASSED(4, "审核通过")
*/
status: number;

/**
* 审核意见
*/
mind: string;

}
interface CourseBaseExtraHotVo extends CourseBaseVO {

isHot: boolean;
}

外部资源wave_orange.svg

language-txt
1
<svg width="100%" height="100%" id="svg" viewBox="0 0 1440 490" xmlns="http://www.w3.org/2000/svg" class="transition duration-300 ease-in-out delay-150"><defs><linearGradient id="gradient" x1="0%" y1="51%" x2="100%" y2="49%"><stop offset="5%" stop-color="#fcb900"></stop><stop offset="95%" stop-color="#ff6900"></stop></linearGradient></defs><path d="M 0,500 L 0,0 C 90.96650717703349,54.02870813397129 181.93301435406698,108.05741626794259 268,115 C 354.066985645933,121.94258373205741 435.23444976076553,81.79904306220095 535,84 C 634.7655502392345,86.20095693779905 753.129186602871,130.7464114832536 867,132 C 980.870813397129,133.2535885167464 1090.248803827751,91.2153110047847 1185,62 C 1279.751196172249,32.78468899521531 1359.8755980861245,16.392344497607656 1440,0 L 1440,500 L 0,500 Z" stroke="none" stroke-width="0" fill="url(#gradient)" fill-opacity="0.53" class="transition-all duration-300 ease-in-out delay-150 path-0"></path><defs><linearGradient id="gradient" x1="0%" y1="51%" x2="100%" y2="49%"><stop offset="5%" stop-color="#fcb900"></stop><stop offset="95%" stop-color="#ff6900"></stop></linearGradient></defs><path d="M 0,500 L 0,0 C 111.98086124401911,108.89952153110048 223.96172248803822,217.79904306220095 335,271 C 446.0382775119618,324.20095693779905 556.133971291866,321.7033492822967 626,309 C 695.866028708134,296.2966507177033 725.5023923444976,273.3875598086125 820,274 C 914.4976076555024,274.6124401913875 1073.8564593301435,298.7464114832536 1188,257 C 1302.1435406698565,215.25358851674642 1371.0717703349283,107.62679425837321 1440,0 L 1440,500 L 0,500 Z" stroke="none" stroke-width="0" fill="url(#gradient)" fill-opacity="1" class="transition-all duration-300 ease-in-out delay-150 path-1"></path></svg>

组件源码

language-html
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
<template>
<div
id="card"
style="padding: 5px;margin: 20px"
>
<el-card
shadow="hover"
style="width: 350px; border-radius: 10px"
>
<div class="wave-orange-card"></div>
<div style="display: flex;flex-direction: column;justify-content: center;align-items: center;">
<el-image
:src="fileBaseUrl+courseBase.pic"
fit="fill"
style="width: 200px"
/>
</div>
<div style="height: 30px; font-size: 20px;margin-top: 10px">
<span>{
{ courseBase.name }}</span>
</div>
<div style="height: 40px; ">
<el-rate
v-model="courseBase.star"
size="large"
show-score
text-color="#ff9900"
:score-template="courseBase.star.toString() + 'points'"
disabled
/>
</div>
<div style="height: 40px; ">
<el-tag v-if="courseBase.charge" type="warning" size="large" effect="light">
<span style="font-size: 20px;font-weight: bold">¥{
{ courseBase.price }}</span>
&nbsp;&nbsp;
<span class="slash-deleted-text" style="font-size: 14px;color: #909399">{
{ courseBase.originalPrice }}</span>
</el-tag>
<el-tag v-else type="success" size="large"><span style="font-size: 20px">免费</span></el-tag>
<span>&nbsp;&nbsp; 6w人报名</span>
</div>
</el-card>
</div>
</template>

<script lang="ts" setup>

import {
CourseBaseVO} from "@/api/course/types";
import {
PropType} from "vue";

const fileBaseUrl = import.meta.env.VITE_APP_MINIO_FILE_URL;
interface CourseBaseExtraHotVo extends CourseBaseVO {

isHot: boolean;
}
const props = defineProps({

courseBase: Object as PropType<CourseBaseExtraHotVo>,
});
const emit = defineEmits(['update:courseBase'])


</script>

<style scoped>

/* 卡片图片背景 */
:deep(.wave-orange-card){

background-image: url("src/assets/svg/wave_orange.svg");
background-repeat: no-repeat;
background-size: cover; /* 或使用 100% 100% 来确保完全覆盖 */
background-position: center; /* 根据需要调整 */
overflow: hidden; /* 避免内容溢出 */
position: absolute; /* 固定定位,不随滚动条移动 */
width: 310px; /* card的宽度为350 */
height: 200px; /*pic的大小为200*200*/
opacity: 0.6;
}

/* 删除线 */
:deep(.slash-deleted-text) {

position: relative;
overflow: hidden; /* 防止斜线溢出容器 */
}

:deep(.slash-deleted-text::after) {

content: '';
position: absolute;
left: 0;
top: 10%; /* 调整为文本高度的一半 */
width: 100%; /* 与容器同宽 */
border-bottom: 1px solid #F56C6C; /* 删除线的样式 */
transform: rotate(25deg); /* 调整角度为倾斜 */
transform-origin: left bottom;
}

/* 卡片背景 */
:deep(:root) {

--margin: 100px;
/* 上演一出黄金分割率的好戏 */
--card-width: 360px;
/* 上演一出黄金分割率的好戏 */
--card-height: calc(var(--card-height) * 1.618);
}

#card{

width: var(--card-width);
height: var(--card-height);
position: relative;
cursor: pointer;
transition: transform 0.4s ease; /* 设置放大动画的过渡效果为2秒 */
}

/* 定义自定义属性 --rotate */
@property --rotate{

/* 自定义属性的默认值 */
initial-value: 90deg;
/*
定义自定义属性允许的语法结构,
此处定义该元素仅接受角度值。
*/
syntax: '<angle>';
/* 定义该自定义属性是否允许被其他元素所继承 */
inherits: false;
}

/* 定义动画 */
@keyframes edge{

from{

--rotate: 0deg;
}
to{

--rotate: 360deg;
}
}

#card::before{

content: '';
width: 104%;
height: 102%;
background: linear-gradient(var(--rotate),
rgb(44, 251, 255), rgb(81, 154, 255), rgb(97, 57, 242));
position: absolute;
z-index: -1;
top: -1%;
left: -2%;
/* 设置边框圆角半径 */
border-radius: 0.5vw;
/*
为当前元素指定使用的动画,并将该动画的
持续时间设置为 3.5s,动画速度保持不变,
动画播放次数为无限次。
*/
animation: edge 10s linear infinite;
}

#card::after{

content: '';
width: 80%;
height: 100%;
background: linear-gradient(var(--rotate),
rgb(44, 251, 255), rgb(81, 154, 255), rgb(97, 57, 242));
position: absolute;
top: 5%;
left: 10%;
filter: blur(2vw);
z-index: -1;
/* 使用动画 */
animation: edge 3.5s linear infinite;
}

/* 卡片悬浮变化背景 */
#card:hover {

transform: scale(1.02); /* 鼠标悬浮时放大到1.1倍 */
}

#card::before, #card::after {

transition: background 1s ease; /* 将过渡应用于background,确保背景渐变的平滑变化 */
}

#card:hover::before, #card:hover::after {

background: linear-gradient(var(--rotate), #f82747, #fc5c7c, #ffc3d3); /* 渐变为淡红色 */
}

</style>

使用示例

language-html
1
2
3
4
5
6
7
8
9
<template>
<CourseCard
v-for="(course, index) in hotList"
:course-base="course"
/>
</template>
<script lang="ts" setup>
const hotList = ref<CourseBaseExtraHotVo[]>([]);
</script>
作者

Xiamu

发布于

2024-04-22

更新于

2024-08-11

许可协议

评论