2524 字
13 分鐘
Apple 文字遮罩截斷技術完整解析:CSS Mask 與漸層的雙層方案

前言#

在設計應用商店、評論區等需要文字截斷的場景時,開發者往往面臨一個看似簡單但實則複雜的問題:如何優雅地在有限空間內展示文字,同時提供視覺流暢的「展開」體驗?

Apple 在其產品中採用了兩種層次分明的截斷技術,每種技術都針對不同的應用場景優化。本文將深入分析這兩種方案的實現原理、應用場景,以及各自的優劣勢。

本文研究來源:Duolingo - Language Lessons (App Store)


第一部分:技術層面的雙層方案#

方案一:簡化型截斷(multiline-clamp)#

應用場景:App 商店描述#

這是你在 Duolingo App Store 描述頁面看到的方案。

HTML 結構:

<p class="svelte-1up5qog">
<div class="multiline-clamp svelte-1a7gcr6" role="text">
<span class="multiline-clamp__text svelte-1a7gcr6">
Learn a new language with the world's most-downloaded
education app! Duolingo is the fun, free app...
</span>
</div>
<button class="svelte-1up5qog">more</button>
</p>

CSS 實現:

button.svelte-1up5qog {
--gradient-direction: 270deg;
bottom: 0;
display: flex;
inset-inline-end: 0; /* 靠右邊 */
justify-content: end;
position: absolute;
/* 漸變蓋層 - 從右下角開始淡出 */
background: linear-gradient(
var(--gradient-direction), /* 270deg */
var(--pageBg) 72%, /* 72% 處是背景色 */
transparent 100% /* 100% 完全透明 */
);
color: var(--keyColor);
padding-inline-start: 20px;
}

特徵:

  • 結構簡潔,沒有複雜 mask 計算
  • 只用一層 linear-gradient 蓋層
  • 對內容變動的容忍度高

方案二:高級型截斷(with-more-button)#

應用場景:用戶評論區#

這是用於 Duolingo 用戶評論的方案,需要處理各種長度和語言的內容。

HTML 結構:

<div class="truncate-wrapper svelte-1ji3yu5">
<p data-testid="truncate-text"
class="content svelte-1ji3yu5 with-more-button"
style="--lines: 5;
--line-height: var(--lineHeight, 16);
--link-length: 4;">
I have been using Duolingo consistently...
</p>
<button data-testid="truncate-more-button"
class="more svelte-1ji3yu5"
type="button">
MORE
</button>
</div>

CSS 實現(完整版):

.with-more-button.svelte-1ji3yu5 {
--fade-direction: 270deg;
/* 雙層 mask 遮罩 */
mask:
/* 第一層:水平遮罩(掐掉頂部) */
linear-gradient(
0deg, /* 垂直方向 */
transparent 0,
transparent calc(var(--line-height) * 1px), /* 頂部透明 */
#000 calc(var(--line-height) * 1px) /* 以下顯示 */
),
/* 第二層:右下角漸隱 */
linear-gradient(
var(--fade-direction), /* 270deg = 右下 */
transparent 0,
transparent calc(
var(--link-length) *
var(--one-ch, 8) * 1px +
var(--inline-mask-offset, 0px)
),
#000 calc(
(var(--link-length) * var(--one-ch, 8) +
var(--line-height) * 2) * 1px +
var(--inline-mask-offset, 0px)
)
);
mask-position: right bottom;
mask-size: initial, initial;
position: relative;
word-break: break-word;
z-index: var(--z-default);
}

CSS 變數說明:

變數用途典型值
--lines顯示行數5
--line-height單行高度16
--link-length”MORE” 按鈕寬度(字符數)4
--one-ch單個字符的像素寬度8
--inline-mask-offset額外偏移0px
--fade-direction漸隱方向270deg

工作原理:

  1. 第一層漸變(0deg) - 垂直掐頂

    • transparent 0transparent calc(var(--line-height) * 1px):頂部完全透明
    • #000:下面部分完全不透明
    • 用途:確保文字從第一行開始顯示
  2. 第二層漸變(270deg) - 右下角淡出

    • transparent 漸變到 #000
    • 距離由 --link-length--one-ch 決定
    • 用途:在 “MORE” 按鈕位置漸隱文字

特徵:

  • 複雜的 mask 計算,需要精確的像素級控制
  • 通過 CSS 變數實現動態調整
  • 適應不同內容長度和語言的文字寬度
  • 視覺效果流暢精美

第二部分:兩種方案的對比#

技術對比#

維度簡化型(App 描述)高級型(用戶評論)
實現方式單層 gradient 蓋層雙層 mask 遮罩
CSS 複雜度
計算量最小中等
瀏覽器支援中(mask 支援略少)
視覺精度基礎精確
代碼行數~10 行~30 行

功能對比#

維度簡化型高級型
內容來源靜態、可控動態、用戶生成
長度變動性
語言適應有限
漸隱效果簡單覆蓋精美漸隱
按鈕位置固定動態調整
移動端表現穩定穩定

