5 điểm bởi GN⁺ 2025-09-09 | Chưa có bình luận nào. | Chia sẻ qua WhatsApp
  • Giới thiệu cách tái tạo hiệu ứng Liquid GlassApple công bố tại WWDC 2025 trên web bằng CSS, SVG và các phép tính khúc xạ dựa trên vật lý
  • Giải thích nguyên lý của hiện tượng khúc xạ và quá trình mô phỏng khúc xạ, mô tả bề mặt kính bằng định luật Snell
  • Sử dụng SVG displacement map để tạo trường vector dịch chuyển phù hợp cho render và trình diễn khả năng áp dụng vào các UI component thực tế
  • Trên Chrome, có thể dùng SVG filter làm backdrop-filter, đồng thời đưa ra ví dụ áp dụng cho nhiều thành phần UI khác nhau như kính lúp, công tắc, trình phát nhạc
  • Có thể triển khai hiệu ứng theo thời gian thực, nhưng vẫn tồn tại vấn đề tương thích đa trình duyệt và giới hạn hiệu năng; bài viết cũng đề cập kế hoạch open source trong tương lai

Giới thiệu

Apple đã lần đầu giới thiệu hiệu ứng Liquid Glass tại WWDC tháng 6 năm 2025. Hiệu ứng này khiến các phần tử giao diện trông giống như kính khúc xạ có bề mặt cong, mang lại trải nghiệm thị giác tương tự bề mặt kính thật. Bài viết này trình bày cách thực hành tạo hiệu ứng tương tự trong môi trường web bằng CSS, SVG displacement map và các phép tính khúc xạ dựa trên vật lý. Mục tiêu không phải là một bản triển khai hoàn chỉnh, mà là một proof of concept có thể mở rộng nhằm tái tạo các đặc trưng chính như khúc xạ và highlight phản xạ. Bài viết bắt đầu từ nguyên lý cơ bản về cách ánh sáng khúc xạ khi đi qua các vật liệu khác nhau, rồi xây dựng hiệu ứng từng bước. Demo tương tác được cung cấp ở cuối hiện chỉ hoạt động đúng trên Chrome.

Tìm hiểu hiện tượng khúc xạ

Khúc xạ là hiện tượng hướng truyền của ánh sáng thay đổi khi nó đi từ vật liệu này sang vật liệu khác. Điều này xảy ra do tốc độ ánh sáng khác nhau trong mỗi môi trường, và mối quan hệ giữa góc tới với góc khúc xạ được biểu diễn bằng định luật Snell:

  • n1 * sin(θ1) = n2 * sin(θ2)
    • n1: chiết suất của môi trường thứ nhất
    • θ1: góc tới
    • n2: chiết suất của môi trường thứ hai
    • θ2: góc khúc xạ

Có thể quan sát trong sơ đồ tương tác:

  • Nếu chiết suất của hai môi trường bằng nhau, ánh sáng đi thẳng mà không khúc xạ
  • Nếu môi trường thứ hai có chiết suất cao hơn, ánh sáng bị bẻ về phía pháp tuyến
  • Nếu môi trường thứ hai có chiết suất thấp hơn, ánh sáng lệch ra xa pháp tuyến; trong một số trường hợp có thể xảy ra phản xạ toàn phần bên trong
  • Nếu tia tới vuông góc với bề mặt, nó vẫn đi thẳng bất kể chiết suất

Giới hạn của dự án này

Để giới hạn độ phức tạp và đơn giản hóa thuật toán, bài viết đặt ra các điều kiện sau:

  • Chiết suất của môi trường bên ngoài là 1 (không khí)
  • Vật liệu kính bên trong dùng giá trị 1.5 (kính thông thường)
  • Chỉ xét một lần khúc xạ duy nhất (bỏ qua khúc xạ tại điểm thoát ra)
  • Tia tới luôn vuông góc với mặt nền phía sau
  • Mọi đối tượng đều là hình 2D và chỉ dùng dạng tròn (có thể mở rộng sang hình chữ nhật v.v. nhưng cần thêm tính toán)
  • Không có khoảng hở giữa đối tượng và mặt nền
  • Với các điều kiện này, có thể dùng định luật Snell để tính đơn giản mọi tia sáng

Tạo bề mặt kính

Để triển khai hiệu ứng Liquid Glass, cần định nghĩa bằng toán học mặt cắt của kính ảo (thấu kính hoặc panel cong).

Hàm bề mặt

Bề mặt kính được định nghĩa bằng surface function, biểu diễn độ dày từ mép vào đến phần trong phẳng

  • Giá trị đầu vào của hàm: 0 là mép ngoài, 1 là cuối bezel (điểm bắt đầu mặt phẳng)
  • Từ đạo hàm tại độ dày của từng điểm, lấy được vector pháp tuyến của bề mặt
