skip to Main Content

(perspective grid that might be useful to illustrate what I mean) I’ve been trying to create what I’d describe as a "dynamic perspective infinite marquee". Specifically I’m using this video as a reference.

Screenshot of it attached.

Any help would be massively appreciated.

I’ve been pulling my hair out trying to adapt this codepen, which has a similar outcome, but all my attempts to turn it into an infinite scroll instead of working from the mouse’s x-axis have failed.

The transform in the previous link does give a similar effect (added so I can link the codepen):

.left-3d {
  position: absolute;
  transform-origin: right center;
  transform: rotateY(100deg);
  top: 0;
  bottom: 0;
  right: 100%;
}

#left {
  transform: translateX(0%);
}

#center {
  transform: translateX(-100%);
}

.right-3d {
  position: absolute;
  transform-origin: left center;
  transform: rotateY(-100deg);
  top: 0;
  bottom: 0;
  left: 100%;
}

#right {
  transform: translateX(-200%);
}

So far I’ve been using vanilla html/css/js. Someone mentioned to me this effect would be easy to replicate in Three.js/R3F but I don’t know where to start on that.

Any help would be massively appreciated!

EDIT: Apologies for any faux pas in this question, I’m very new to this

2

Answers


  1. here i made small infinite scroll example and i think you can a little adapt it and use:
    small infinite y scroll example on codepen (no rendering on canvas)

    <div class="fullSize">
        <div class="block" style="background: #f88;">any html<br>structure 1</div>
        <div class="block" style="background: #8f8;">any html<br>structure 2</div>
        <div class="block" style="background: #88f;">any html<br>------ structure 3 -------</div>
        <div class="block" style="background: #888;">any html<br>structure 4</div>
    </div>  
    

    the main idea of this realization of infinite scroll is using:
    mod(yScroll+elementCenter.x, parentElement.width) – elementCenter.x
    to set element position

    .fullSize {
        --fullWidth: 600px;
        width: var(--fullWidth);
        display: flex;
        justify-content: space-around;
        outline: solid 2px #f00;
        overflow: hidden;
    }
    
    .fullSize .block {
        outline: solid 1px #000;
        padding: 10px;
        --pos: calc(mod(calc(var(--yScroll) + var(--xPos)), var(--fullWidth)) - var(--xPos));
        transform: translateX(var(--pos));
        transition-duration: 0.1s;
    

    this script calculate each element x center position which must be scrolled and save it as css variable so css can calculate value for translateX
    Also it set scroll from wheel event and add some behaviour when element is going to jump from end to start or vice versa (element become transparent before jump, (if 1/4 width of element is out of bounds it become transparent))

    function init(
         fullSize = document.querySelector('.infiniteScroll > .fullSize')
        ,blockSelector = '.block'
    ) {
        const fullSizeRect = fullSize.getBoundingClientRect()
        const blocks = [...fullSize.querySelectorAll(blockSelector)].map(a=>({el: a, rect: a.getBoundingClientRect()}))
        blocks.forEach((block)=>{
            block.el.style.setProperty('--xPos', `${block.rect.x - fullSizeRect.x + block.rect.width/2}px`)
        })
    
        fullSize.addEventListener('wheel', w=>{
            w.preventDefault()
            const scrollVal = parseFloat(fullSize.style.getPropertyValue('--yScroll').slice(0,-2)) || 0;
            const scrollLimit = fullSizeRect.width
            //                       to make scroll always in bounds from 0 to +limit
            const limited = (scrollLimit + (scrollVal + w.deltaY/5)%scrollLimit)%scrollLimit 
    
            blocks.forEach((block)=>{
                const xVal = parseFloat(block.el.style.getPropertyValue('--xPos').slice(0,-2)) || 0;
                if ( Math.abs((scrollVal+xVal)/scrollLimit-1) < 1/4/blocks.length ) {
                    block.el.style.opacity = '0.2'; // this must be 0, this is not 0 only to show how it moves
                } else {
                    block.el.style.opacity = '';
                }                   
            })
    
            fullSize.style.setProperty('--yScroll', limited+'px')
        });         
    }       
    
    init()
    

    small infinite y scroll example on codepen (no rendering on canvas)

    Login or Signup to reply.
  2. CSS is pretty good at 3d so this snippet gives a pure CSS method.

    First the ‘room’ is drawn. This consists of 3 walls with the two side walls at 90 degrees to the back wall. This snippet makes them all an equal size.

    The ‘observer’ is placed using the perspective CSS property, you can change that plus the size of the walls as you wish.

    The images are then drawn as background images on each wall. The fairly common way of creating an infinite marquee on each – having two copies and moving the strip by just 50% of its width, so giving a continuous flow – is used.

    Each wall positions its after pseudo element which has this strip of images slightly differently so to start with the left wall shows the first image, the back wall the second and the right all the third.

    Then as they move the images look as though they are going through 90degrees to the next wall.

    You will want to play with the sizing and the number of images to suit your particular use case.

    <style>
      * {
        margin: 0;
      }
      
      body {
        width: 100vw;
        height: 100vh;
      }
      
      .room {
        position: relative;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 50vmin;
        height: 50vmin;
        transform-style: preserve-3d;
        perspective: 100vmin;
      }
      
      .wall {
        --w: 50vmin;
        width: var(--w);
        height: var(--w);
        position: absolute;
        overflow: hidden;
        box-sizing: border-box;
        border: solid black 1px;
      }
      
      .wall::after {
        content: '';
        position: absolute;
        width: calc(var(--w) * 6);
        height: 100%;
        right: 0;
        transform: translateX(var(--x));
        --img1: url("https://images.unsplash.com/photo-1492970471430-bc6bd7eb2b13?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=9893bc89e46e2b77a5d8c091fbba04e9&auto=format&fit=crop&w=1355&q=80");
        --img2: url("https://images.unsplash.com/photo-1501707305551-9b2adda5e527?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=0cf5887247e17503ce4e542d00d86b9d&auto=format&fit=crop&w=1335&q=80");
        --img3: url("https://images.unsplash.com/photo-1496749843252-699a989877a1?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=fe5da9650707e5a93c8c3cf164c2e74b&auto=format&fit=crop&w=1375&q=80");
        background-image: var(--img1), var(--img2), var(--img3), var(--img1), var(--img2), var(--img3);
        background-size: calc(var(--w) / 3) auto;
        background-position-x: 0, var(--w), calc(2 * var(--w)), calc(3 * var(--w)), calc(4 * var(--w)), calc(5 * var(--w)), calc(6 * var(--w));
        background-position-y: center;
        background-repeat: no-repeat;
        animation: move 30s linear infinite;
      }
      
      @keyframes move {
        to {
          transform: translateX(calc(var(--x) + 50%));
        }
      }
      
      .center {
        --x: 0vmin;
      }
      
      .left {
        transform: translateX(-100%) rotateY(90deg);
        transform-origin: right center;
      }
      
      .left::after {
        --x: var(--w);
      }
      
      .right {
        transform: translateX(100%) rotateY(-90deg);
        transform-origin: left center;
      }
      
      .right::after {
        --x: calc(2 * var(--w));
      }
    </style>
    
    <body>
      <div class="room">
        <div class="wall center"></div>
        <div class="wall left"></div>
        <div class="wall right"></div>
      </div>
    
      <body>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search