第三部分:應用場景分析#

簡化型方案何時使用?#

最適合:

  • App 商店描述(官方內容,相對固定)
  • 營銷文案區域(控制度高)
  • 內容管理系統的摘要(字數可控)
  • SEO 重要區域(需要爬蟲容易解析)
  • 低性能設備(減少計算)

不適合:

  • 用戶評論(長度不一)
  • 社交媒體動態(語言多樣)
  • 多語言內容(字符寬度差異大)

高級型方案何時使用?#

最適合:

  • 用戶評論區(內容完全動態)
  • 社交媒體動態(需要精美截斷)
  • 多語言應用(需要自適應)
  • 搜索結果摘要(字數不確定)
  • 聊天記錄預覽(長度變動大)

不適合:

  • 簡單的固定內容(過度設計)
  • 低端 Android 設備(mask 支援問題)
  • 對性能敏感的區域(計算複雜)

第四部分:深層設計思想#

Apple 的設計哲學#

Apple 在這個問題上展現的不是「一種解決方案」,而是「場景匹配的方案選擇」:

應用層次分析
├─ 靜態內容層
│ ├─ 來源:官方
│ ├─ 變動:低
│ └─ 方案:簡化型 ✓ 快速、高效
└─ 動態內容層
├─ 來源:用戶
├─ 變動:高
└─ 方案:高級型 ✓ 精確、自適應

核心優化點#

1. 效能優化#

  • 不是所有截斷都需要複雜 mask
  • 根據內容動態性選擇方案
  • 減少不必要的 GPU 計算

2. 體驗優化#

  • 簡化型:快速載入,不影響頁面性能
  • 高級型:精美漸隱,提升內容質感

3. 可維護性#

  • CSS 變數參數化,不需要改動 HTML
  • 通過 Style Attribute 動態注入值
  • 易於在後端控制截斷邏輯

4. 相容性考量#

  • 簡化型:廣泛支援(IE 都支援 gradient)
  • 高級型:現代瀏覽器優先(mask 支援主要是 Chrome、Safari)

第五部分:實現細節深度剖析#

mask 工作原理#

CSS mask 使用漸變來控制元素的透明度。對於 with-more-button 的雙層 mask:

視覺效果分解:

原始文本(5 行):
┌─────────────────────┐
│ Line 1 │
│ Line 2 │
│ Line 3 │
│ Line 4 │
│ Line 5 more button │
└─────────────────────┘
第一層 mask(垂直):
┌─────────────────────┐
│ ░░░░░░░░░░░░░░░░░░░ │ ← 透明(line-height 高度)
│ ███████████████████ │ ← 不透明
│ ███████████████████ │
│ ███████████████████ │
│ ███████████████████ │
└─────────────────────┘
第二層 mask(右下漸隱):
┌─────────────────────┐
│ ███████████████████ │
│ ███████████████████ │
│ ███████████████████ │
│ ███████░░░░░░░░░░░░ │ ← 漸變區
│ ░░░░░░░░░░░░░░░░░░░ │ ← 完全透明
└─────────────────────┘
最終效果(兩層疊加):
┌─────────────────────┐
│ ░░░░░░░░░░░░░░░░░░░ │ ← 第一層切掉
│ ███████████████████ │
│ ███████████████████ │
│ ███████░░░░░░░░░░░░ │ ← 第二層淡出
│ ░░░░░░░░░░░░░░░░░░░ │ ← 完全隱藏
└─────────────────────┘

CSS 變數的動態計算#

關鍵計算式分解:

/* "MORE" 按鈕寬度的像素值 */
var(--link-length) * var(--one-ch, 8) * 1px
= 4 * 8 * 1px
= 32px
/* 漸隱終止位置 */
(var(--link-length) * var(--one-ch, 8) + var(--line-height) * 2) * 1px
= (32px + 16px * 2)
= 64px
/*
--link-length * --one-ch = 按鈕寬度
--line-height * 2 = 兩行高度(預留空間)
總共:按鈕寬度 + 預留空間
*/

第六部分:實踐建議#

實施檢查清單#

選擇方案時:#

  • 內容是靜態還是動態?
  • 字符寬度是否一致(單語言 vs 多語言)?
  • 性能要求有多高?
  • 需要支援的最低瀏覽器版本?
  • 視覺要求(基礎截斷 vs 精美漸隱)?

實施時:#

  • 簡化型:直接使用 gradient 蓋層,5 分鐘搞定
  • 高級型:
    • 測試 CSS 變數計算的準確性
    • 驗證中文、日文等多字節文字的表現
    • 測試極長內容(>1000 字)的表現
    • 檢查移動端點擊 “MORE” 按鈕的可點擊區域

調試時:#

/* 臨時添加背景色便於視覺調試 */
.with-more-button {
background: rgba(0, 0, 0, 0.1); /* 看清 mask 邊界 */
}
button.more {
background: rgba(255, 0, 0, 0.3); /* 看清按鈕邊界 */
}

