Как создать этот эффект иллюминации с помощью CSS?
Я хотел бы смоделировать "сканирующий" свет, который покажет слова в рамке.
Идея состоит в том, чтобы показать слова в div class:.description; в соответствии с положением источника света. Если возможно, я хотел бы использовать CSS только для создания этого эффекта и использовать JavaScript только для перемещения сканирования (которое в дальнейшем станет CSS-анимацией).
Вот пример того, как должна работать эта анимация.
Я пытался использовать какие-то псевдоэлементы, но это не сработало.
Вот мой код:
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>
Свободный перевод вопроса How to make this illumination effect with CSS от участника @Leo Letto.
Ответы (3 шт):
Вы можете использовать прозрачный текст с градиентным фоном. Я использовал 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>
Вот идея использования преобразования для повышения производительности:
document.onmousemove = function(event){
document.body.style.setProperty("--p", `${event.clientX}px`);
};
body{
margin: 0;
overflow:hidden;
}
.banner{
height: 100vh;
display: flex;
align-items: center;
background-color: #031321;
}
.banner::before{
content:"";
width: 7px;
height: 80%;
position: absolute;
left: 0;
transform:translateX(var(--p,30px));
z-index: 3;
transition: transform 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{
color: white;
font-size: 3em;
text-align: center;
width:100%;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
overflow:hidden;
position:relative;
}
.banner .description::before {
content:"";
position:absolute;
top:0;
right:0;
bottom:0;
width:200%;
background:linear-gradient(to right,#031321 40%,transparent,#031321 60%);
transform:translateX(var(--p,0px));
}
<div class="banner">
<div class="description">
Just trying something
</div>
</div>
Чтобы применить его только к нескольким словам, вы играете с z-index
document.onmousemove = function(event){
document.body.style.setProperty("--p", `${event.clientX}px`);
};
body{
margin: 0;
overflow:hidden;
}
.banner{
height: 100vh;
display: flex;
align-items: center;
background-color: #031321;
}
.banner::before{
content:"";
width: 7px;
height: 80%;
position: absolute;
left: 0;
transform:translateX(var(--p,30px));
z-index: 3;
transition: transform 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{
color: white;
font-size: 3em;
text-align: center;
width:100%;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
overflow:hidden;
position:relative;
z-index:0;
}
.banner .description::before {
content:"";
position:absolute;
z-index:-1;
top:0;
right:0;
bottom:0;
width:200%;
background:linear-gradient(to right,#031321 40%,transparent,#031321 60%);
transform:translateX(var(--p,0px));
}
.banner .description > span {
position:relative;
z-index:-2;
color:lightblue;
font-weight:bold;
}
<div class="banner">
<div class="description">
Just <span>trying</span> something <span>cool</span>
</div>
</div>
Еще одна идея заставить его работать с любым фоном, если вам нужна прозрачность:
document.onmousemove = function(event){
document.body.style.setProperty("--p", `${event.clientX}px`);
};
body{
margin: 0;
overflow:hidden;
}
.banner{
height: 100vh;
display: flex;
align-items: center;
justify-content:center;
color: white;
font-size: 3em;
background: url(https://picsum.photos/id/1018/800/800) center/cover;
position:relative;
z-index:0;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.banner::before{
content:"";
width: 7px;
height: 80%;
position: absolute;
left: 0;
transform:translateX(var(--p,30px));
z-index: 3;
transition: transform 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::after {
content:"";
position:absolute;
z-index:-1;
top:0;
right:0;
bottom:0;
left:0;
background:inherit;
-webkit-mask:
linear-gradient(to right,#fff 45%,transparent,#fff 55%)
right calc(-1*var(--p,0px)) top 0/200% 100% no-repeat;
}
.banner > span {
position:relative;
z-index:-2;
color:red;
font-weight:bold;
}
<div class="banner">
Just <span>trying</span> something <span>cool</span>
</div>
Свободный перевод ответа от участника @Temani Afif.
Я предполагаю, что это для логотипа, и что текст должен продолжать отображаться, когда светящаяся палочка передала текст, как это происходит в сканере
Я бы использовал псевдоэлемент для элемента описания, поместил его сверху и использовал gradient-background, переходящий от прозрачного к темно-синему цвету фона. Используя градиент, вы можете добиться плавного размытия текста.
Затем я бы установил начальную точку темного цвета фона с помощью переменной 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>
Свободный перевод ответа от участника @Rickard Elimää.