const height = f(distanceFromSide);
const delta = 0.001;
const y1 = f(distanceFromSide - delta);
const y2 = f(distanceFromSide + delta);
const derivative = (y2 - y1) / (2 * delta);
const normal = { x: -derivative, y: 1 };

Các loại hàm bề mặt chính

  • Convex Circle: y = sqrt((1 - (1 - x))^2)
    • Dạng cung tròn đơn giản, càng vào trong càng nhanh chóng phẳng ra nên mép khúc xạ hiện rõ
  • Convex Squircle: y = ((1 - (1 - x))^4)^(1/4)
    • Hình dạng Apple thường ưa dùng, ranh giới giữa phần cong và phần phẳng mượt hơn nên khi mở rộng vẫn cho hiệu ứng tự nhiên
  • Concave: y = 1 - Convex(x)
    • Dạng lõm (ngược với lồi), ánh sáng bị khúc xạ ra ngoài biên đối tượng nên cần lấy mẫu ở bên ngoài
  • Lip: y = mix(Convex(x), Concave(x), Smootherstep(x))
    • Cấu trúc kết hợp với phần mép nhô lên và một độ lõm nhẹ ở trung tâm

Chỉ với bốn hàm này cũng có thể so sánh khá hiệu quả sự khác biệt khúc xạ theo từng hình dạng bề mặt.

Mô phỏng

Dựa trên từng hàm bề mặt, bài viết mô phỏng đường đi khúc xạ của tia sáng để trực quan hóa sự khác biệt của hiệu ứng thực tế.

  • Dạng lồi (Convex) gom đường đi ánh sáng vào trong, còn dạng lõm (Concave) đẩy chúng ra ngoài
  • Liquid Glass của Apple phần lớn ưu tiên dạng lồi (ngoại lệ như Switch)
  • Mũi tên nền hiển thị lượng khúc xạ (độ dịch chuyển) bằng màu theo độ lớn
  • Ở cùng khoảng cách tính từ biên trái/phải sẽ có cùng độ dịch chuyển, nên có thể tái sử dụng hiệu quả

Tạo trường vector dịch chuyển

Xây dựng một trường vector biểu diễn hướng và độ lớn dịch chuyển của ánh sáng tại từng vị trí trên toàn bộ bề mặt kính

  • Với hình tròn, chuyển động luôn theo hướng pháp tuyến dựa trên đường biên

Tính trước độ lớn dịch chuyển

  • Do độ lớn dịch chuyển đối xứng theo khoảng cách từ biên, có thể tính trước các giá trị theo đơn vị bán kính rồi lưu vào mảng
  • Trong 2D chỉ cần tính một lần (mô phỏng 127 tia) rồi quay quanh trục z để tạo toàn bộ trường

Chuẩn hóa vector

Để dùng vector trong displacement map, cần chuẩn hóa (scale tối đa 1.0)

  • Lấy giá trị dịch chuyển lớn nhất làm chuẩn rồi chia độ lớn của các vector còn lại cho giá trị này
const maximumDisplacement = Math.max(...displacementMagnitudes);
displacementVector_normalized = {
  angle: normalAtBorder,
  magnitude: magnitude / maximumDisplacement,
};

Khi chuyển đổi sang đơn vị pixel thực tế trong SVG displacement map, giá trị lớn nhất ban đầu sẽ được khôi phục bằng cách nhân lại qua scale.

SVG Displacement Map

Để áp dụng kết quả tính toán khúc xạ vào render trong trình duyệt một cách thực tế, bài viết sử dụng SVG displacement map

  • Mỗi kênh (RGBA, 8-bit) của <feDisplacementMap /> có thể lần lượt đảm nhiệm độ dịch chuyển theo trục X, Y
  • Mỗi kênh có giá trị từ 0 đến 255, trong đó 128 là trạng thái trung tính (không dịch chuyển)
  • displacement map bắt buộc phải được chuyển thành ảnh
<svg colorInterpolationFilters="sRGB">
  <filter id={id}>
    <feImage
      href={displacementMapDataUrl}
      x={0}
      y={0}
      width={width}
      height={height}
      result="displacement_map"
    />
    <feDisplacementMap
      in="SourceGraphic"
      in2="displacement_map"
      scale={scale}
      xChannelSelector="R"
      yChannelSelector="G"
    />
  </filter>
</svg>

Scale

