How to make this illumination effect with CSS

I'd like to simulate a "scan" light that will reveal words in a box, this is my code by now:

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>

The idea is the show the words in the .description div according to the scan light position. If possible I'd like to use CSS only to make this effect, and use JavaScript only to move the scan (which will further become a CSS animation). I tried to use some pseudo elements, but it didn't work well. Here's an example of how this animation should work.

6030 次浏览

Seems a little tricky. The first solution that comes to mind is maybe using a linear gradient with a dynamic "stopping point" at the light bar position. The gradient goes from dark -> transparent (light bar position) -> dark. The code will maybe look something like:

.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);
}

不知道这是否会工作,它可能需要一个盒子的阴影扔在某处,但也许它会给你一些想法。

我刚试过剪切路径。从技术上说,它可以满足您的需要,但是当与发光效果结合在一起时,动画 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>

Try like this:

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 中启动它,然而这对您来说是最好的。

好酷的荧光棒!

我假设这是一个标志,文字应该继续显示时,发光棒已经通过文字。


我会在描述元素上使用一个伪元素,把它放在顶部,并使用渐变背景从透明到深蓝色的背景颜色。通过使用渐变,您可以实现一个很好的文本淡入。

I would then set the starting point of the dark background color with a CSS variable that I update through your onmousemove method.

代码没有考虑到不同的屏幕大小,所以如果您希望您的动画具有响应性,那么您可能需要将像素转换为百分比。

我还把你的课改成 ID 了。我认为更合适的做法是使用 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>

你可以使用带有渐变背景的透明文本。我使用了 background-attachment: fixed和一个 CSS 变量来控制背景位置。

您可以增加背景大小(本例中为500px)以增加过渡平滑。

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.

Here is an idea using transformation to have better performance

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>