Tạo ảnh động chia tách văn bản

Tổng quan cơ bản về cách tạo ảnh động phân tách cho chữ cái và chữ cái.

Trong bài đăng này, tôi muốn chia sẻ suy nghĩ về cách giải quyết các lượt tương tác và ảnh động phân tách văn bản trên web mà ở mức tối thiểu, có thể truy cập được và hoạt động trên nhiều trình duyệt. Thử bản minh hoạ.

Bản minh hoạ

Nếu bạn thích video, đây là phiên bản YouTube của bài đăng này:

Tổng quan

Hoạt ảnh phân tách văn bản có thể rất ấn tượng. Trong bài đăng này, chúng tôi sẽ khái quát được phần lớn về tiềm năng của ảnh động, nhưng cung cấp nền tảng để xây dựng. Mục tiêu là tạo ảnh động tăng dần. Theo mặc định, văn bản phải đọc được, với ảnh động được tích hợp ở trên cùng. Hiệu ứng chuyển động phân tách văn bản có thể trở nên quá mức và có khả năng gây gián đoạn, vì vậy, chúng tôi sẽ chỉ thao tác với HTML hoặc áp dụng các kiểu chuyển động nếu người dùng vẫn ổn với chuyển động.

Dưới đây là thông tin tổng quan chung về quy trình làm việc và kết quả:

  1. Chuẩn bị các biến có điều kiện chuyển động đã giảm cho CSS và JS.
  2. Chuẩn bị các tiện ích phân tách văn bản trong JavaScript.
  3. Sắp xếp các điều kiện và tiện ích khi tải trang.
  4. Viết các hiệu ứng chuyển đổi và ảnh động CSS cho các chữ cái và từ ngữ (phần Rad!).

Dưới đây là bản xem trước các kết quả có điều kiện mà chúng ta sẽ dùng:

ảnh chụp màn hình công cụ cho nhà phát triển của Chrome, trong đó bảng điều khiển Phần tử mở và chuyển động giảm được đặt thành "giảm" và h1 được hiển thị không phân tách
Người dùng thích giảm chuyển động: văn bản dễ đọc / không bị tách

Nếu người dùng muốn giảm chuyển động, chúng ta sẽ để riêng tài liệu HTML và không sử dụng ảnh động. Nếu chuyển động được, chúng ta tiếp tục và cắt thành nhiều mảnh. Dưới đây là bản xem trước HTML sau khi JavaScript phân tách văn bản theo chữ cái.

ảnh chụp màn hình công cụ cho nhà phát triển của Chrome, trong đó bảng điều khiển Phần tử mở và chuyển động giảm được đặt thành "giảm" và h1 được hiển thị không phân tách
Người dùng không gặp vấn đề gì với chuyển động; văn bản được chia thành nhiều phần tử <span>

Đang chuẩn bị điều kiện chuyển động

Truy vấn nội dung nghe nhìn @media (prefers-reduced-motion: reduce) có sẵn một cách thuận tiện sẽ được sử dụng từ CSS và JavaScript trong dự án này. Truy vấn phương tiện này là điều kiện chính của chúng tôi để quyết định có phân tách văn bản hay không. Truy vấn phương tiện CSS sẽ được dùng để giữ lại các hiệu ứng chuyển đổi và ảnh động, trong khi truy vấn phương tiện JavaScript sẽ được dùng để giữ lại thao tác với HTML.

Chuẩn bị CSS có điều kiện

Tôi đã sử dụng PostCSS để kích hoạt cú pháp của Truy vấn đa phương tiện cấp 5, trong đó tôi có thể lưu trữ boolean truy vấn đa phương tiện vào một biến:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

Đang chuẩn bị JS có điều kiện

Trong JavaScript, trình duyệt cung cấp một cách kiểm tra các truy vấn nội dung đa phương tiện, tôi đã sử dụng tính năng giải cấu trúc để trích xuất và đổi tên kết quả boolean từ hoạt động kiểm tra truy vấn nội dung đa phương tiện:

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

