커스텀 스플래시 HTML 빌더

:information_source: 요약 관리자 정의된 HTML과 CSS를 사용하여 스플래시 화면을 커스터마이즈할 수 있도록 하는 Discourse 플러그인입니다.
:hammer_and_wrench: 저장소 링크 https://github.com/VaperinaDEV/custom-splash-html-builder
:heart: 도움이 되셨나요? > ./support --coffee
:open_book: 설치 가이드 Discourse에 플러그인 설치하는 방법

안녕하세요 :waving_hand:

관리자 정의된 HTML과 CSS를 사용하여 스플래시 화면을 커스터마이즈할 수 있도록 하는 작은 Discourse 플러그인을 만들었습니다. 이를 통해 Discourse 코어 스플래시 템플릿의 수정된 사본을 유지보수할 필요가 없습니다.

이 플러그인을 만든 원래 동기는 실제로 모바일 성능이었습니다.

더 정교한 애니메이션 스플래시 화면을 만들고 싶었으나, 현재 Discourse 코어 스플래시 구현에서 지원하는 SVG 애니메이션이 모바일 기기에서 의외로 문제를 일으킬 수 있음을 발견했습니다.

데스크톱에서는 애니메이션이 완벽하게 매끄럽게 보일 수 있지만, 모바일에서는 눈에 띄게 끊기거나, 프레임 드롭, 지연, 또는 애니메이션 중 멈춘 것처럼 보일 수 있었습니다.

다양한 접근법을 실험한 끝에, 애니메이션을 SVG 자체에서 <div>와 같은 주변 HTML 요소로 이동시키는 것이 매우 큰 차이를 만든다는 것을 발견했습니다.

SVG 내용을 지속적으로 애니메이션 처리하는 대신, SVG는 정적으로 유지하고 브라우저가 CSS 변환을 사용하여 포함하는 HTML 레이어를 애니메이션 처리할 수 있습니다.

이것은 브라우저가 디바이스의 그래픽 하드웨어를 사용하여 애니메이션을 합성 작업으로 처리할 수 있는 훨씬 더 좋은 기회를 제공합니다.

그 결과, SVG 기반 접근 방식에서 보였던 끊김과 동결 없이 모바일에서 훨씬 더 매끄러운 애니메이션을 얻을 수 있었습니다.

이것이 이 플러그인이 만들어진 주요 이유였습니다.

이전 (애니메이션 SVG: 애니메이션이 지연되고 멈춤)

이후 (애니메이션 HTML: 매끄러운 애니메이션)


SVG 애니메이션 처리의 문제점

원래 스플래시 구현은 단순한 로고나 비교적 가벼운 애니메이션에는 완벽하게 적합합니다.

그러나 애니메이션이 더 복잡해지면 SVG 렌더링이 비용이 많이 들 수 있습니다.

예를 들어, SVG 또는 그 내부 요소에 직접 적용된 애니메이션은 브라우저가 애니메이션 동안 SVG의 일부 부분을 반복적으로 처리하거나 다시 그릴 것을 요구할 수 있습니다.

모바일 기기에서는 이것이 특히 눈에 띄게 될 수 있습니다.

테스트 중, 애니메이션이 다음과 같은 경우를 목격했습니다:

  • 눈에 띄게 끊김
  • 일시적으로 동결
  • 멈춘 것처럼 보임
  • 데스크톱보다 훨씬 더 나쁜 동작

흥미로운 점은 동일한 시각적 애니메이션이 실제로 애니메이션 처리되는 대상에 따라 매우 다르게 동작할 수 있다는 것이었습니다.


애니메이션을 HTML 레이어로 이동하기

훨씬 더 잘 작동한 접근법은 SVG 자체를 정적으로 유지하고 이를 일반 HTML 요소 안에 넣는 것이었습니다.

예를 들어:

<div class="logo-layer">
  <svg viewBox="0 0 500 500">
    ...
  </svg>
</div>

SVG를 애니메이션 처리하는 대신, 애니메이션은 컨테이너에 적용됩니다:

