Як зробити цей ефект освітлення за допомогою CSS


125

Я хотів би імітувати світло "сканування", яке відображатиме слова у полі, це вже мій код:

const e = document.getElementsByClassName('scan')[0];
document.onmousemove = function(event){
  e.style.left = `${event.clientX}px`;
};
*{
    margin: 0;
    padding: 0;
}

html, body{
    width: 100%;
    min-height: 100vh;
    overflow-x: hidden;
    
    display: flex;
}

.banner{
    width: 100vw;
    height: 100vh;

    display: flex;
    flex-grow: 1;
    flex-direction: row;
    align-items: center;
    background-color: #031321;
}

.banner .scan{
    width: 7px;
    height: 80%;
    
    position: absolute;
    left: 30px;
    z-index: 3;

    transition: left 50ms ease-out 0s;
    
    border-radius: 15px;
    background-color: #fff;
    box-shadow:
        0 0 15px 5px #fff,  /* inner white */
        0 0 35px 15px #008cff, /* inner blue */
        0 0 350px 20px #0ff; /* outer cyan */
}

.banner .description{
    width: 100%;
    color: white;
    font-size: 3em;
    text-align: center;

    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
<div class="banner">
    <div class="scan"></div>
    <div class="description">
        Just trying something
    </div>
</div>

Ідея полягає в тому, щоб показати слова у .descriptiondiv відповідно до положення світла сканування. Якщо це можливо, я хотів би використовувати CSS лише для того, щоб зробити цей ефект, і використовувати JavaScript лише для переміщення сканування (яке надалі стане анімацією CSS). Я намагався використати деякі псевдоелементи, але це не спрацювало добре. Ось приклад того, як повинна працювати ця анімація.

Відповіді:


114

Ви можете використовувати прозорий текст з градієнтним фоном. Я використовував background-attachment: fixedі змінну CSS для управління фоновою позицією.

Ви можете збільшити розмір фону (500 пікселів у цьому прикладі), щоб збільшити згладжування переходів.

const e = document.getElementsByClassName('scan')[0];
const hidden = document.getElementsByClassName('hidden')[0];

document.onmousemove = function(event) {
  e.style.left = `${event.clientX}px`; //               ↓ background width (500px) / 2
  hidden.style.setProperty("--pos", `${event.clientX - 250}px`);
};
* {
  margin: 0;
  padding: 0;
}

html,
body {
  width: 100%;
  min-height: 100vh;
  overflow-x: hidden;
  display: flex;
}

.banner {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-grow: 1;
  flex-direction: row;
  align-items: center;
  background-color: #031321;
}

.banner .scan {
  width: 7px;
  height: 80%;
  position: absolute;
  left: 30px;
  z-index: 3;
  transition: left 50ms ease-out 0s;
  border-radius: 15px;
  background-color: #fff;
  box-shadow: 0 0 15px 5px #fff, /* inner white */
  0 0 35px 15px #008cff, /* inner blue */
  0 0 350px 20px #0ff;
  /* outer cyan */
}

.banner .description {
  width: 100%;
  color: white;
  font-size: 3em;
  text-align: center;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.hidden {
  background: radial-gradient(dodgerblue 10%, #031321 50%) var(--pos) 50% / 500px 500px no-repeat fixed;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
<div class="banner">
  <div class="scan"></div>
  <div class="description">
    Just <span class="hidden">hidden</span> something
  </div>
</div>

Ось ще один приклад з дуже довгим абзацом та кількома прихованими текстами. Тут ми контролюємо і вісь X і Y.

const hiddens = document.querySelectorAll('.hidden');

document.addEventListener("mousemove", e => {
  hiddens.forEach(p => {
    //                                            ↓ background width (400px) / 2
    p.style.setProperty("--posX", `${e.clientX - 200}px`);
    p.style.setProperty("--posY", `${e.clientY - 200}px`);
  });
});
html,
body {
  width: 100%;
  overflow-x: hidden;
}

body {
  background: #031321;
  color: #fff;
  font-size: 3rem;
  line-height: 1.5;
  padding: 20px;
  box-sizing: border-box;
}

.hidden {
  background: radial-gradient(dodgerblue 10%, #031321 50%) var(--posX) var(--posY) / 400px 400px no-repeat fixed;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
Lorem ipsum dolor sit amet, <span class="hidden">consectetur</span> adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. <span class="hidden">Excepteur sint</span> occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum
dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit
in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea <span class="hidden">commodo</span> consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim
id est laborum.


50

Ось ідея використання трансформації для кращої роботи

Щоб застосувати його лише до кількох слів, ви граєтесь z-index


Ще одна ідея змусити його працювати з будь-яким фоном, якщо ви хочете прозорість:


35

Класна паличка світіння!

Я припускаю, що це для логотипу, і що текст повинен продовжувати відображатися, коли паличка світильника передає текст.


Я б використовував псевдоелемент на елементі опису, розміщував його зверху і використовував градієнт-фон, що переходить від прозорого до темно-синього кольору фону. Використовуючи градієнт, ви можете досягти приємного зникання тексту.

Потім я встановив початкову точку кольору темного тла за допомогою змінної CSS, яку я оновлюю за допомогою вашого методу onmousemove.

Код не враховує різні розміри екрану, тому, ймовірно, вам потрібно перетворити пікселі у відсотки, якщо ви хочете, щоб ваша анімація відповідала.

Я також змінив ваші класи на id. Я думаю, що доречніше показати, використовуючи ідентифікатори, що елемент якимось чином використовується javascript. Також простіше прив’язати елементи до змінних.

const scanEl = document.getElementById('scan');
const descEl = document.getElementById("description")

document.onmousemove = function(event){
  let descriptionDisplacement = 100;
  scanEl.style.left = `${event.clientX}px`;
  descEl.style.setProperty("--background-shift", `${event.clientX + descriptionDisplacement}px`);
};
*{
    margin: 0;
    padding: 0;
}

html, body{
    width: 100%;
    min-height: 100vh;
    overflow-x: hidden;
    
    display: flex;
}

.banner{
    width: 100vw;
    height: 100vh;

    display: flex;
    flex-grow: 1;
    flex-direction: row;
    align-items: center;
    background-color: #031321;
}

.banner > #scan{
    width: 7px;
    height: 80%;
    
    position: absolute;
    left: 30px;
    z-index: 3;

    transition: left 50ms ease-out 0s;
    
    border-radius: 15px;
    background-color: #fff;
    box-shadow:
        0 0 15px 5px #fff,  /* inner white */
        0 0 35px 15px #008cff, /* inner blue */
        0 0 350px 20px #0ff; /* outer cyan */
}

.banner > #description{
    width: 100%;
    color: white;
    font-size: 3em;
    text-align: center;

    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;
    
    /* ADDED */
    --background-shift: 0px;
    --background-shift-transparent: calc(var(--background-shift) - 150px);
    
    position: relative;
}

.banner > #description::before {
  content: '';
  background: linear-gradient(to right, transparent var(--background-shift-transparent), #031321 var(--background-shift));
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}
<div class="banner">
    <div id="scan"></div>
    <div id="description">
        Just trying something
    </div>
</div>


17

Я щойно спробував clipPath. Технічно він робить те, що вам потрібно, але продуктивність анімованого clipPath досить погана в поєднанні з ефектом світіння (але набагато краще без!). Можливо, побудова світіння з чогось на зразок зображення, а не з тіні-ящика, це покращило б. (як це може зменшити розмір зовнішньої тіні самого вікна)

const e = document.getElementsByClassName('scan')[0];
const description = document.getElementsByClassName('description')[0];
document.onmousemove = function(event){
    // comment out to compare performance
    e.style.left = `${event.clientX}px`;
    description.style.clipPath = `polygon(0 0, ${event.clientX}px 0, ${event.clientX}px 100%, 0 100%)`;
};
*{
    margin: 0;
    padding: 0;
}

html, body{
    width: 100%;
    min-height: 100vh;
    overflow-x: hidden;
    
    display: flex;
}

.banner{
    width: 100vw;
    height: 100vh;


    display: flex;
    flex-grow: 1;
    flex-direction: row;
    align-items: center;
    background-color: #031321;
}

.banner .scan{
    width: 7px;
    height: 80%;

    
    position: absolute;
    left: 30px;
    z-index: 3;

    transition: left 50ms ease-out 0s;
    
    border-radius: 15px;
    background-color: #fff;
    box-shadow:
        0 0 15px 5px #fff,  /* inner white */
        0 0 35px 15px #008cff, /* inner blue */
        0 0 350px 20px #0ff; /* outer cyan */
}

.banner .description{
    width: 100%;
    color: white;
    font-size: 3em;
    text-align: center;


    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
<div class="banner">
    <div class="scan"></div>
    <div class="description">
        Just trying something
    </div>
</div>


1
Мені сподобалось це рішення, сумно, що я б змусив анімацію стати важкою за допомогою clip-path, особливо тому, що врешті-решт я заховаю лише кілька слів від фрази, тому для цього буде потрібно більше елементів, використовуючи кліп, але все одно дякую! Просто додавши до запитання приклад того, як повинна працювати анімація, це може допомогти.
Лео Лето

1
На жаль, це не працює, поки миша не заходить у область перегляду. І якщо він потрапляє у область перегляду з правого боку, текст залишається видимим, навіть якщо ви ще не прокрутили його.
TylerH

1
@TylerH: мій допис не задумувався як повне рішення, а як ідея, звичайно, до css можна додати шлях до кліпу за замовчуванням, але справа тут не в цьому. Також виявилося, що бажана поведінка відрізнялася від того, що я зрозумів
Ilmarinen123

9

Спробуйте так:

const e = document.getElementsByClassName('cover')[0];

e.addEventListener('click', animate);

function animate() {
    e.classList.add('scanning');
}
*{
    margin: 0;
    padding: 0;
}

html, body{
    width: 100%;
    min-height: 100vh;
    overflow-x: hidden;
    
    display: flex;
}

.banner{
    width: 100vw;
    height: 100vh;

    display: flex;
    flex-grow: 1;
    flex-direction: row;
    align-items: center;
    background-color: #031321;
}

.banner .cover{
    
    position: absolute;
    left: 30px;
    z-index: 3;
    height: 80%;
  width:100vw;
    background-color: #031321;
    transition: left 700ms ease-out 0s;
}

.banner .cover.scanning {
  left: calc(100% - 30px);
}

.banner .scan{
    width: 7px;
    height:100%;

    transition: left 50ms ease-out 0s;
    
    border-radius: 15px;
    background-color: #fff;
    box-shadow:
        0 0 15px 5px #fff,  /* inner white */
        0 0 35px 15px #008cff, /* inner blue */
        0 0 350px 20px #0ff; /* outer cyan */
}

.banner .description{
    width: 100%;
    color: white;
    font-size: 3em;
    text-align: center;

    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
<div class="banner">
  <div class="cover">
    <div class="scan">
    </div>
  </div>
    <div class="description">
        Just trying something
    </div>
</div>

У цьому рішенні використовується обкладинка праворуч відсканованого зображення з тим же кольором тла, що й банер. Обкладинка рухається разом зі сканом, тому, коли сканування рухається праворуч, воно розкриває текст ліворуч. Це працює, натискаючи на нього в цій демонстрації, але ви можете ініціювати його в JavaScript, проте найкраще для вас.


2
Повне виявлення / приховування за клацанням миші, здається, не відповідає бажаному результату роботи OP щодо переміщення смуги / приховування тексту на основі положення курсору.
TylerH

Домовились. Це якраз те, що я мав на увазі "сканування" і "використовувати JavaScript лише для переміщення сканування".
sbgib

5

Здається, трохи хитро. Перше рішення, яке спадає на думку, - це, можливо, використання лінійного градієнта з динамічною "точкою зупинки" в положенні світлової смуги. Градієнт переходить від темного -> прозорого (положення світлої смуги) -> темного. Код може виглядати приблизно так:

.description-overlay {
  /*
    Replace 50% with the position of the light bar. Get brighter and more 
    transparent as you approach the position of the light bar.
  */
  background: linear-gradient(to right, #000, 50% hsla(0, 0%, 100%, 0.2), #000);
}

Не впевнений, що це спрацює, і, можливо, йому знадобиться кудись кинути тінь, але, можливо, це дасть вам кілька ідей.


Просто додавши до запитання приклад того, як повинна працювати анімація, це може допомогти.
Лео Летто
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.