Sau đó, tôi có thể kiểm thử motionOK và chỉ thay đổi tài liệu nếu người dùng chưa yêu cầu giảm chuyển động.

if (motionOK) {
  // document split manipulations
}

Tôi có thể kiểm tra cùng một giá trị bằng cách sử dụng PostCSS để bật cú pháp @nest từ Nesting Draft 1. Điều này cho phép tôi lưu trữ tất cả logic về ảnh động cũng như các yêu cầu về kiểu cho phần tử mẹ và con ở cùng một nơi:

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

Với thuộc tính tuỳ chỉnh PostCSS và boolean JavaScript, chúng tôi đã sẵn sàng nâng cấp hiệu ứng có điều kiện. Sau đó, chúng ta sẽ chuyển sang phần tiếp theo. Tôi sẽ phân tích JavaScript để chuyển đổi các chuỗi thành các phần tử.

Tách văn bản

Không thể tạo ảnh động riêng lẻ cho chữ cái, từ, dòng, v.v bằng CSS hoặc JS. Để đạt được hiệu quả này, chúng ta cần các hộp. Nếu chúng ta muốn tạo ảnh động cho từng chữ cái, thì mỗi chữ cái cần phải là một phần tử. Nếu chúng ta muốn tạo ảnh động cho từng từ, thì mỗi từ cần phải là một phần tử.

  1. Tạo các hàm tiện ích JavaScript để chia chuỗi thành các phần tử
  2. Sắp xếp việc sử dụng các tiện ích này

Hàm tiện ích tách chữ cái

Một cách thú vị để bắt đầu là sử dụng hàm nhận một chuỗi và trả về mỗi chữ cái trong một mảng.

export const byLetter = text =>
  [...text].map(span)

Cú pháp lan truyền từ ES6 thực sự giúp việc đó trở nên nhanh chóng.

Chức năng tách từ

Tương tự như việc chia tách chữ cái, hàm này nhận một chuỗi và trả về từng từ trong một mảng.

export const byWord = text =>
  text.split(' ').map(span)

Phương thức split() trên chuỗi JavaScript cho phép chúng ta chỉ định ký tự nào cần cắt. Tôi đã thông qua một khoảng trống, cho biết có sự phân tách giữa các từ.

Tạo hàm hiệu dụng trong hộp

Hiệu ứng này đòi hỏi các hộp cho mỗi chữ cái và chúng ta thấy trong các hàm đó, map() đang được gọi bằng hàm span(). Dưới đây là hàm span().

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

Điều quan trọng cần lưu ý là thuộc tính tuỳ chỉnh có tên là --index đang được đặt cùng với vị trí mảng. Việc có các hộp cho ảnh động dạng chữ cái là rất tuyệt vời, nhưng việc có một chỉ mục để sử dụng trong CSS là một bổ sung dường như nhỏ nhưng có tác động lớn. Đáng chú ý nhất trong tác động lớn này là đáng kinh ngạc. Chúng ta có thể sử dụng --index làm cách bù trừ cho ảnh động cho giao diện so le.

Kết luận về phần mềm tiện ích

Đã hoàn tất mô-đun splitting.js:

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

export const byLetter = text =>
  [...text].map(span)

export const byWord = text =>
  text.split(' ').map(span)

Tiếp theo là nhập và sử dụng các hàm byLetter()byWord() sau.

Sắp xếp phân tách

Khi các tiện ích phân tách đã sẵn sàng để sử dụng, việc kết hợp tất cả các tiện ích này lại với nhau có nghĩa là:

  1. Tìm phần tử cần phân tách
  2. Tách chúng và thay thế văn bản bằng HTML

Sau đó, CSS tiếp quản và tạo ảnh động cho các phần tử / hộp.

Tìm phần tử

