Текучая окраска свга в разные секции

Я пытаюсь изменить цвет SVG в соответствии с его текущим фоном сечения... его работа с фигурами, но как только я изменю его на путь, он будет переходить цвета вместо плавного изменения... почему это так?

Это то, чего я хочу достичь (плавный переход цветов)

var sections = document.querySelectorAll('section');
var logo = document.querySelector('#logo');
var nextColor = document.querySelector('#nextColor');
var currentColor = document.querySelector('#currentColor');
var initialHeight = logo.clientHeight;
var lastScrollPosition = 0;

// Set current section color
sections.forEach(function (section) {
  var top = section.offsetTop - window.pageYOffset;
  if (top <= logo.offsetTop && (top + section.clientHeight) >= logo.offsetTop) {
    currentColor.setAttribute('fill', section.dataset.color);
  }
});

window.addEventListener('scroll', function () {
  sections.forEach(function (section) {
    var top = section.offsetTop - window.pageYOffset;
    var offset = top - logo.offsetTop;
    
    // If the top edge of the section is behind the logo
    if (top <= logo.offsetTop) {
      // Make sure the section color has its initial height
      currentColor.setAttribute('height', initialHeight);
      // If the logo is still inside the section, fill it with the section color
      var bottom = top + section.clientHeight;
      if (bottom >= logo.offsetTop) {
        currentColor.setAttribute('fill', section.dataset.color);
      }
      
      return;
    }
    
    // If logo collides with the lower section
    if (offset <= logo.clientHeight) {
      nextColor.setAttribute('fill', section.dataset.color);
      currentColor.setAttribute('height', offset);
    }
  });
});
#logo {
  position: fixed;
  position: fixed;
  top: 50%;
  left: 50%;
  margin-top: -50px;
  margin-left: -50px;
}

section {
  height: 100vh
}

#dark {
  background-color: #2a2a2a;
}
<div id="logo">
  <svg height="100" width="100">
    <rect id="nextColor" width="100" height="100"/>
    <rect id="currentColor" y="0" width="100" height="100"/>
  </svg>
</div>

<div class="sections">
  <section data-color="#000000" id="light"></section>
  <section data-color="#ffffff" id="dark"></section>
  <section data-color="#cccccc" id="light2"></section>
</div>

Это то, что я получил, но не плавно, как в приведенном выше примере:

var sections = document.querySelectorAll('section');
var logo = document.querySelector('#logo');
var nextColor = document.querySelector('#nextColor');
var currentColor = document.querySelector('#currentColor');
var initialHeight = logo.clientHeight;
var lastScrollPosition = 0;

// Set current section color
sections.forEach(function (section) {
  var top = section.offsetTop - window.pageYOffset;
  if (top <= logo.offsetTop && (top + section.clientHeight) >= logo.offsetTop) {
    currentColor.setAttribute('fill', section.dataset.color);
  }
});

window.addEventListener('scroll', function () {
  sections.forEach(function (section) {
    var top = section.offsetTop - window.pageYOffset;
    var offset = top - logo.offsetTop;
    
    // If the top edge of the section is behind the logo
    if (top <= logo.offsetTop) {
      // Make sure the section color has its initial height
      currentColor.setAttribute('height', initialHeight);
      // If the logo is still inside the section, fill it with the section color
      var bottom = top + section.clientHeight;
      if (bottom >= logo.offsetTop) {
        currentColor.setAttribute('fill', section.dataset.color);
      }
      
      return;
    }
    
    // If logo collides with the lower section
    if (offset <= logo.clientHeight) {
      nextColor.setAttribute('fill', section.dataset.color);
      currentColor.setAttribute('height', offset);
    }
  });
});
#logo {
  position: fixed;
  position: fixed;
  top: 50%;
  left: 50%;
  margin-top: -50px;
  margin-left: -50px;
}

section {
  height: 100vh
}

#blue {
  background-color: #b4da55;
}
<div id="logo">

  <svg height="48" xmlns="http://www.w3.org/2000/svg">
    
    <path height="48" id="nextColor" d="M5.8 0v36.3h10.7V48l25.6-11.7V0H5.8zm10.5"/>
    
    <path height="48" id="currentColor" y="0" d="M5.8 0v36.3h10.7V48l25.6-11.7V0H5.8zm10.5"/>
 
  </svg>
</div>

<div class="sections">
  <section data-color="#b4da55" id="white"></section>
  <section data-color="#ffffff" id="blue"></section>
  <section data-color="#b4da55" id="white"></section>
</div>

2 ответа

Решение

Это может быть достигнуто путем обрезки белого логотипа внутри прямоугольника.

Логика заключается в следующем:

  • зеленый логотип всегда отображается
  • всякий раз, когда "синий" участок перекрывается с логотипом, перемещайте прямоугольник так, чтобы он покрывал перекрывающуюся поверхность (измените его положение и высоту)
  • белый логотип обрезается внутри этого прямоугольника и появляется сверху зеленого логотипа

Обновлено ниже. Если то, что происходит, не совсем понятно для вас, вы можете просто переместить rect определение за пределами clipPath Таким образом, вы будете визуализировать, как его положение и видимость изменяются при прокрутке.

Примечание: вы можете отменить прослушивание события прокрутки, чтобы обеспечить хорошую производительность на вашей странице.

var logo = document.querySelector('#logo')
    , clipRect = document.querySelector('#clipRect')
    , blueSection = document.querySelector('#blue')

