Używam feSpecularLighting filtra SVG i jestem animując położenie fePointLight, gdzie mousemoves na pulpicie.

<feSpecularLighting in="blur" surfaceScale="15" specularConstant="2.5" specularExponent="200" result="specOut" lighting-color="white">
    <fePointLight x="-10000" y="-10000" z="8000" />
</feSpecularLighting>

Nie mogę znaleźć sposobu, aby użyć efektów przejściowych CSS, aby złagodzić x i y zmiany atrybutu, nie sądzę, że filtry SVG działają w ten sposób. Więc zastanawiam się, czy można złagodzić zmiany liczby całkowitej x i y?

Patrz poniższy przykład roboczy, z bez luzu z x i y wartości atrybutów.

// lighting effect variables
let lightDist = 10000;
let currentLightPos = {
  x: -lightDist,
  y: -lightDist
};
let currentMousePos = {
  x: -1,
  y: -1
};
let windowWidth = $(window).outerWidth();
let windowHeight = $(window).outerHeight();
let ratio = {
  x: lightDist / (windowWidth / 2),
  y: lightDist / (windowHeight / 2)
};

// when the window is resized
$(window).on('resize', function() {

  // update lighting effect variables
  windowWidth = $(window).outerWidth();
  windowHeight = $(window).outerHeight();
  ratio = {
    x: lightDist / (windowWidth / 2),
    y: lightDist / (windowHeight / 2)
  };

});

// when the mouse is moved (desktop)
$(document).on('mousemove', function(e) {

  // get our current mouse position
  currentMousePos.x = e.pageX;
  currentMousePos.y = e.pageY;

  // if mouse is on right side of window
  if (currentMousePos.x > (windowWidth / 2)) {

    // calculate the positive light x position
    currentLightPos.x = ratio.x * (currentMousePos.x - (windowWidth / 2));

    // if mouse is on left side of window
  } else if (currentMousePos.x < (windowWidth / 2)) {

    // calculate the negative light x position
    currentLightPos.x = -lightDist + (ratio.x * currentMousePos.x);

  }

  // calculate the negative light y position
  // if mouse is on bottom side of window
  if (currentMousePos.y > (windowHeight / 2)) {

    // calculate the positive light y position
    currentLightPos.y = ratio.y * (currentMousePos.y - (windowHeight / 2));

    // if mouse is on top side of window
  } else if (currentMousePos.y < (windowHeight / 2)) {

    // calculate the negative light y position
    currentLightPos.y = -lightDist + (ratio.y * currentMousePos.y);

  }
  
  // console.log(currentLightPos.x, currentLightPos.y);

  // update shine filter fePointLight attributes
  $('fePointLight').attr({
    'x': currentLightPos.x,
    'y': currentLightPos.y
  });

});
.circle {
  width: 100px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
    <circle cx="50" cy="50" r="50" filter="url(#shine)" />
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <filter id="shine" filterUnits="objectBoundingBox" x="-10%" y="-10%" width="150%" height="150%">
        <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur" />
        <feSpecularLighting in="blur" surfaceScale="15" specularConstant="2.5" specularExponent="200" result="specOut" lighting-color="white">
            <fePointLight x="-10000" y="-10000" z="8000" />
        </feSpecularLighting>
        <feComposite in="specOut" in2="SourceAlpha" operator="in" result="specOut2" />
        <feComposite in="SourceGraphic" in2="specOut2" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" />
    </filter>
</svg>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Aby naśladować problem, jeśli mając, jeśli przesuniesz myszą poza przykładem okna i przynieś mysz z powrotem do okna w innej pozycji, zauważysz oświetlenie punktowe natychmiast skacze do nowo obliczonej pozycji.

Zastanawiam się, że można to złagodzić, więc liczba całkowita {X1}} i y zawsze łatwo łatwo w ich nowo obliczonej pozycji, zamiast natychmiast skakać do nowej pozycji, jeśli przynieś mysz do okna inna strona?

1
joshmoto 27 lipiec 2020, 20:13

1 odpowiedź

Najlepsza odpowiedź

(Na bok: podczas gdy twoje pytanie mówi "liczby całkowite", atrybuty {X0}} również wziąć również liczby rzeczywiste. Więc nie jest szkodliwe do płynnego łagodzenia.)

Możesz to zrobić za pomocą deklaratywnej animacji SMIL w dwóch krokach: Najpierw deklarujesz dwa <animate> elementy dla dwóch x i y <fePointLight>. restart="always" zapewnia, że ta animacja może ponownie uruchomić w dowolnym momencie, nawet jeśli aktualnie uruchomi się animacja. fill="freeze" zachowuje wartość po zakończeniu animacji.

Ale główną sztuczką jest ustawianie begin="indefinite". To odkłada początek animacji, dopóki nie zostanie uruchomiony z połączenia API JavaScript.

<fePointLight x="-10000" y="-10000" z="8000">
    <animate id="anim_x" attributeName="x"
             begin="indefinite" dur="0.5s" restart="always"
             from="-10000" to="-10000" fill="freeze" />
    <animate id="anim_y" attributeName="y"
             begin="indefinite" dur="0.5s" restart="always"
             from="-10000" to="-10000" fill="freeze" />