Tôi đã chọn sử dụng các thuộc tính và giá trị để lưu trữ thông tin về ảnh động mong muốn và cách chia tách văn bản. Tôi thích việc đặt các tuỳ chọn khai báo này vào HTML. Thuộc tính split-by được dùng từ JavaScript để tìm các phần tử và tạo hộp cho chữ cái hoặc từ. Thuộc tính letter-animation hoặc word-animation được dùng trong CSS để nhắm mục tiêu các phần tử con cũng như áp dụng các phép biến đổi và ảnh động.

Dưới đây là mẫu HTML minh hoạ hai thuộc tính:

<h1 split-by="letter" letter-animation="breath">animated letters</h1>
<h1 split-by="word" word-animation="trampoline">hover the words</h1>

Tìm các phần tử của JavaScript

Tôi đã sử dụng cú pháp bộ chọn CSS cho sự hiện diện của thuộc tính để thu thập danh sách các phần tử muốn phân tách văn bản:

const splitTargets = document.querySelectorAll('[split-by]')

Tìm các phần tử từ CSS

Tôi cũng đã sử dụng bộ chọn sự hiện diện của thuộc tính trong CSS để cung cấp cho tất cả ảnh động dạng chữ cái giống nhau kiểu cơ sở. Sau đó, chúng ta sẽ sử dụng giá trị thuộc tính để thêm các kiểu cụ thể hơn nhằm đạt được hiệu ứng.

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

Đang tách văn bản tại chỗ

Đối với mỗi mục tiêu phân tách mà chúng tôi tìm thấy trong JavaScript, chúng tôi sẽ phân tách văn bản của mục tiêu đó dựa trên giá trị của thuộc tính và ánh xạ từng chuỗi với một <span>. Sau đó, chúng ta có thể thay thế văn bản của phần tử bằng các hộp đã tạo:

splitTargets.forEach(node => {
  const type = node.getAttribute('split-by')
  let nodes = null

  if (type === 'letter') {
    nodes = byLetter(node.innerText)
  }
  else if (type === 'word') {
    nodes = byWord(node.innerText)
  }

  if (nodes) {
    node.firstChild.replaceWith(...nodes)
  }
})

Kết luận về việc sắp xếp

Đã hoàn tất index.js:

import {byLetter, byWord} from './splitting.js'

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

if (motionOK) {
  const splitTargets = document.querySelectorAll('[split-by]')

  splitTargets.forEach(node => {
    const type = node.getAttribute('split-by')
    let nodes = null

    if (type === 'letter')
      nodes = byLetter(node.innerText)
    else if (type === 'word')
      nodes = byWord(node.innerText)

    if (nodes)
      node.firstChild.replaceWith(...nodes)
  })
}

Bạn có thể đọc JavaScript bằng tiếng Anh sau:

  1. Nhập một số hàm tiện ích trợ giúp.
  2. Kiểm tra xem người dùng này có chấp nhận chuyển động hay không. Nếu không thì không cần làm gì cả.
  3. Cho mỗi phần tử muốn phân tách.
    1. Hãy chia nhỏ chúng theo cách phân chia.
    2. Thay thế văn bản bằng các phần tử.

Tách ảnh động và hiệu ứng chuyển tiếp

Thao tác chia tách tài liệu ở trên vừa mở ra vô số ảnh động và hiệu ứng tiềm ẩn bằng CSS hoặc JavaScript. Chúng tôi có một số đường liên kết ở cuối bài viết này để giúp bạn khơi dậy tiềm năng phân chia.

Đã đến lúc thể hiện những việc bạn có thể làm với tính năng này! Tôi sẽ chia sẻ 4 ảnh động và hiệu ứng chuyển đổi do CSS điều khiển. 🤓

Tách chữ cái

Làm nền tảng cho các hiệu ứng phân tách chữ cái, tôi thấy CSS sau đây hữu ích. Tôi đặt tất cả hiệu ứng chuyển đổi và ảnh động phía sau truy vấn phương tiện truyền thông chuyển động, sau đó cung cấp cho mỗi chữ cái con mới span một thuộc tính hiển thị cùng với kiểu cho việc cần làm với khoảng trắng:

[letter-animation] > span {
  display: inline-block;
  white-space: break-spaces;
}