window.addEventListener('scroll', function () {

    // position of the top of the blue section
    var top = blueSection.offsetTop - window.pageYOffset
        // position of the bottom of the blue section
        , bottom = blueSection.offsetTop + blueSection.clientHeight - window.pageYOffset
    
    // offset between top of the logo and top of the blue section
    var offset = top - logo.offsetTop


    if ( offset <= logo.clientHeight && bottom >= logo.offsetTop) {
    // blue section overlaps with the logo

        clipRect.setAttribute('y', Math.max(offset, 0))
        
        clipRect.setAttribute('height', bottom - logo.offsetTop)
    }
    else {
    // blue section does not overlap with the logo
    
        // make sure the clip-path isn't visible
        clipRect.setAttribute('y', 0)
        clipRect.setAttribute('height', 0)
    }
    
})
#logo {
  position: fixed;
  position: fixed;
  top: 50%;
  left: 50%;
  margin-top: -50px;
  margin-left: -50px;
}

section {
  height: 100vh
}

#blue {
  background-color: #b4da55;
}

#currentColor {
  fill: #b4da55;
}

#nextColor {
  fill: white;
}

/*
following useful only for testing purpose
*/
#clipRect {
  fill: red;
  fill-opacity: .5;
}
<div id="logo">

  <svg height="48" xmlns="http://www.w3.org/2000/svg">
 <defs>
  <clipPath id="clipping">
    <rect id="clipRect" x="0" y="0" width="48" height="0"></rect>
  </clipPath>
 </defs>
    
    <path height="48" id="currentColor" d="M5.8 0v36.3h10.7V48l25.6-11.7V0H5.8zm10.5"/>
    <path height="48" id="nextColor" d="M5.8 0v36.3h10.7V48l25.6-11.7V0H5.8zm10.5" clip-path="url(#clipping)"/>

     
  </svg>
</div>

<div class="sections">
  <section data-color="#b4da55" id="white"></section>
  <section data-color="#ffffff" id="blue"></section>
  <section data-color="#b4da55" id="white"></section>
</div>

Ваш код не использует форму прямоугольника SVG, как я думаю, что вы думаете, это так. Чтобы продемонстрировать это, я изменил rectс ellipses, а затем изменили атрибуты соответствующим образом, т.е. y в cy, width в rx, а также height в ry, Результат показывает, что форма, во-первых, показывает только часть всей фигуры (нижняя правая четверть?) И, во-вторых, фактически сдавливает форму, когда граница сечения перекрывает ее. Вы не видите этих вещей в своем первоначальном примере, потому что четверть прямоугольника по-прежнему является прямоугольником, а сжатый прямоугольник также остается прямоугольником. Таким образом, то, что вы думаете, работает в вашем первоначальном примере, на самом деле не работает. Таким образом, проблема не в использовании пути, а в вашем исходном коде.

Если вы хотите использовать только прямоугольники, вы можете использовать исходный код, как я полагаю, потому что он производит желаемый визуальный эффект. Однако, если вы использовали только прямоугольник в качестве доказательства концепции, и ваша реальная конечная цель - использовать путь, то вам нужен другой подход. Вы могли бы рассмотреть возможность использования clipPath/clip-path "разрезать" путь на части, поскольку граница раздела проходит через него, но для этого потребуется значительная переработка вашего кода.

var sections = document.querySelectorAll('section');
var logo = document.querySelector('#logo');
var nextColor = document.querySelector('#nextColor');
var currentColor = document.querySelector('#currentColor');
var initialHeight = logo.clientHeight;
var lastScrollPosition = 0;

// Set current section color
sections.forEach(function (section) {
  var top = section.offsetTop - window.pageYOffset;
  if (top <= logo.offsetTop && (top + section.clientHeight) >= logo.offsetTop) {
    currentColor.setAttribute('fill', section.dataset.color);
  }
});

window.addEventListener('scroll', function () {
  sections.forEach(function (section) {
    var top = section.offsetTop - window.pageYOffset;
    var offset = top - logo.offsetTop;
    
    // If the top edge of the section is behind the logo
    if (top <= logo.offsetTop) {
      // Make sure the section color has its initial height
      currentColor.setAttribute('ry', initialHeight);
      // If the logo is still inside the section, fill it with the section color
      var bottom = top + section.clientHeight;
      if (bottom >= logo.offsetTop) {
        currentColor.setAttribute('fill', section.dataset.color);
      }
      
      return;
    }
    
    // If logo collides with the lower section
    if (offset <= logo.clientHeight) {
      nextColor.setAttribute('fill', section.dataset.color);
      currentColor.setAttribute('ry', offset);
    }
  });
});
#logo {
  position: fixed;
  position: fixed;
  top: 50%;
  left: 50%;
  margin-top: -50px;
  margin-left: -50px;
}

section {
  height: 100vh
}

#dark {
  background-color: #2a2a2a;
}
<div id="logo">
  <svg height="100" width="100">
    <ellipse id="nextColor" rx="100" ry="100"/>
    <ellipse id="currentColor" cy="0" rx="100" ry="100"/>
  </svg>
</div>

<div class="sections">
  <section data-color="#000000" id="light"></section>
  <section data-color="#ffffff" id="dark"></section>
  <section data-color="#cccccc" id="light2"></section>
</div>

Другие вопросы по тегам