第七部分:常見問題排查#

Q1:多語言下 mask 計算不准?#

原因: --one-ch 針對英文優化(8px),但中文/日文通常是 16px

解決:

.with-more-button {
--one-ch: 8px; /* 英文、拉丁文 */
}
.with-more-button[lang="zh"],
.with-more-button[lang="ja"],
.with-more-button[lang="ko"] {
--one-ch: 16px; /* 東亞文字 */
}

Q2:Safari 上 mask 不生效?#

原因: Safari 需要 -webkit- 前綴

解決:

.with-more-button {
-webkit-mask: /* 雙層漸變 */;
mask: /* 雙層漸變 */;
}

Q3:按鈕位置在不同分辨率下錯位?#

原因: 硬編碼的 --one-ch 不適應 Retina 屏或縮放

解決:

@media (-webkit-min-device-pixel-ratio: 2) {
.with-more-button {
--one-ch: 16px; /* 調整 DPR */
}
}

第八部分:性能考量#

方案一(簡化型)性能指標#

首屏時間:最小化(無複雜計算)
重排(Reflow):低
重繪(Repaint):低
GPU 使用:最小
總體評分:⭐⭐⭐⭐⭐ 優秀

方案二(高級型)性能指標#

首屏時間:稍微增加(mask 計算)
重排(Reflow):低
重繪(Repaint):中(mask 更新時)
GPU 使用:中(mask 需要 GPU 加速)
總體評分:⭐⭐⭐⭐ 良好

優化建議#

  1. 使用 will-change 優化
.with-more-button {
will-change: mask; /* 提示瀏覽器預先優化 */
}
  1. 避免頻繁改動 CSS 變數
// ❌ 不要這樣做
element.style.setProperty('--line-height', '16px');
element.style.setProperty('--link-length', '4');
// ✅ 這樣做更好
element.style.cssText = `
--line-height: 16px;
--link-length: 4;
`;
  1. 使用 CSS Containment
.truncate-wrapper {
contain: layout style paint; /* 限制重排範圍 */
}

第九部分:結論與延伸思考#

核心發現#

  1. 沒有絕對完美的方案 — 只有「適合當前場景的方案」
  2. 複雜性和成果成正比 — 投入得越多,效果越精美
  3. 效能和體驗需要平衡 — Apple 的做法就是這種平衡的體現

Apple 的啟示#

即使是大公司的產品,也不是全部用最複雜的技術。而是根據:

  • 📊 內容特性(靜態 vs 動態)
  • 🎯 用戶體驗(轉化率 vs 視覺質感)
  • ⚡ 性能預算(首屏時間 vs 計算成本)

精心選擇解決方案。

延伸應用#

這個思路可以推廣到:

  • 文字行數限制(多行省略)
  • 圖片自適應裁切
  • 響應式排版
  • 動態容器適配

都可以通過「靜態方案 + 動態方案」的雙層設計來優化。


附錄:完整代碼示例#

完整的簡化型實現#

<style>
.app-description {
position: relative;
padding-bottom: 40px;
}
.app-description.truncated {
max-height: 150px;
overflow: hidden;
}
.app-description button {
position: absolute;
bottom: 0;
right: 0;
background: linear-gradient(
270deg,
var(--page-bg, white) 72%,
transparent 100%
);
color: var(--key-color, #0066cc);
padding: 0 20px;
border: none;
cursor: pointer;
}
</style>
<div class="app-description truncated">
<p>Learn a new language...</p>
<button>more</button>
</div>

完整的高級型實現#

<style>
.comment-container {
--lines: 5;
--line-height: 16;
--link-length: 4;
--one-ch: 8;
}
.comment-text {
mask:
linear-gradient(
0deg,
transparent 0,
transparent calc(var(--line-height) * 1px),
#000 calc(var(--line-height) * 1px)
),
linear-gradient(
270deg,
transparent 0,
transparent calc(
var(--link-length) * var(--one-ch) * 1px
),
#000 calc(
(var(--link-length) * var(--one-ch) +
var(--line-height) * 2) * 1px
)
);
-webkit-mask: var(--mask-value);
mask-position: right bottom;
mask-repeat: no-repeat;
position: relative;
}
.comment-more-btn {
position: absolute;
bottom: 0;
right: 0;
background: var(--page-bg, white);
color: var(--key-color, #0066cc);
padding: 0 20px;
border: none;
cursor: pointer;
}
</style>
<div class="comment-container">
<p class="comment-text">I have been using Duolingo...</p>
<button class="comment-more-btn">MORE</button>
</div>

參考資源

MDN: CSS mask

W3C: CSS Masked Content Module

Can I use: CSS mask

CSS Masking - MDN

Apple 文字遮罩截斷技術完整解析:CSS Mask 與漸層的雙層方案
https://laplusda.com/posts/apple-text-mask-analysis/
作者
Zero
發佈於
2025-11-05
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。