So erzielen Sie diesen Beleuchtungseffekt mit CSS

81

Ich möchte ein "Scan" -Licht simulieren, das Wörter in einer Box enthüllt. Dies ist jetzt mein Code:

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>

Die Idee ist, die Wörter im .descriptionDiv entsprechend der Position des Scanlichts anzuzeigen. Wenn möglich, möchte ich nur CSS verwenden, um diesen Effekt zu erzielen, und JavaScript nur verwenden, um den Scan zu verschieben (der weiter zu einer CSS-Animation wird). Ich habe versucht, einige Pseudoelemente zu verwenden, aber es hat nicht gut funktioniert. Hier ist ein Beispiel, wie diese Animation funktionieren sollte.

Leo Letto
quelle

Antworten:

81

Sie können transparenten Text mit Verlaufshintergrund verwenden. Ich habe background-attachment: fixedeine CSS-Variable verwendet, um die Hintergrundposition zu steuern.

Sie können die Hintergrundgröße erhöhen (in diesem Beispiel 500 Pixel), um die Glättung des Übergangs zu erhöhen.

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>

dgknca
quelle
38

Hier ist eine Idee, die Transformation verwendet, um eine bessere Leistung zu erzielen

Um es auf nur wenige Wörter anzuwenden, spielen Sie mit z-index


Eine weitere Idee, damit es mit jedem Hintergrund funktioniert, falls Sie Transparenz wünschen:

Temani Afif
quelle
23

Cooler Leuchtstab!

Ich gehe davon aus, dass dies für ein Logo ist und dass der Text weiterhin angezeigt werden soll, wenn der Leuchtstab den Text passiert hat.


Ich würde ein Pseudoelement für das Beschreibungselement verwenden, es oben platzieren und einen Verlaufshintergrund verwenden, der von transparent zu dunkelblau geht. Durch die Verwendung eines Verlaufs können Sie eine schöne Einblendung des Textes erzielen.

Ich würde dann den Startpunkt der dunklen Hintergrundfarbe mit einer CSS-Variablen festlegen, die ich über Ihre Onmousemove-Methode aktualisiere.

Der Code berücksichtigt keine unterschiedlichen Bildschirmgrößen. Daher müssen Sie wahrscheinlich Pixel in Prozent konvertieren, wenn Ihre Animation reagieren soll.

Ich habe auch deine Klassen in id geändert. Ich denke, es ist angemessener, mithilfe von IDs zu zeigen, dass das Element irgendwie von Javascript verwendet wird. Es ist auch einfacher, die Elemente an Variablen zu binden.

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ää
quelle
11

Ich habe gerade clipPath ausprobiert. Technisch macht es das, was Sie brauchen, aber die Leistung des animierten clipPath ist in Kombination mit dem Glow-Effekt ziemlich schlecht (aber viel besser ohne!). Möglicherweise würde das Bauen des Lichts aus so etwas wie einem Bild anstelle eines Kastenschattens dies verbessern. (wie könnte die Größe des äußersten Kastenschattens reduzieren)

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>

Ilmarinen123
quelle
Ich mochte diese Lösung, traurig, dass ich die Animation durch die Verwendung schwer machen würde clip-path, vor allem, weil ich irgendwann nur noch ein paar Wörter aus einer Phrase verstecken werde, sodass mehr Elemente mit dem Clip erforderlich wären, aber trotzdem danke! Nur ein Beispiel für die Funktionsweise der Animation hinzugefügt, kann hilfreich sein.
Leo Letto
Dies funktioniert leider erst, wenn die Maus das Ansichtsfenster betritt. Und wenn es in das Ansichtsfenster auf der rechten Seite gelangt, bleibt der Text sichtbar, obwohl Sie noch nicht darüber gescrollt haben.
TylerH
@ TylerH: Mein Beitrag war nicht als vollständige Lösung gedacht, sondern als Idee. Natürlich kann dem CSS ein Standard-Clip-Pfad hinzugefügt werden, aber das war hier nicht der Punkt. Es stellte sich auch heraus, dass das gewünschte Verhalten anders war als das, was ich verstand
Ilmarinen123
4

Versuchen Sie es wie folgt:

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>

Diese Lösung verwendet eine Abdeckung rechts vom Scan mit derselben Hintergrundfarbe wie das Banner. Das Cover bewegt sich mit dem Scan. Wenn sich der Scan nach rechts bewegt, wird der Text auf der linken Seite angezeigt. Es funktioniert, indem Sie in dieser Demo darauf klicken. Sie können es jedoch in JavaScript initiieren, es ist jedoch am besten für Sie.

sbgib
quelle
2
Das vollständige Anzeigen / Ausblenden per Mausklick scheint nicht der gewünschten Ausgabe von OP zu entsprechen, bei der die Leiste verschoben / der Text basierend auf der Cursorposition ausgeblendet wird.
TylerH
Einverstanden. Das ist genau das, was ich unter "Scannen" und "Verwenden von JavaScript nur zum Verschieben des Scans" verstanden habe.
sbgib
2

Scheint ein wenig knifflig. Die erste Lösung, die mir in den Sinn kommt, ist möglicherweise die Verwendung eines linearen Gradienten mit einem dynamischen "Haltepunkt" an der Position des Lichtbalkens. Der Farbverlauf reicht von dunkel -> transparent (Lichtbalkenposition) -> dunkel. Der Code sieht vielleicht so aus:

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

Ich bin mir nicht sicher, ob dies funktionieren wird und es würde wahrscheinlich irgendwo einen Kastenschatten brauchen, aber vielleicht gibt es Ihnen einige Ideen.

qwelling
quelle
Nur ein Beispiel für die Funktionsweise der Animation hinzugefügt, kann hilfreich sein.
Leo Letto