#d-splash .logo-layer {
  animation: pulse 1.8s ease-in-out infinite;
  will-change: transform;
}

@keyframes pulse {
  0%,
  100% {
    transform: scale(0.8);
  }

  50% {
    transform: scale(0.85);
  }
}

SVG 자체는 변경되지 않습니다.

따라서 브라우저는 HTML 레이어의 변환을 훨씬 더 효율적으로 처리할 수 있으며, 지원되는 경우 그래픽 하드웨어에 의해 처리되는 합성 레이어로 승격시킬 수 있습니다.

이것은 모바일 기기에서 극적으로 매끄러운 결과를 가져왔습니다.

따라서 중요한 구별은 다음과 같습니다:

코어 접근법:

SVG
 └── SVG 애니메이션
      └── SVG 내용이 애니메이션 처리됨

vs:

커스텀 접근법:

HTML 레이어
 └── SVG
      └── HTML 레이어에 대한 CSS 변환
           └── 합성에 유리한 애니메이션

이것이 모든 애니메이션이 GPU 가속될 것이라는 보장은 아닙니다. 브라우저가 궁극적으로 애니메이션이 어떻게 합성되는지 결정하지만, 제 테스트에서 차이는 매우 뚜렷했습니다.


왜 Custom Splash HTML Builder를 만들었는가

이 접근법이 작동하는 것을 확인한 후, 이를 중심으로 스플래시 화면을 실제로 구축하는 방법도 필요했습니다.

표준 스플래시 템플릿은 이러한 유형의 구현에 대해 충분한 유연성을 제공하지 않습니다.

더 복잡한 애니메이션의 경우, 다음이 필요할 수 있습니다:

  • 여러 SVG 레이어
  • 여러 HTML 컨테이너
  • 독립적으로 애니메이션 처리되는 요소
  • 커스텀 CSS keyframes
  • 다른 애니메이션 타이밍
  • 커스텀 위치 지정
  • 기본 스플래시와 완전히 다른 마크업

따라서 또 다른 하드코딩된 스플래시 구현을 만드는 대신, 시각적 부분을 두 개의 사이트 설정을 통해 노출하기로 결정했습니다.

플러그인은 다음을 추가합니다:

splash_custom_html

스플래시 화면 안에 렌더링되는 HTML/SVG 마크업.

splash_custom_css

커스텀 스플래시에서 사용되는 CSS. 애니메이션, keyframes, 위치 지정 및 반응형 동작 포함.

이것은 애니메이션이 변경될 때마다 플러그인 소스 코드를 수정하지 않고도 스플래시를 효과적으로 커스터마이즈할 수 있게 합니다.


내장 관리자 편집기

플러그인은 또한 커스텀 스플래시를 관리하기 위한 작은 내장 관리자 편집기를 제공합니다.

Discourse 관리자 인터페이스에 Splash HTML Builder 섹션을 추가하며, 다음을 위한 별도의 편집기를 제공합니다:

  • 커스텀 HTML
  • 커스텀 CSS

변경 사항은 관련 사이트 설정을 수동으로 편집하지 않고도 관리자 인터페이스에서 직접 저장할 수 있습니다.

기본 설정은 여전히 다음과 같습니다:

  • splash_custom_html
  • splash_custom_css

편집기는 단순히 그것들을 관리하기 위한 더 편리한 인터페이스입니다.

이것은 또한 스플래시 애니메이션을 변경할 때마다 플러그인 소스 파일을 수정할 필요가 없다는 것을 의미합니다.


예시

커스텀 스플래시는 여러 독립적인 레이어를 포함할 수 있습니다:

<div class="ring-layer">
  <svg viewBox="0 0 500 500">
    ...
  </svg>
</div>

<div class="logo-layer">
  <svg viewBox="0 0 500 500">
    ...
  </svg>
</div>

그리고 각 레이어는 자체 애니메이션을 가질 수 있습니다:

#d-splash .ring-layer {
  animation: rotate 2.2s linear infinite;
  will-change: transform;
}

