Style 内置样式 - Vant 4
内置样式
介绍
🎨 样式魔法师的工具箱 ✨
Vant 就像一位贴心的样式魔法师,为你精心准备了一套实用的样式工具箱!这些内置样式就像是预制的魔法咒语,只需轻轻一挥 className 的魔法棒,就能瞬间为你的界面增添美丽的效果。
无需繁琐的 CSS 编写,无需复杂的样式调试,这些经过精心打磨的样式类就像是你的得力助手,随时待命为你的项目增光添彩! 🌟
📝 文字省略 - 优雅的空间艺术家
✂️ 智能文本裁剪师,让长文本优雅"瘦身"
当文本内容像脱缰的野马一样超出容器边界时,这位贴心的空间艺术家会优雅地出手,用精美的省略号为过长的文字画上完美的句号。无论是一行、两行还是三行,都能完美掌控,让你的界面始终保持整洁美观的视觉效果! 🎭
这是一段最多显示一行的文字,多余的内容会被省略 这是一段最多显示两行的文字,多余的内容会被省略 这是一段最多显示三行的文字,多余的内容会被省略🌟 最佳实践
样式类组合使用
<!-- 组合使用多个样式类 -->
<div class="van-ellipsis van-haptic-feedback">
这是一个既有文字省略又有触碰反馈的元素
</div>
<!-- 安全区 + 1px边框的完美组合 -->
<div class="van-safe-area-bottom van-hairline--top">
底部安全区域内容
</div>
<!-- 多行省略 + 触碰反馈 -->
<p class="van-multi-ellipsis--l2 van-haptic-feedback">
这是一段很长的文字内容,会被限制在两行内显示,超出部分用省略号表示,同时具有触碰反馈效果
</p>响应式样式应用
// 动态应用样式类
const applyResponsiveStyles = (element, isMobile) => {
if (isMobile) {
element.classList.add('van-safe-area-bottom');
element.classList.add('van-haptic-feedback');
} else {
element.classList.remove('van-safe-area-bottom');
element.classList.add('van-ellipsis');
}
};
// 根据屏幕尺寸动态调整
const handleResize = () => {
const isMobile = window.innerWidth <= 768;
const elements = document.querySelectorAll('.responsive-element');
elements.forEach(el => applyResponsiveStyles(el, isMobile));
};
window.addEventListener('resize', handleResize);性能优化建议
/* 自定义样式时保持一致性 */
.custom-ellipsis {
/* 继承 Vant 的省略样式 */
@extend .van-ellipsis;
/* 添加自定义效果 */
color: var(--van-primary-color);
font-weight: 500;
}
/* 避免样式冲突 */
.my-component {
/* 使用 CSS 变量保持主题一致性 */
--van-text-color: #333;
--van-border-color: #eee;
}
/* 优化动画性能 */
.smooth-transition {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform, opacity;
}💡 使用技巧
智能省略文本处理
// 动态计算最适合的省略行数
const calculateOptimalLines = (element, maxHeight) => {
const lineHeight = parseInt(getComputedStyle(element).lineHeight);
const maxLines = Math.floor(maxHeight / lineHeight);
// 应用对应的省略样式
const ellipsisClass = maxLines === 1 ? 'van-ellipsis' :
maxLines === 2 ? 'van-multi-ellipsis--l2' :
'van-multi-ellipsis--l3';
element.className = ellipsisClass;
return maxLines;
};
// 文本内容自适应
const adaptiveTextDisplay = (text, container) => {
const tempElement = document.createElement('div');
tempElement.textContent = text;
tempElement.style.visibility = 'hidden';
tempElement.style.position = 'absolute';
document.body.appendChild(tempElement);
const textHeight = tempElement.offsetHeight;
const containerHeight = container.offsetHeight;
document.body.removeChild(tempElement);
return calculateOptimalLines(container, containerHeight);
};高级边框效果
/* 渐变边框效果 */
.gradient-hairline {
position: relative;
background: linear-gradient(90deg, transparent, var(--van-border-color), transparent);
}
.gradient-hairline::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(
90deg,
transparent 0%,
var(--van-border-color) 20%,
var(--van-border-color) 80%,
transparent 100%
);
transform: scaleY(0.5);
}
/* 动态边框效果 */
.animated-border {
position: relative;
overflow: hidden;
}
.animated-border::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 1px;
background: linear-gradient(90deg, transparent, var(--van-primary-color), transparent);
animation: border-slide 2s infinite;
transform: scaleY(0.5);
}
@keyframes border-slide {
0% { left: -100%; }
100% { left: 100%; }
}创意安全区应用
<template>
<div class="smart-safe-area">
<!-- 顶部安全区指示器 -->
<div class="safe-area-indicator top" v-if="hasTopNotch">
<div class="notch-simulation"></div>
</div>
<!-- 内容区域 -->
<div class="content-area van-safe-area-top van-safe-area-bottom">
<slot />
</div>
<!-- 底部安全区指示器 -->
<div class="safe-area-indicator bottom" v-if="hasBottomSafeArea">
<div class="home-indicator"></div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const hasTopNotch = ref(false);
const hasBottomSafeArea = ref(false);
onMounted(() => {
// 检测设备安全区
const safeAreaTop = getComputedStyle(document.documentElement)
.getPropertyValue('--van-safe-area-inset-top');
const safeAreaBottom = getComputedStyle(document.documentElement)
.getPropertyValue('--van-safe-area-inset-bottom');
hasTopNotch.value = parseInt(safeAreaTop) > 0;
hasBottomSafeArea.value = parseInt(safeAreaBottom) > 0;
});
</script>
<style scoped>
.notch-simulation {
width: 150px;
height: 30px;
background: #000;
border-radius: 0 0 15px 15px;
margin: 0 auto;
}
.home-indicator {
width: 134px;
height: 5px;
background: rgba(0, 0, 0, 0.3);
border-radius: 2.5px;
margin: 8px auto;
}
</style>🔧 常见问题解决
省略号不显示问题
// Q: 为什么省略号样式不生效?
// A: 检查以下几个常见原因
const debugEllipsis = (element) => {
const styles = getComputedStyle(element);
console.log('调试省略样式:');
console.log('width:', styles.width);
console.log('overflow:', styles.overflow);
console.log('white-space:', styles.whiteSpace);
console.log('text-overflow:', styles.textOverflow);
// 常见问题检查
if (styles.width === 'auto') {
console.warn('⚠️ 元素宽度为 auto,需要设置固定宽度');
}
if (styles.overflow !== 'hidden') {
console.warn('⚠️ overflow 不是 hidden');
}
if (styles.whiteSpace !== 'nowrap' && !element.classList.contains('van-multi-ellipsis')) {
console.warn('⚠️ 单行省略需要 white-space: nowrap');
}
};
// 修复省略样式
const fixEllipsis = (element) => {
element.style.width = '100%';
element.style.maxWidth = '200px'; // 根据需要调整
element.style.overflow = 'hidden';
element.style.textOverflow = 'ellipsis';
element.style.whiteSpace = 'nowrap';
};1px边框在某些设备上显示异常
/* 解决方案:使用更兼容的实现 */
.reliable-hairline {
position: relative;
}
.reliable-hairline::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
border: 1px solid var(--van-border-color);
transform: scale(0.5);
transform-origin: 0 0;
box-sizing: border-box;
pointer-events: none;
}
/* 针对不同DPR的优化 */
@media (-webkit-min-device-pixel-ratio: 2) {
.hairline-dpr2::after {
transform: scale(0.5);
}
}
@media (-webkit-min-device-pixel-ratio: 3) {
.hairline-dpr3::after {
transform: scale(0.33);
}
}安全区在某些浏览器不生效
// 安全区兼容性处理
const setupSafeAreaFallback = () => {
// 检测是否支持安全区
const supportsSafeArea = CSS.supports('padding-top: env(safe-area-inset-top)');
if (!supportsSafeArea) {
// 手动检测设备类型并设置安全区
const isIPhoneX = /iPhone/.test(navigator.userAgent) &&
window.screen.height === 812 &&
window.screen.width === 375;
if (isIPhoneX) {
document.documentElement.style.setProperty('--van-safe-area-inset-top', '44px');
document.documentElement.style.setProperty('--van-safe-area-inset-bottom', '34px');
}
}
};
// 动态安全区检测
const detectSafeArea = () => {
const testElement = document.createElement('div');
testElement.style.paddingTop = 'env(safe-area-inset-top)';
testElement.style.visibility = 'hidden';
testElement.style.position = 'absolute';
document.body.appendChild(testElement);
const computedPadding = getComputedStyle(testElement).paddingTop;
const hasSafeArea = computedPadding !== '0px';
document.body.removeChild(testElement);
return hasSafeArea;
};🎨 设计灵感
创意文字效果
/* 彩虹省略号 */
.rainbow-ellipsis {
position: relative;
}
.rainbow-ellipsis::after {
content: '...';
background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4, #feca57);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: rainbow-shift 3s ease-in-out infinite;
}
@keyframes rainbow-shift {
0%, 100% { filter: hue-rotate(0deg); }
50% { filter: hue-rotate(180deg); }
}
/* 打字机效果省略 */
.typewriter-ellipsis {
overflow: hidden;
white-space: nowrap;
animation: typewriter 3s steps(40) infinite;
}
@keyframes typewriter {
0% { width: 0; }
50% { width: 100%; }
100% { width: 0; }
}
/* 呼吸效果边框 */
.breathing-border::after {
animation: breathing 2s ease-in-out infinite;
}
@keyframes breathing {
0%, 100% { opacity: 0.3; transform: scaleY(0.5); }
50% { opacity: 1; transform: scaleY(1); }
}互动式安全区
/* 可视化安全区 */
.visual-safe-area {
position: relative;
}
.visual-safe-area::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
height: env(safe-area-inset-top);
background: linear-gradient(180deg,
rgba(255, 107, 107, 0.1) 0%,
transparent 100%);
pointer-events: none;
z-index: 9999;
}
.visual-safe-area::after {
content: '';
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: env(safe-area-inset-bottom);
background: linear-gradient(0deg,
rgba(78, 205, 196, 0.1) 0%,
transparent 100%);
pointer-events: none;
z-index: 9999;
}
/* 安全区动画指示器 */
.safe-area-pulse {
animation: safe-area-pulse 2s ease-in-out infinite;
}
@keyframes safe-area-pulse {
0%, 100% {
box-shadow: 0 0 0 0 rgba(var(--van-primary-color-rgb), 0.4);
}
50% {
box-shadow: 0 0 0 10px rgba(var(--van-primary-color-rgb), 0);
}
}高级触碰反馈
/* 水波纹触碰效果 */
.ripple-feedback {
position: relative;
overflow: hidden;
}
.ripple-feedback::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.5);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.ripple-feedback:active::before {
width: 300px;
height: 300px;
}
/* 弹性触碰效果 */
.elastic-feedback {
transition: transform 0.2s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.elastic-feedback:active {
transform: scale(0.95);
}
/* 发光触碰效果 */
.glow-feedback {
transition: box-shadow 0.3s ease;
}
.glow-feedback:active {
box-shadow:
0 0 20px rgba(var(--van-primary-color-rgb), 0.5),
0 0 40px rgba(var(--van-primary-color-rgb), 0.3),
0 0 60px rgba(var(--van-primary-color-rgb), 0.1);
}🚀 高级功能扩展
智能样式系统
// 自适应样式管理器
class AdaptiveStyleManager {
constructor() {
this.observers = new Map();
this.breakpoints = {
mobile: 768,
tablet: 1024,
desktop: 1200
};
}
// 注册响应式样式
registerResponsiveStyle(element, styles) {
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
const { width } = entry.contentRect;
this.applyResponsiveStyles(element, width, styles);
}
});
observer.observe(element);
this.observers.set(element, observer);
}
// 应用响应式样式
applyResponsiveStyles(element, width, styles) {
// 清除之前的样式
element.className = element.className
.split(' ')
.filter(cls => !cls.startsWith('van-'))
.join(' ');
// 应用新样式
if (width <= this.breakpoints.mobile) {
element.classList.add(...styles.mobile);
} else if (width <= this.breakpoints.tablet) {
element.classList.add(...styles.tablet);
} else {
element.classList.add(...styles.desktop);
}
}
// 销毁观察器
destroy(element) {
const observer = this.observers.get(element);
if (observer) {
observer.disconnect();
this.observers.delete(element);
}
}
}
// 使用示例
const styleManager = new AdaptiveStyleManager();
styleManager.registerResponsiveStyle(document.querySelector('.adaptive-text'), {
mobile: ['van-ellipsis', 'van-haptic-feedback'],
tablet: ['van-multi-ellipsis--l2'],
desktop: ['van-multi-ellipsis--l3']
});主题化样式系统
<template>
<div class="theme-provider" :class="themeClass">
<div class="style-showcase">
<div class="demo-card van-hairline--surround van-haptic-feedback">
<h3 class="van-ellipsis">{{ title }}</h3>
<p class="van-multi-ellipsis--l2">{{ description }}</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const currentTheme = ref('light');
const themeClass = computed(() => `theme-${currentTheme.value}`);
const switchTheme = () => {
currentTheme.value = currentTheme.value === 'light' ? 'dark' : 'light';
};
defineExpose({ switchTheme });
</script>
<style scoped>
.theme-light {
--van-text-color: #323233;
--van-border-color: #ebedf0;
--van-background-color: #ffffff;
}
.theme-dark {
--van-text-color: #f7f8fa;
--van-border-color: #323233;
--van-background-color: #1e1e1e;
}
.demo-card {
padding: 16px;
margin: 16px;
background: var(--van-background-color);
border-radius: 8px;
transition: all 0.3s ease;
}
.demo-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
</style>📚 延伸阅读
技术文档
设计指南
- 移动端设计规范 - Material Design 指南
- iOS 人机界面指南 - Apple 设计规范
- 响应式设计原则 - 响应式设计最佳实践
性能优化
相关组件
- Button 按钮 - 按钮组件
- Cell 单元格 - 单元格组件
- Card 卡片 - 卡片组件
- Layout 布局 - 布局组件
- Popup 弹出层 - 弹窗组件
- Transition 动画 - 过渡动画
- Theme 主题 - 主题定制
- Space 间距 - 间距组件### 📏 1px 边框 - 精密的线条工匠
🔍 Retina 屏幕的完美主义者,打造发丝般精细的边框
这位精密的线条工匠专门为高清屏幕量身定制,运用巧妙的伪类 transform 魔法,创造出真正的 1 像素边框(hairline)。就像用最细的画笔勾勒轮廓一样,为你的元素添加清晰锐利、毫不模糊的精美边框,让每一条线都完美呈现! ✨
🛡️ 安全区 - 贴心的屏幕守护者
📱 全面屏时代的智能适配专家,让内容远离"危险地带"
这位贴心的屏幕守护者深知现代设备的"刘海"、"药丸"、"圆角"等特殊区域,就像一位经验丰富的安全员,智能地为你的元素添加安全区适配。确保重要内容永远不会被遮挡,让你的应用在各种奇形怪状的屏幕上都能完美展现! 🌟
🎬 动画 - 生动的视觉魔法师
✨ 让静态界面瞬间活起来的动效大师
这位生动的视觉魔法师拥有丰富的动画宝库,通过 transition 组件这个神奇的传送门,你可以轻松召唤各种精美的内置动画效果。从优雅的淡入淡出到活泼的滑动效果,每一个动画都经过精心调校,为你的界面注入生命力和趣味性! 🎭
👆 触碰反馈 - 敏感的互动精灵
🎯 让每一次点击都有回应的贴心小助手
这位敏感的互动精灵时刻关注着用户的每一次触碰,当手指轻抚屏幕时,它会立即做出优雅的回应——元素透明度的微妙变化,就像害羞的小精灵眨了眨眼睛。
这种细腻的反馈让用户清楚地知道"我点到了!",特别适合按钮等可点击元素,为交互体验增添一份温暖的人性化触感! 💫
🧹 清除浮动 - 布局的整理大师
🔧 专治各种浮动"乱象"的布局清洁工
这位勤劳的布局整理大师专门解决 float 布局带来的各种"浮动乱象"。当元素们像调皮的气球一样到处乱飞时,它会挥舞着神奇的清理魔法棒,让所有浮动元素乖乖归位,恢复页面的整洁有序。一招制敌,让布局重回和谐! ✨