Kiểu khoảng trắng quan trọng để các span chỉ là một không gian không bị công cụ bố cục thu gọn. Bây giờ, hãy cùng khám phá những nội dung thú vị.

Ví dụ về các chữ cái tách biệt chuyển đổi

Ví dụ này sử dụng hiệu ứng chuyển đổi CSS cho hiệu ứng tách văn bản. Với các hiệu ứng chuyển đổi, chúng tôi cần các trạng thái để công cụ tạo ảnh động và tôi đã chọn 3 trạng thái: không di chuột, di chuột trong câu và di chuột qua một chữ cái.

Khi người dùng di chuột lên câu, còn gọi là vùng chứa, tôi thu nhỏ tất cả các thành phần con như thể người dùng đẩy chúng ra xa hơn. Sau đó, khi người dùng di chuột một chữ cái, tôi đưa thư đó lên phía trước.

@media (--motionOK) {
  [letter-animation="hover"] {
    &:hover > span {
      transform: scale(.75);
    }

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:hover {
        transform: scale(1.25);
      }
    }
  }
}

Ví dụ về tạo ảnh động cho các chữ cái được tách

Ví dụ này sử dụng ảnh động @keyframe được xác định trước để tạo ảnh động vô hạn cho từng chữ cái, đồng thời tận dụng chỉ mục thuộc tính tuỳ chỉnh cùng dòng để tạo hiệu ứng so le.

@media (--motionOK) {
  [letter-animation="breath"] > span {
    animation:
      breath 1200ms ease
      calc(var(--index) * 100 * 1ms)
      infinite alternate;
  }
}

@keyframes breath {
  from {
    animation-timing-function: ease-out;
  }
  to {
    transform: translateY(-5px) scale(1.25);
    text-shadow: 0 0 25px var(--glow-color);
    animation-timing-function: ease-in-out;
  }
}

Tách từ

Đối với tôi trong các ví dụ này, Flexbox hoạt động như một loại vùng chứa, tận dụng đơn vị ch dưới dạng một khoảng cách ổn định.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
Các công cụ cho nhà phát triển của hộp linh hoạt cho thấy khoảng cách giữa các từ

Ví dụ về cách phân tách từ chuyển cảnh

Trong ví dụ về hiệu ứng chuyển đổi này, tôi lại sử dụng thao tác di chuột. Vì ban đầu, hiệu ứng này sẽ ẩn nội dung cho đến khi di chuột, nên tôi đã đảm bảo rằng hoạt động tương tác và kiểu chỉ được áp dụng nếu thiết bị có khả năng di chuột.

@media (hover) {
  [word-animation="hover"] {
    overflow: hidden;
    overflow: clip;

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:not(:hover) {
        transform: translateY(50%);
      }
    }
  }
}

Ví dụ về ảnh động cho các từ được tách

Trong ví dụ về ảnh động này, tôi sử dụng lại CSS @keyframes để tạo ảnh động vô hạn so le trên một đoạn văn bản thông thường.

[word-animation="trampoline"] > span {
  display: inline-block;
  transform: translateY(100%);
  animation:
    trampoline 3s ease
    calc(var(--index) * 150 * 1ms)
    infinite alternate;
}

@keyframes trampoline {
  0% {
    transform: translateY(100%);
    animation-timing-function: ease-out;
  }
  50% {
    transform: translateY(0);
    animation-timing-function: ease-in;
  }
}

Kết luận

Giờ bạn đã biết tôi làm việc đó như thế nào, bạn sẽ làm thế nào?! 🙂

Hãy đa dạng hoá phương pháp tiếp cận của chúng ta và tìm hiểu tất cả các cách xây dựng trên web. Tạo Codepen hoặc tổ chức bản minh hoạ của riêng bạn, đăng bài cho tôi trên Twitter và tôi sẽ thêm vào phần Bản phối lại của cộng đồng bên dưới.

Nguồn

Các bản minh hoạ và nguồn cảm hứng khác

Bản phối lại của cộng đồng