Search code examples
cssanimationcanvasmix-blend-mode

Is it possible to use a static background behind an animated canvas with mix-blend-mode?


I'm trying to create an animation where each element would change its colour based on the parent div's background. I'm using pts.js to make the animation. It draws everything on a canvas.

Here's my setup: fiddle.

After adding mix-blend-mode: difference; to the canvas, the whole gradient background disappears despite the fact that the canvas (#pt) has its background set to transparent. I want the animated lines to dynamically change their colour just like the static text does. How can I achieve it? Is it even possible with pure CSS?

enter image description here


Solution

  • You can put the gradient on top of the canvas (using a ::before psuedo-element), and then mix-blend-mode will work:

    $(document).ready(() => {
      Pts.namespace(window);
      const run = Pts.quickStart("#pt", "transparent");
      let pts = new Group();
      space.add({
        start: () => {
          pts = Create.distributeRandom(space.bound, 50);
        },
        animate: (time, ftime) => {
          const perpend = new Group(
            new Pt(0, 0),
            new Pt(0, window.innerWidth)
          ).op(Line.perpendicularFromPt);
          pts.rotate2D(0.0055, space.center);
          pts.forEach((p, i) => {
            const lp = perpend(p);
            form.stroke(`rgba(255,255,255,255`, 1.01).line([p, lp]);
            form.fillOnly(`#000000`).point(p, 1);
          });
        },
    
      });
      space.bindMouse().bindTouch().play();
    });
    html {
      -moz-osx-font-smoothing: grayscale;
      -webkit-font-smoothing: antialiased;
      text-rendering: optimizeLegibility;
      text-size-adjust: 100%;
    }
    
    #staticBg {
      height: 100vh;
      width: 100vw;
      top: 0;
      left: 0;
      position: fixed;
    }
    
    #staticBg::before {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background: linear-gradient(90deg, rgba(0, 0, 0, 1) 20%, rgba(255, 255, 255, 1) 60%);
      content: '';
      mix-blend-mode: difference;
      z-index: 1;
    }
    
    #pt {
      height: 100vh;
      width: 100vw;
      top: 0;
      left: 0;
      position: fixed;
    }
    
    .text {
      color: white;
      font-size: 50px;
      text-align: center;
      width: 100vw;
      mix-blend-mode: difference;
      z-index: 1;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pts/0.8.8/pts.min.js"></script>
    <div id="staticBg">
      <canvas id="pt"></canvas>
      <div class="text">TEST STRING</div>
    </div>