如何使 iframe 的行为与水平滚动区域中的其他媒体元素一样?

How can I make an iframe behave like other media elements in a horizontal scrolling area?

提问人:Anna_B 提问时间:7/18/2023 最后编辑:journpyAnna_B 更新时间:8/7/2023 访问量:557

问:

这是我的代码:

$(document).ready(function() {
  $('.scrollable-area').on('wheel', function(e) {
    var scrollLeft = $(this).scrollLeft();
    var width = $(this).get(0).scrollWidth - $(this).width();
    var deltaY = e.originalEvent.deltaY;
    var deltaX = e.originalEvent.deltaX;
    var newScrollLeft = scrollLeft + deltaY + deltaX;
    if ((deltaY > 0 && newScrollLeft < width) ||
      (deltaY < 0 && newScrollLeft > 0)) {
      e.preventDefault();
    }
    if (newScrollLeft <= 0) {
      $(this).scrollLeft(0);
    } else if (newScrollLeft >= width) {
      $(this).scrollLeft(width);
    } else {
      $(this).scrollLeft(newScrollLeft);
    }
  });
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.gallery-container {
  position: relative;
  width: 100%;
  height: 90vh;
  background-color: yellow;
  overflow: hidden;
}

.scrollable-area {
  width: 100%;
  height: 100%;
  overflow-x: auto;
}

.gallery-items {
  display: flex;
  min-width: 100%;
  height: 100%;
}

.gallery-item {
  flex: 0 0 auto;
  height: 100%;
  display: flex;
}

.gallery-item img {
  max-width: 100%;
  height: auto;
  object-fit: contain;
}

.gallery-item iframe {
  background-color: blue;
  width: auto;
  width: 800px;
}
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>

<div class="gallery-container">
  <div class="scrollable-area">
    <div class="gallery-items">
      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG">
      </div>
      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/d/da/Sky_landscape.jpg">
      </div>
      <div class="gallery-item">
        <iframe src="https://player.vimeo.com/video/584985260" frameborder="0" allow="fullscreen"></iframe>
      </div>
      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG">
      </div>
    </div>
  </div>
</div>

如果将光标放在媒体库上并正常向下滚动,则首先将水平滚动浏览库。这是故意的。但是,当包含 iframe 时,它会停止。我已经尝试在滚动过程中防止指针事件,但它会导致其他一些问题,例如无法单击视频的播放按钮。

此外,找到一种方法来始终将 iframe 调整为视频的大小并适应水平画廊的整体高度会很酷。有谁知道这是怎么回事?

javascript jquery css iframe-resizer

评论

0赞 sleepwalk 7/23/2023
这就是你要问的吗?stackoverflow.com/questions/10082155/......
0赞 Anna_B 7/23/2023
不,它不会改变任何东西。
1赞 Anna_B 8/3/2023
你好土豆:)
0赞 Adam Zabrocki 8/7/2023
这是一个扭曲的问题,因为我们无法控制跨域 iframe 上的事件。如果使用具体的视频提供商,我们可以用自定义解决方案覆盖视频的表示层,以播放、静音等。就vimeo而言,这个包会很有用:github.com/vimeo/player.js。另一种解决方案是创建覆盖播放栏的附加层,该层将仅在该位置侦听指针事件。但是,这一层的位置当然可能因视频提供商而异

答:

0赞 Wakil Ahmed 7/23/2023 #1

修改 jQuery 代码以处理图像和 iframe 的滚动事件,还可以动态调整 iframe 的大小以适合其内容,并相应地调整水平库的高度。

<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script>
  $(document).ready(function() {
    $('.scrollable-area').on('wheel', function(e) {
      var scrollLeft = $(this).scrollLeft();
      var width = $(this).get(0).scrollWidth - $(this).width();
      var deltaY = e.originalEvent.deltaY;
      var deltaX = e.originalEvent.deltaX;
      var newScrollLeft = scrollLeft + deltaY + deltaX;
      if ((deltaY > 0 && newScrollLeft < width) ||
        (deltaY < 0 && newScrollLeft > 0)) {
        e.preventDefault();
      }
      if (newScrollLeft <= 0) {
        $(this).scrollLeft(0);
      } else if (newScrollLeft >= width) {
        $(this).scrollLeft(width);
      } else {
        $(this).scrollLeft(newScrollLeft);
      }
    });

    // Resize iframes to fit their content
    function resizeIframes() {
      $('.gallery-item iframe').each(function() {
        this.height = this.contentWindow.document.body.scrollHeight + 'px';
      });
    }

    // Call the function on initial load and on window resize
    resizeIframes();
    $(window).resize(function() {
      resizeIframes();
    });
  });
</script>

评论

0赞 Anna_B 7/23/2023
这是行不通的。如果光标位于 iframe 上方,则滚动将停止。
2赞 herrstrietzel 7/24/2023 #2

您可以向滚动 div 添加覆盖层,以禁用 iframe 指针事件,并在鼠标移动时重新启用指针事件。

根据 trincot 的回答“如何检测鼠标移动何时停止”
,我们可以使用自定义事件。
mousestop

  1. 我们通过附加覆盖来禁用 iframe 指针事件。
  2. 我们根据鼠标移动切换/丢弃叠加层:
    如果鼠标移动停止(使用 setTimeout 延迟),我们将添加/激活叠加层以阻止 iframe 内容中的指针事件 - 启用滚轮滚动事件 在滑块换行上移动时 - 我们可以访问 iframe/滑动按钮。
  3. iframe 尺寸只需通过 16/9 等方式设置aspect-ratio

let galleryWrap = document.querySelector('.gallery-container');


(function(mouseStopDelay) {
  var timeout;
  document.addEventListener('mousemove', function(e) {
    clearTimeout(timeout);
    timeout = setTimeout(function() {
      var event = new CustomEvent("mousestop", {
        detail: {
          clientX: e.clientX,
          clientY: e.clientY
        },
        bubbles: true,
        cancelable: true
      });
      e.target.dispatchEvent(event);
    }, mouseStopDelay);
  });
}(1000));

// Example use
galleryWrap.addEventListener('mousestop', function(e) {
  galleryWrap.classList.add('inactive')
});

galleryWrap.addEventListener('mousemove', function(e) {
  galleryWrap.classList.remove('inactive')
});

galleryWrap.addEventListener('click', function(e) {
  galleryWrap.classList.remove('inactive')
});


$(document).ready(function() {
  let galleryWrap = $('.gallery-container')
  let scrollArea = $('.scrollable-area')
  galleryWrap.on('wheel', function(e) {
    scrolling = true;
    var scrollLeft = scrollArea.scrollLeft();
    var width = scrollArea.get(0).scrollWidth - scrollArea.width();
    var deltaY = e.originalEvent.deltaY;
    var deltaX = e.originalEvent.deltaX;
    var newScrollLeft = scrollLeft + deltaY + deltaX;
    if ((deltaY > 0 && newScrollLeft < width) ||
      (deltaY < 0 && newScrollLeft > 0)) {
      e.preventDefault();
    }
    if (newScrollLeft <= 0) {
      scrollArea.scrollLeft(0);
    } else if (newScrollLeft >= width) {
      scrollArea.scrollLeft(width);
    } else {
      scrollArea.scrollLeft(newScrollLeft);
    }
  });
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.gallery-container {
  position: relative;
  width: 100%;
  height: 250px;
  background-color: yellow;
  overflow: hidden;
}

.scrollable-area {
  width: 100%;
  height: 100%;
  overflow-x: auto;
}

.gallery-items {
  display: flex;
  min-width: 100%;
  height: 100%;
}

.gallery-item {
  flex: 0 0 auto;
  height: 100%;
  display: flex;
}

.gallery-item img {
  max-width: 100%;
  height: auto;
  object-fit: contain;
}

.gallery-item iframe {
  width: 100%;
  aspect-ratio: 16/9;
  display: block;
}

.scrollable-area {
  position: relative;
}

.gallery-container.inactive:after {
  content: "";
  position: absolute;
  display: block;
  left: 0;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  background: red;
  opacity: 0.1;
  z-index: 100;
}
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>

<div class="gallery-container inactive">
  <div class="scrollable-area">
    <div class="gallery-items">

      <div class="gallery-item">
        <iframe frameborder="0" scrolling="no" srcdoc='<html><body style="margin:0; padding:0;"><video style="margin:0; padding:0; width:100%; aspect-ratio:16/9" controls muted playsinline controls preload="meta"><source src="https://cdn.jsdelivr.net/npm/[email protected]/video.mp4#t0" type="video/mp4"></video></html></body>'></iframe>
      </div>

      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG">
      </div>
      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/d/da/Sky_landscape.jpg">
      </div>

      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG">
      </div>
    </div>
  </div>
</div>

我添加了半透明的红色调来说明叠加切换。

评论

0赞 Anna_B 7/24/2023
有趣。这是有道理的。我仍然觉得它没有发挥应有的作用。如果我开始滚动,那么它不能直接与视频配合使用。但是在无缝滚动一段时间后,它就可以工作了。您知道如何改进它以使其始终直接工作吗?
0赞 Hugo Fang 8/1/2023 #3

我认为你可以用它来实现这一点。我知道这不是一个好办法。如果你能更新源网站,没问题。setTimeoutiframe

$(document).ready(function () {
        $(".scrollable-area").on("wheel", function (e) {
          var scrollLeft = $(this).scrollLeft()
          var width = $(this).get(0).scrollWidth - $(this).width()
          var deltaY = e.originalEvent.deltaY
          var deltaX = e.originalEvent.deltaX
          var newScrollLeft = scrollLeft + deltaY + deltaX
          if (
            (deltaY > 0 && newScrollLeft < width) ||
            (deltaY < 0 && newScrollLeft > 0)
          ) {
            e.preventDefault()
          }
          if (newScrollLeft <= 0) {
            $(this).scrollLeft(0)
          } else if (newScrollLeft >= width) {
            $(this).scrollLeft(width)
          } else {
            $(this).scrollLeft(newScrollLeft)
          }
        })

        let frameBack = document.querySelector(".background")
        frameBack.addEventListener("mousemove", (e) => {
          frameBack.classList.remove("z-1")
          frameBack.classList.add("-z-1")
          setTimeout(() => {
            frameBack.classList.remove("-z-1")
            frameBack.classList.add("z-1")
          }, 100)
        })
      })
* {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      .gallery-container {
        position: relative;
        width: 100%;
        height: 90vh;
        background-color: yellow;
        overflow: hidden;
      }

      .scrollable-area {
        width: 100%;
        height: 100%;
        overflow-x: auto;
      }

      .gallery-items {
        display: flex;
        min-width: 100%;
        height: 100%;
      }

      .gallery-item {
        flex: 0 0 auto;
        height: 100%;
        display: flex;
      }

      .gallery-item img {
        max-width: 100%;
        height: auto;
        object-fit: contain;
      }

      .gallery-item iframe {
        background-color: blue;
        width: auto;
        width: 800px;
      }

      .relative {
        position: relative;
      }

      .background {
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
      }

      .z-1 {
        z-index: 1;
      }

      .-z-1 {
        z-index: -1;
      }
 <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>

    <div class="gallery-container inactive">
      <div class="scrollable-area">
        <div class="gallery-items">
          <div class="gallery-item">
            <img
              src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG"
            />
          </div>
          <div class="gallery-item">
            <img
              src="https://upload.wikimedia.org/wikipedia/commons/d/da/Sky_landscape.jpg"
            />
          </div>
          <div class="gallery-item relative">
            <iframe
              src="https://player.vimeo.com/video/584985260"
              frameborder="0"
              allow="fullscreen"
            ></iframe>
            <div class="background z-1"></div>
          </div>
          <div class="gallery-item">
            <img
              src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG"
            />
          </div>
        </div>
      </div>
    </div>

评论

0赞 Anna_B 8/3/2023
嗯,这个想法很有趣。但是使用视频的按钮效果不佳。
0赞 Hugo Fang 8/3/2023
我以为用户可以在单击视频中的按钮之前移动鼠标。如果用户移动鼠标,则可以单击按钮。但是移动鼠标 100 毫秒后,用户无法单击按钮,因为 iframe 上方有一个隐藏层。要删除隐藏层 100 毫秒,用户应移动鼠标。除非您更新 iframe 源网站,否则似乎没有好方法。
0赞 ChipChop Gizmo 8/7/2023 #4

我过去使用过一种令人费解的方法。它是将事件侦听器注入到 iFrame 中,然后返回到父页面。

我刚刚做了一个非常快速的测试,它有点适用于你的例子。

$(document).ready(function() {
   
   ....your code here.....
   
   ...and just add this...
    //this will inject event listeners to any iframe in your page, you can do the selector any way you want, this is just an example
    let iframes = document.getElementsByTagName('iframe')
    for(let iframe of iframes){
        iframe.contentWindow.addEventListener("wheel", function(e){

           //IMPORTANT: you need to call a function outside this scope as this is actually being executed inside the iframe, not here. We are basically telling the iframe to fire a function "someFunction()" inside it's parent which is this window where we are.

            window.parent.someFunction(e) //someFunction() is further below
            
        })
    }
 
    
});

// this needs to exists otherwise you will get a script error
  function someFunction(e){
    //and we should get the wheel event re-broadcast here, thank you mister iFrame :0)

      console.log(e) // on my comp don't need to do anything with this, the gallery keeps scrolling on its own or you may need to just pass this event to your main scrolling code

  }

现在,如果您在 iFrame 中加载的页面实现了严格的 CORS,这很可能不起作用(您的视频示例并不挑剔,似乎工作正常)

此外,如果 iFrame 背景暴露,它可能需要一些调整,它可能不会对滚轮做出反应,但这可以通过将另一个滚轮事件附加到 iframe 本身来解决。

希望这会有所帮助,如果没有,那就有点 iFrame 琐事了。