Giá trị của kênh Red(X) và Green(Y) được ánh xạ vào khoảng [−1, 1]

  • Sau đó nhân với độ dịch chuyển pixel tối đa thông qua thuộc tính scale để tạo render thực tế
  • Có thể animate scale để điều chỉnh cường độ hiệu ứng

Chuyển vector → giá trị Red/Green

  • Chuyển vector dịch chuyển (góc, độ lớn) sang tọa độ trực giao x, y rồi ánh xạ về 0~255 với mốc 128 là trung tính
const x = Math.cos(angle) * magnitude;
const y = Math.sin(angle) * magnitude;
const result = {
  r: 128 + x * 127,
  g: 128 + y * 127,
  b: 128,
  a: 255,
};

Ảnh hoàn chỉnh sau đó có thể được dùng làm displacement map cho SVG filter.

Playground

Trong Playground tương tác, người dùng có thể thay đổi theo thời gian thực hình dạng bề mặt, độ dày bezel, độ dày kính, effect scale v.v. để trải nghiệm sự thay đổi của trường khúc xạ, displacement map và kết quả render cuối cùng.

Specular Highlight

Cuối cùng, bài viết thêm specular highlight (highlight sáng ở mép bề mặt kính)

  • Cách triển khai của Apple là kiểu rim light, trong đó cường độ sáng thay đổi theo pháp tuyến bề mặt và góc của nguồn sáng

Kết hợp khúc xạ và Specular Highlight

Trong SVG filter cuối cùng, displacement map và ảnh specular highlight được nạp riêng bằng <feImage /> rồi gộp lại bằng <feBlend /> để tạo hiệu ứng hoàn chỉnh

  • Có thể tinh chỉnh các tham số filter để tạo nhiều phong cách thị giác khác nhau

Dùng SVG filter làm backdrop-filter

  • Để thực sự áp dụng hiệu ứng Liquid Glass lên UI component, cần có hỗ trợ backdrop-filter: url(#...) của Chrome
  • Vì kích thước ảnh của backdrop-filter không tự động khớp, nên bắt buộc phải chuẩn bị displacement map tương ứng với kích thước phần tử
.glass-panel {
  backdrop-filter: url(#liquidGlassFilterId);
}

Áp dụng lên UI component thực tế

Bài viết còn triển khai ví dụ áp dụng vào các UI component thực tế dựa trên refraction và displacement map đã tính toán

  • Chỉ Chrome mới có thể xử lý SVG filter dưới dạng backdrop-filter
  • Các ví dụ không phải component production thực thụ, mà nhằm trình diễn cách hiệu ứng Liquid Glass có thể được áp dụng lên nhiều loại UI khác nhau

Magnifying Glass

  • Dùng refraction và zoom ở cả hai phía cùng với hai displacement map
  • Bổ sung bóng đổ và điều chỉnh scale để tạo hiệu ứng tương tác
  • Có thể kéo để biến dạng thấu kính và quan sát đường khúc xạ
  • Thêm specular highlight mượt mà

Searchbox

  • Áp dụng hiệu ứng Liquid Glass cho dạng ô nhập liệu tiêu chuẩn

Switch

  • Dùng lip bezel để phần ngoài là lồi, phần trong là lõm
  • Chỉ thanh trượt ở giữa được phóng to/thu nhỏ, còn phần mép thì áp dụng khúc xạ ảnh bên trong

Slider

  • Dùng convex bezel, hiển thị nguyên giá trị hiện tại qua lớp kính, còn hai bên thì áp dụng khúc xạ nền

Music Player

  • Mô phỏng phong cách panel Liquid Glass của Apple Music

  • Dùng convex bezel và specular highlight nhẹ để tăng cảm giác khối

  • Sử dụng iTunes Search API để tải album art, tên bài hát và các thông tin khác

  • (Cung cấp danh sách tên bài hát và thông tin album dưới dạng danh sách)

Kết luận

Prototype này diễn giải đơn giản khái niệm Liquid Glass của Apple bằng hiệu ứng khúc xạ theo thời gian thực và highlight cơ bản. Nó chỉ thực sự khả dụng trên trình duyệt nền tảng Chromium (hoặc Electron), còn ở các trình duyệt khác có thể thay bằng lớp blur.
Đây là một bản triển khai mang tính thử nghiệm; việc phải tái tạo displacement map mỗi khi thay đổi shape/size là cực kỳ kém hiệu quả (chỉ một số tham số như scale của filter mới có thể animate).
Tác giả cho biết đang cân nhắc phát hành open source trong tương lai, đồng thời bày tỏ sự quan tâm tới tối ưu hóa và dọn dẹp mã nguồn.

Chưa có bình luận nào.

Chưa có bình luận nào.