#d-splash .logo-layer {
  animation: pulse 1.8s ease-in-out infinite;
  will-change: transform;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

@keyframes pulse {
  0%,
  100% {
    transform: scale(0.8);
  }

  50% {
    transform: scale(0.85);
  }
}

SVG는 정적으로 유지되는 동안 주변 HTML 레이어가 애니메이션 처리됩니다.

이는 SVG 자체에서 비용이 많이 드는 애니메이션 작업을 제외하면서 훨씬 더 복잡한 스플래시 애니메이션을 만들 수 있게 합니다.


왜 단순히 코어 스플래시 템플릿을 오버라이드하지 않는가?

또 다른 중요한 목표는 Discourse의 코어 스플래시 템플릿 사본을 유지보수하는 것을 피하는 것이었습니다.

직접적인 접근법은 다음을 오버라이드하는 것입니다:

app/views/common/_discourse_splash.html.erb

그리고 현재 Discourse 구현을 플러그인에 복사하는 것입니다.

문제는 이것이 유지보수 부담을 만든다는 것입니다.

Discourse가 향후 릴리스에서 스플래시 구현을 변경하면, 플러그인에는 여전히 구버전이 포함될 것입니다.

그것은 잠재적으로 다음과 같은 결과를 초래할 수 있습니다:

  • 새로운 코어 변경 사항 누락
  • 성능 개선 누락
  • Discourse 업데이트 후 동작 오류
  • 모든 업데이트 후 플러그인 템플릿을 코어와 수동으로 비교해야 함

저는 그것을 완전히 피하고 싶었습니다.


코어 폴백

따라서 플러그인은 코어 폴백을 가진 커스텀 스플래시를 지원합니다.

커스텀 HTML이 구성됨

만약:

SiteSetting.splash_custom_html.present?

이면, 플러그인은 커스텀 스플래시를 렌더링합니다.

커스텀 HTML이 비어 있음

커스텀 스플래시가 구성되지 않은 경우, 플러그인은 현재 Discourse 코어 스플래시 템플릿으로 폴백합니다.

플러그인은 실행 중인 Discourse 설치에서 실제 코어 파일을 찾습니다:

Rails.root/app/views/common/_discourse_splash.html.erb

그리고 그 구현을 렌더링합니다.

개념적으로:

core_splash_path = Rails.root.join("app", "views", "common", "_discourse_splash.html.erb")

if File.exist?(core_splash_path)
  render inline: File.read(core_splash_path), type: :erb
end

이것은 플러그인이 코어 스플래시 템플릿의 두 번째 사본을 포함하지 않는 것을 의미합니다.


성능 고려 사항

플러그인은 모든 CSS 애니메이션이 마법처럼 GPU 가속될 것이라고 주장하려는 것이 아닙니다.

브라우저는 여전히 개별 애니메이션이 어떻게 렌더링되고 합성되는지 결정합니다.

목표는 대신 브라우저에게 하드웨어 가속 합성을 위한 훨씬 더 유리한 구조를 제공하는 것입니다:

  • SVG 내용 정적 유지
  • 독립적으로 애니메이션 처리되는 요소 격리
  • HTML 레이어 애니메이션 처리
  • 이동/스케일링/회전에 transform 선호
  • 불필요하게 비용이 많이 드는 다시 그리기 작업 피하기
  • 적절한 곳에 will-change 사용

예를 들어:

#d-splash .ring-layer {
  will-change: transform;
  animation: rotate 2.2s linear infinite;
}

이 접근법은 제 사용 사례에서 특히 잘 작동했으며, 원래 SVG 애니메이션에서 보였던 모바일 끊김을 제거했습니다.


커스텀 스플래시 활성화 또는 비활성화

플러그인은 또한 custom_splash_html_builder_enabled 사이트 설정을 제공합니다.

비활성화되면, 커스텀 HTML 또는 CSS가 구성되었는지 여부와 관계없이 표준 Discourse 스플래시 화면이 사용됩니다.