</fePointLight>

Opuściłem funkcję łagodzenia w domyślnym wartości linear. Aby uzyskać bardziej złożone zachowanie, składnia jest nieco bardziej zaangażowana niż funkcje łagodzące CSS. Coś porównywalnego z {X1}} wyglądałoby tak:

    <animate id="anim_x" attributeName="x"
             begin="indefinite" dur="0.5s" restart="always"
             calcMode="spline" values="-10000;-10000" keyTimes="0;1"
             keySplines=".5 0 .5 1" fill="freeze" />

Teraz, na każdym ruchu myszy, słuchacz zdarzeń musi robić trzy rzeczy:

  • Ustaw atrybut <animate from> do bieżącej animowanej wartości <fePointLight> element.x.animVal / element.y.animVal. To sprawia, że Animatyna rozpoczyna się od wszędzie, gdzie obecnie jest wartość, nawet podczas uruchamianej animacji.
  • Ustaw atrybut <animate to> do wartości currentLightPos, aby przenieść animację w kierunku celu.
  • Uruchom animację metodą <fePointLight> element.beginElement(), anulowanie aktualnie uruchomionej animacji.

(Jeśli używałeś splajnu, musisz ustawić atrybut values zamiast from i to.)

  const pointLight = $('fePointLight');
  const lightX = $('#anim_x');
  const lightY = $('#anim_y');

  lightX.attr({
    from: pointLight.prop('x').animVal,
    to: currentLightPos.x
  });
  lightX[0].beginElement();
  lightY.attr({
    from: pointLight.prop('y').animVal,
    to: currentLightPos.y
  });
  lightY[0].beginElement();

Pozwala to, aby światło punktowe rodzaju "Chase" bieżącej pozycji myszy, aż do zatrzymania ruchu myszy, a animacja ma szansę uruchomić do końca.

// lighting effect variables
let lightDist = 10000;
let currentLightPos = {
  x: -lightDist,
  y: -lightDist
};
let currentMousePos = {
  x: -1,
  y: -1
};
let windowWidth = $(window).outerWidth();
let windowHeight = $(window).outerHeight();
let ratio = {
  x: lightDist / (windowWidth / 2),
  y: lightDist / (windowHeight / 2)
};

const pointLight = $('fePointLight');
const lightX = $('#anim_x');
const lightY = $('#anim_y');

// when the window is resized
$(window).on('resize', function() {

  // update lighting effect variables
  windowWidth = $(window).outerWidth();
  windowHeight = $(window).outerHeight();
  ratio = {
    x: lightDist / (windowWidth / 2),
    y: lightDist / (windowHeight / 2)
  };

});

// when the mouse is moved (desktop)
$(document).on('mousemove', function(e) {

  // get our current mouse position
  currentMousePos.x = e.pageX;
  currentMousePos.y = e.pageY;

  // if mouse is on right side of window
  if (currentMousePos.x > (windowWidth / 2)) {

    // calculate the positive light x position
    currentLightPos.x = ratio.x * (currentMousePos.x - (windowWidth / 2));

    // if mouse is on left side of window
  } else if (currentMousePos.x < (windowWidth / 2)) {

    // calculate the negative light x position
    currentLightPos.x = -lightDist + (ratio.x * currentMousePos.x);

  }

  // calculate the negative light y position
  // if mouse is on bottom side of window
  if (currentMousePos.y > (windowHeight / 2)) {

    // calculate the positive light y position
    currentLightPos.y = ratio.y * (currentMousePos.y - (windowHeight / 2));

    // if mouse is on top side of window
  } else if (currentMousePos.y < (windowHeight / 2)) {

    // calculate the negative light y position
    currentLightPos.y = -lightDist + (ratio.y * currentMousePos.y);

  }
  
  // console.log(currentLightPos.x, currentLightPos.y);

  // update shine filter fePointLight attributes
  lightX.attr({
    from: pointLight.prop('x').animVal,
    to: currentLightPos.x
  });
  lightX[0].beginElement();
  lightY.attr({
    from: pointLight.prop('y').animVal,
    to: currentLightPos.y
  });
  lightY[0].beginElement();

});
.circle {
  width: 100px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
    <circle cx="50" cy="50" r="50" filter="url(#shine)" />
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <filter id="shine" filterUnits="objectBoundingBox" x="-10%" y="-10%" width="150%" height="150%">
        <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur" />
        <feSpecularLighting in="blur" surfaceScale="15" specularConstant="2.5" specularExponent="200" result="specOut" lighting-color="white">
          <fePointLight x="-10000" y="-10000" z="8000">
            <animate id="anim_x" attributeName="x"
                     begin="indefinite" dur="0.5s" restart="always"
                     from="-10000" to="-10000" fill="freeze" />
            <animate id="anim_y" attributeName="y"
                     begin="indefinite" dur="0.5s" restart="always"
                     from="-10000" to="-10000" fill="freeze" />
          </fePointLight>
        </feSpecularLighting>
        <feComposite in="specOut" in2="SourceAlpha" operator="in" result="specOut2" />
        <feComposite in="SourceGraphic" in2="specOut2" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" />
    </filter>
</svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
2
ccprog 27 lipiec 2020, 20:14