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 設計規範
- 響應式設計原則 - 響應式設計最佳實踐
效能最佳化
相關元件
- Transition 動畫 - 過渡動畫
📏 1px 邊框 - 精密的線條工匠
🔍 Retina 螢幕的完美主義者,打造髮絲般精細的邊框
這位精密的線條工匠專門為高清螢幕量身定製,運用巧妙的偽類 transform 魔法,創造出真正的 1 像素邊框(hairline)。就像用最細的畫筆勾勒輪廓一樣,為您的元素新增清晰銳利、毫不模糊的精美邊框,讓每一條線都完美呈現! ✨
<div class="van-clearfix">
<div style="float: left;">左側浮動內容</div>
<div style="float: right;">右側浮動內容</div>
</div>📚 相關內容
🎨 設計與主題
📱 元件文檔
- 🔘 Button 按鈕 - 按鈕元件的使用方法
- 📝 Cell 單元格 - 單元格元件及其樣式
- 🎭 Transition 動畫 - 過渡動畫效果
🚀 效能與最佳化
🛠️ 開發工具
🌟 版本資訊
- 🚀 Vant 4 - 最新版本特性和功能
- 📱 Vant Weapp - 小程式版本
- 📋 更新日誌 - 版本更新記錄
🛡️ 安全區 - 貼心的螢幕守護者
📱 全螢幕時代的智慧適配專家,讓內容遠離「危險地帶」
這位貼心的螢幕守護者深知現代裝置的「瀏海」、「藥丸」、「圓角」等特殊區域,就像一位經驗豐富的安全員,智慧地為您的元素新增安全區適配。確保重要內容永遠不會被遮擋,讓您的應用程式在各種奇形怪狀的螢幕上都能完美展現! 🌟
<div class="van-safe-area-top">頂部安全區域內容</div>
<div class="van-safe-area-bottom">底部安全區域內容</div>🎬 動畫 - 生動的視覺魔法師
✨ 讓靜態介面瞬間活起來的動效大師
這位生動的視覺魔法師擁有豐富的動畫寶庫,透過 transition 元件這個神奇的傳送門,您可以輕鬆召喚各種精美的內建動畫效果。從優雅的淡入淡出到活潑的滑動效果,每一個動畫都經過精心調校,為您的介面注入生命力和趣味性! 🎭
<van-transition name="fade">
<div v-show="show">淡入淡出效果</div>
</van-transition>👆 觸碰回饋 - 敏感的互動精靈
🎯 讓每一次點擊都有回應的貼心小助手
這位敏感的互動精靈時刻關注著使用者的每一次觸碰,當手指輕撫螢幕時,它會立即做出優雅的回應——元素透明度的微妙變化,就像害羞的小精靈眨了眨眼睛。
這種細膩的回饋讓使用者清楚地知道「我點到了!」,特別適合按鈕等可點擊元素,為互動體驗增添一份溫暖的人性化觸感! 💫
<div class="van-haptic-feedback">點擊我試試觸碰回饋效果</div>🧹 清除浮動 - 版面的整理大師
🔧 專治各種浮動「亂象」的版面清潔工
這位勤勞的版面整理大師專門解決 float 版面帶來的各種「浮動亂象」。當元素們像調皮的氣球一樣到處亂飛時,它會揮舞著神奇的清理魔法棒,讓所有浮動元素乖乖歸位,恢復頁面的整潔有序。一招制敵,讓版面重回和諧! ✨
<div class="van-clearfix">
<div style="float: left;">左側浮動內容</div>
<div style="float: right;">右側浮動內容</div>
</div>