이것은 저장된 HTML/CSS를 삭제하지 않고도 커스텀 스플래시를 일시적으로 비활성화하기 위한 추가적인 안전 스위치를 제공합니다.

커스텀 스플래시는 다음 두 조건이 모두 충족될 때만 렌더링됩니다:

custom_splash_html_builder_enabled = true
splash_custom_html이 비어 있지 않음

그렇지 않으면, 현재 Discourse 코어 스플래시가 사용됩니다.


가장 중요한 것은, SVG 자체를 직접 애니메이션 처리하는 대신 정적인 SVG 내용 주위의 HTML 레이어를 애니메이션 처리하여 모바일에서 훨씬 더 잘 수행되는 커스텀 애니메이션 스플래시를 구축하는 방법을 제공한다는 것입니다.

8개의 좋아요

안녕하세요 :waving_hand:

#d-splash 섹션에 data-color-scheme를 추가하여, 라이트 모드와 다크 모드 스플래시 로고를 쉽게 설정할 수 있도록 했습니다. DEV: Implement dynamic color scheme for splash section · VaperinaDEV/custom-splash-html-builder@ba6641b · GitHub

<%- splash_forced_scheme = (dark_color_scheme? || forced_dark_mode?) ? "dark" : (forced_light_mode? ? "light" : nil) %>

<section id="d-splash"<%= " data-color-scheme=\"#{splash_forled_scheme}\"".html_safe if splash_forced_scheme %>>

따라서 라이트 또는 다크 스킴이 명시적으로 강제될 때 #d-splash에는 data-color-scheme="dark" / "light" 속성이 부여되며, 두 스킴 모두 활성화되어 운영체제가 결정하도록 할 때만 해당 속성 없이 남겨집니다.

수정 방법은 커스텀 CSS에서 미디어 쿼리보다 이 속성이 우선하도록 하고, 속성이 없을 때만 prefers-color-scheme로 폴백되도록 하는 것입니다.

예시:

커스텀 HTML

<!-- LIGHT MODE -->
<div class="custom-splash-light">
  <div class="ring-layer">
    <svg viewBox="0 0 500 500">
      ...
    </svg>
  </div>
    
  <div class="logo-layer">
    <svg viewBox="0 0 500 500">
      ...
    </svg>
  </div>
</div>

<!-- DARK MODE -->
<div class="custom-splash-dark">
  <div class="ring-layer">
    <svg viewBox="0 0 500 500">
      ...
    </svg>
  </div>
    
  <div class="logo-layer">
    <svg viewBox="0 0 500 500">
      ...
    </svg>
  </div>
</div>

커스텀 CSS

#d-splash .custom-splash-dark {
  display: none;
}

/* OS decides — only when there's no forced scheme */
@media (prefers-color-scheme: dark) {
  #d-splash:not([data-color-scheme]) .custom-splash-light {
    display: none;
  }
  #d-splash:not([data-color-scheme]) .custom-splash-dark {
    display: block;
  }
}

/* forced scheme always wins, regardless of OS */
#d-splash[data-color-scheme="light"] .custom-splash-dark {
  display: none;
}
#d-splash[data-color-scheme="dark"] .custom-splash-light {
  display: none;
}
#d-splash[data-color-scheme="dark"] .custom-splash-dark {
  display: block;
}

:not([data-color-scheme])는 속성이 존재하는 경우 단순히 매칭되지 않으므로, 두 규칙 세트 사이에 특이성(specificity) 충돌이 발생하지 않으며, 강제된 스킴이 항상 우선합니다.

2개의 좋아요

멋지네요. 몇 년 전부터 제가 제안하고 찾던 바로 그 기능이에요. 연결이 느릴 때 브랜딩/이미지를 제어해서, 사용자가 혼란스러운 상태에 있을 때 더 잘 지향점을 잡고 안정감을 느낄 수 있도록 하는 거죠.

첫인상만으로도 제가 상상했던 것보다 더 좋은 것 같아요. 언젠가 직접 써보기를 기대하고 있어요. 정말 잘 만들었습니다! 감사합니다.

2개의 좋아요