蒙层禁止下方页面滚动防抖动完美方案

news2024/10/6 0:36:39

学习链接

js如何禁止滚动条滚动,但不消失! - 这个是完美解决方案(在线demo示例)

解决窗口滚动条消失而导致的页面内容抖动的问题

完美解决js 禁止滚动条滚动,并且滚动条不消失,页面大小不闪动

蒙层禁止页面滚动的方案

蒙层禁止页面滚动-自己的上篇

最终的效果

在这里插入图片描述

前言

在页面中,经常会需要打开一些弹窗,弹窗下面会有一个蒙层,蒙层下面是页面。蒙层需要覆盖整个视口。但是一般会遇到3个问题不那么好解决

  1. 打开弹窗后,当鼠标滚动时,底下的页面也会跟随滚动。
  2. 浏览器右侧的滚动条在一些方案中会隐藏,而导致打开弹窗和关闭弹窗时,由于滚动条的显示和隐藏而导致页面抖动闪烁。
  3. 浏览器右侧的滚动条在一些方案中,打开弹窗时候,可能会丢失当前的滚动位置,而直接回到了顶部。

如果有上面这3个问题,直接找上面提到的完美方案即可解决

蒙层禁止页面滚动的方案

直接拷贝的蒙层禁止页面滚动的方案 - 来自WindrunnerMax,以作记录。

弹窗是一种常见的交互方式,而蒙层是弹窗必不可少的元素,用于隔断页面与弹窗区块,暂时阻断页面的交互。但是在蒙层出现的时候滚动页面,如果不加处理,蒙层底部的页面会开始滚动,实际上我们是不希望他进行滚动的,因此需要阻止这种行为。当弹出蒙层时禁止蒙层下的页面滚动,也可以称为滚动穿透的问题,文中介绍了一些常用的解决方案。

实现

首先需要实现一个蒙层下滚动的效果示例,当我们点击弹窗按钮显示蒙层之后,再滚动鼠标的话能够看到蒙层下的页面依旧是能够滚动的。如果在蒙层的内部进行滚动,当蒙层内滚动条滚动到底部的时候再继续滚动的话,蒙层下的页面也是能够滚动的,这样的交互就比较混乱,文中内容的测试环境是Chrome 96.0.4664.110。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>蒙层禁止页面滚动的方案</title>
    <style type="text/css">
        #mask{
            position: fixed;
            height: 100vh;
            width: 100vw;
            background: rgba(0, 0, 0, 0.6);
            top: 0;
            left: 0;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .hide{
            display: none !important;
        }
        .long-content > div{
            height: 300px;
        }
        .mask-content{
            width: 300px;
            height: 100px;
            overflow-x: auto;
            background: #fff;
        }
        .mask-content > div{
            height: 300px;
        }
    </style>
</head>
<body>
    <button id="btn">弹窗</button>
    <div class="long-content">
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
    </div>
    <div id="mask" class="hide">
        <div class="mask-content">
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
        </div>
    </div>
</body>
    <script type="text/javascript">
        (() => {
            const btn = document.getElementById("btn");
            const mask = document.getElementById("mask");
            btn.addEventListener("click", e => {
                mask.classList.remove("hide");
            })
            mask.addEventListener("click", e => {
                mask.classList.add("hide");
            })
        })();
    </script>
</html>

body hidden

此方案是一种比较常用的方案,即打开蒙层时给body添加overflow: hidden;,在关闭蒙层时就移除这个样式,例如思否的登录弹窗、antd的Modal对话框就是这样的方式。 这种方案的优点是简单方便,只需添加css样式,没有复杂的逻辑。缺点是在移动端的适配性差一些,部分安卓机型以及safari中,无法阻止底部页面滚动,另外有些机型可能需要给根节点添加overflow: hidden;样式才有效果,此外由于实际上是将页面的内容给裁剪了,所以在设置这个样式的时候滚动条会消失,而移除样式的时候滚动条又会出现,所以在视觉上是会有一定的闪烁现象的,当然也可以定制滚动条的样式,但滚动条样式就是另一个兼容性的问题了,还有同样是因为裁剪。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>蒙层禁止页面滚动的方案</title>
    <style type="text/css">
        #mask{
            position: fixed;
            height: 100vh;
            width: 100vw;
            background: rgba(0, 0, 0, 0.6);
            top: 0;
            left: 0;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .hide{
            display: none !important;
        }
        .long-content > div{
            height: 300px;
        }
        .body-overflow-hidden{
            overflow: hidden;
        }
        .mask-content{
            width: 300px;
            height: 100px;
            overflow-x: auto;
            background: #fff;
        }
        .mask-content > div{
            height: 300px;
        }
    </style>
</head>
<body>
    <button id="btn">弹窗</button>
    <div class="long-content">
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
    </div>
    <div id="mask" class="hide">
        <div class="mask-content">
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
        </div>
    </div>
</body>
    <script type="text/javascript">
        (() => {
            const btn = document.getElementById("btn");
            const mask = document.getElementById("mask");
            const body = document.body;
            btn.addEventListener("click", e => {
                mask.classList.remove("hide");
                body.classList.add("body-overflow-hidden");
            })
            mask.addEventListener("click", e => {
                mask.classList.add("hide");
                body.classList.remove("body-overflow-hidden");
            })
        })();
    </script>
</html>

touch preventDefault

上边的方案对于移动端的效果不是很理想,如果需要在移动端进行处理的话,可以利用移动端的touch事件,来阻止默认行为,当然这是适用于移动端的方式,另外要是把手机通过蓝牙也好转接线也好接上鼠标的话,那就是另一回事了。假如蒙层内容不会有滚动条,那么上述方法是没有问题的,但是假如蒙层内容有滚动条的话,那么它再也无法动弹了。所以如果在蒙层内部有元素需要滚动的话,需要用Js控制其逻辑,但是逻辑控制起来又是比较复杂的,我们可以判断事件的event.target元素,如果touch的目标是弹窗不可滚动区域即背景蒙层就禁掉默认事件,反之就不做控制,之后又出现了问题,需要判断滚动到顶部和滚动到底部的时候禁止滚动,否则触碰到上下两端,弹窗可滚动区域的滚动条到了顶部或者底部,依旧穿透到body,使得body跟随弹窗滚动,这样的话逻辑的复杂程度就比较高了。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>蒙层禁止页面滚动的方案</title>
    <style type="text/css">
        #mask{
            position: fixed;
            height: 100vh;
            width: 100vw;
            background: rgba(0, 0, 0, 0.6);
            top: 0;
            left: 0;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .hide{
            display: none !important;
        }
        .long-content > div{
            height: 300px;
        }
        .mask-content{
            width: 300px;
            height: 100px;
            overflow-x: auto;
            background: #fff;
        }
        .mask-content > div{
            height: 300px;
        }
    </style>
</head>
<body>
    <button id="btn">弹窗</button>
    <div class="long-content">
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
    </div>
    <div id="mask" class="hide">
        <div class="mask-content">
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
        </div>
    </div>
</body>
    <script type="text/javascript">
        (() => {
            const btn = document.getElementById("btn");
            const mask = document.getElementById("mask");
            const body = document.body;
            const scrollerContainer = document.querySelector(".mask-content");

            let targetY = 0; // 记录下第一次按下时的`clientY`
            scrollerContainer.addEventListener("touchstart", e => {
                targetY = Math.floor(e.targetTouches[0].clientY);
            });
            const touchMoveEventHandler = e => {
                if(!scrollerContainer.contains(e.target)) {
                    e.preventDefault();
                }
                let newTargetY = Math.floor(e.targetTouches[0].clientY); //本次移动时鼠标的位置,用于计算
                let scrollTop = scrollerContainer.scrollTop; // 当前滚动的距离
                let scrollHeight = scrollerContainer.scrollHeight; // 可滚动区域的高度
                let containerHeight = scrollerContainer.clientHeight; //可视区域的高度
                if (scrollTop <= 0 && newTargetY - targetY > 0) { // 到顶
                    console.log("到顶");
                    if(e.cancelable)  e.preventDefault(); // 必须判断`cancelable` 否则容易出现滚动正在进行无法取消的`error`
                } else if (scrollTop >= scrollHeight - containerHeight && newTargetY - targetY < 0 ) { // 到底
                    console.log("到底");
                    if(e.cancelable) e.preventDefault(); // 必须判断`cancelable` 否则容易出现滚动正在进行无法取消的`error`
                }
            }
            btn.addEventListener("click", e => {
                mask.classList.remove("hide");
                body.addEventListener("touchmove", touchMoveEventHandler, { passive: false });
            })
            mask.addEventListener("click", e => {
                mask.classList.add("hide");
                body.removeEventListener("touchmove", touchMoveEventHandler);
            })
        })();
    </script>
</html>

body fixed

目前常用的方案就是该方案了,要阻止页面滚动,可以将其固定在视图中即position: fixed,这样它就无法滚动了,当蒙层关闭时再释放,当然还有一些细节要考虑,将页面固定视图后内容会回头最顶端,这里我们需要记录一下用来同步top值,这样就可以得到一个兼容移动端与PC端的较为完善的方案了,当然对于浏览器的api兼容性是使用document.documentElement.scrollTop控制还是window.pageYOffset + window.scrollTo控制就需要另行适配了。在示例中为了演示弹窗时不会导致视图重置到最顶端,将弹窗按钮移动到了下方。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>蒙层禁止页面滚动的方案</title>
    <style type="text/css">
        #mask{
            position: fixed;
            height: 100vh;
            width: 100vw;
            background: rgba(0, 0, 0, 0.6);
            top: 0;
            left: 0;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .hide{
            display: none !important;
        }
        .long-content > div{
            height: 300px;
        }
        .mask-content{
            width: 300px;
            height: 100px;
            overflow-x: auto;
            background: #fff;
        }
        .mask-content > div{
            height: 300px;
        }
    </style>
</head>
<body>
    <div class="long-content">
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <button id="btn">弹窗</button>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
        <div>long content</div>
    </div>
    <div id="mask" class="hide">
        <div class="mask-content">
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
            <div>mask-content</div>
        </div>
    </div>
</body>
    <script type="text/javascript">
        (() => {
            const btn = document.getElementById("btn");
            const mask = document.getElementById("mask");
            const body = document.body;

            let documentTop = 0; // 记录按下按钮时的 `top`

            btn.addEventListener("click", e => {
                mask.classList.remove("hide");
                documentTop = document.scrollingElement.scrollTop;
                body.style.position = "fixed"
                body.style.top = -documentTop + "px";
            })
            mask.addEventListener("click", e => {
                mask.classList.add("hide");
                body.style.position = "static";
                body.style.top = "auto";
                document.scrollingElement.scrollTop = documentTop;
            })
        })();
    </script>
</html>

解决js 禁止滚动条滚动,并且滚动条不消失,页面大小不闪动

下面就是:

  • 关闭页面滚动的时候,记录页面的滚动位置。然后监听滚轮滚动事件,它一滚动就恢复原来的位置
  • 恢复页面滚动的时候,把记录的位置都清空,然后该怎么滚就怎么滚
  • 在scrollTo方法中,最好加上behavior:'instant’属性。
    • 例:window.scrollTo({top:winY,behavior:‘instant’});

但是,我自己在测试的时候,还是会有下方页面滚动的现象,但是最终还是回到原来的位置,就想弹簧一样。

var winX = null;
var winY = null;
 
window.addEventListener('scroll', function () {
    if (winX !== null && winY !== null) {
        window.scrollTo(winX, winY);
    }
});
 
function disableWindowScroll() {
    winX = window.scrollX;
    winY = window.scrollY;
}
 
function enableWindowScroll() {
    winX = null;
    winY = null;
}

解决窗口滚动条消失而导致的页面内容抖动的问题

今天在项目中遇到了这样一个问题,点击按钮出现遮罩层和弹窗,不过弹窗出现后还是可以滚动底部的页面,于是写了一段js使弹窗出来后使底部页面增添overflow: hidden的样式解决了这个问题,但是还有一个细节问题就是页面的滚动条也占据宽度,滚动条消失后会有一个抖动的效果,给人体验很不好

尝试了几个办法,以下是个人认为比较好的解决办法:

首先获取窗口文档显示区的宽度和DOM文档的根节点html元素对象的宽度,前者减去后者计算出滚动条的宽度

// 滚动条宽度
let scrollWidth = window.innerWidth - document.documentElement.offsetWidth;

弹窗打开时,滚动条消失先给body元素设置overflow:hidden防止底部页面滚动,然后再给底部页面右侧的padding 设置scrollWidth的值来模拟滚动条存在时底部页面内容的位置

document.body.style.overflow = 'hidden';
this.$refs.index.style.paddingRight = scrollWidth + 'px';

弹窗关闭时,body元素设置overflow:auto,因为弹窗关闭时弹窗又恢复了,重新获取 scrollWidth 的值设置padding为0,这样底部页面的内容就不会在抖动啦,以下为完整代码(不是一个组件的方法,主要逻辑就是上面的代码)

show() {
      this.isShow = !this.isShow;
      // 滚动条宽度
      let scrollWidth = window.innerWidth - document.documentElement.offsetWidth;
      document.body.style.overflow = 'hidden';
      this.$refs.index.style.paddingRight = scrollWidth + 'px';
}
close() {
      this.isShow = false;
      this.$parent.show();
      document.body.style.overflow = 'auto';
}

完美解决方案

<script>
  document.getElementById("enable").onclick = function () {
    enableScroll();
    document.getElementById("status").innerHTML = "enabled";
    document.getElementById("status").className = "enabled";
  };

  document.getElementById("disable").onclick = function () {
    disableScroll();
    document.getElementById("status").innerHTML = "disabled";
    document.getElementById("status").className = "disabled";
  };

  // left: 37, up: 38, right: 39, down: 40
  const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };

  function preventDefault(e) {
    e.preventDefault();
  }

  function preventDefaultForScrollKeys(e) {
    if (keys[e.keyCode]) {
      preventDefault(e);
      return false;
    }
  }

  let supportsPassive = false;
  try {
    window.addEventListener("test", null, Object.defineProperty({}, "passive", {
        get: function () {
          supportsPassive = true;
        },
      })
    );
  } catch (e) {}

  const wheelOpt = supportsPassive ? { passive: false } : false;
  const wheelEvent = "onwheel" in document.createElement("div") ? "wheel" : "mousewheel";

  // call this to Disable
  function disableScroll() {
    window.addEventListener("DOMMouseScroll", preventDefault, false); // older FF
    window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
    window.addEventListener("touchmove", preventDefault, wheelOpt); // mobile
    window.addEventListener("keydown", preventDefaultForScrollKeys, false);
  }

  // call this to Enable
  function enableScroll() {
    window.removeEventListener("DOMMouseScroll", preventDefault, false);
    window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
    window.removeEventListener("touchmove", preventDefault, wheelOpt);
    window.removeEventListener("keydown", preventDefaultForScrollKeys, false);
  }
</script>

  • preventDefaultForScrollKeys事件主要阻止键盘↑↓←→键的默认行为;
  • e.preventDefault()该方法将通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)。相关文档
  • { passive: false }的作用:passive的作用
  • window.addEventListener(“test”, null…这段代码来源于:addEventListener
  • …“onwheel” in document.createElement(“div”)…滚轮事件的不同浏览器兼容

示例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
        }

        body {
            width: 100%;
        }

        .box1 {
            height: 1000px;
            width: 100%;
            background-color: #bfa;
        }
    </style>
    <script>
        

        // left: 37, up: 38, right: 39, down: 40
        const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };

        function preventDefault(e) {
            e.preventDefault();
        }

        function preventDefaultForScrollKeys(e) {
            if (keys[e.keyCode]) {
                preventDefault(e);
                return false;
            }
        }

        let supportsPassive = false;
        try {
            window.addEventListener("test", null, Object.defineProperty({}, "passive", {
                get: function () {
                    supportsPassive = true;
                },
            })
            );
        } catch (e) { }

        const wheelOpt = supportsPassive ? { passive: false } : false;
        const wheelEvent = "onwheel" in document.createElement("div") ? "wheel" : "mousewheel";

        // call this to Disable
        function disableScroll() {
            window.addEventListener("DOMMouseScroll", preventDefault, false); // older FF
            window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
            window.addEventListener("touchmove", preventDefault, wheelOpt); // mobile
            window.addEventListener("keydown", preventDefaultForScrollKeys, false);
        }

        // call this to Enable
        function enableScroll() {
            window.removeEventListener("DOMMouseScroll", preventDefault, false);
            window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
            window.removeEventListener("touchmove", preventDefault, wheelOpt);
            window.removeEventListener("keydown", preventDefaultForScrollKeys, false);
        }

        window.onload = () => {
            let btn1 = document.querySelector('#btn1')
            let btn2 = document.querySelector('#btn2')

            btn1.onclick = () => {
                disableScroll()
            }

            btn2.onclick = () => {
                enableScroll()
            }


        }
    </script>
</head>

<body>
    <div class="box1">
       一段很长的文字...[使用lorem1000]
    </div>
    <button id="btn1">disableWindowScroll</button>
    <button id="btn2">enableWindowScroll</button>
</body>

</html>

vue中使用

把这两个函数暴露出去就行了,然后打开弹窗的时候,调用disableScroll方法,在关闭弹窗的时候,调用enableScroll的方法

scrollCtrl.js

// 控制页面的滚动条是否可以跟随鼠标滚动而滚动

// left: 37, up: 38, right: 39, down: 40
const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };

function preventDefault(e) {
    e.preventDefault();
}

function preventDefaultForScrollKeys(e) {
    if (keys[e.keyCode]) {
        preventDefault(e);
        return false;
    }
}

let supportsPassive = false;
try {
    window.addEventListener("test", null, Object.defineProperty({}, "passive", {
        get: function () {
            supportsPassive = true;
        },
    })
    );
} catch (e) { }

const wheelOpt = supportsPassive ? { passive: false } : false;
const wheelEvent = "onwheel" in document.createElement("div") ? "wheel" : "mousewheel";

// call this to Disable
function disableScroll() {
    window.addEventListener("DOMMouseScroll", preventDefault, false); // older FF
    window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
    window.addEventListener("touchmove", preventDefault, wheelOpt); // mobile
    window.addEventListener("keydown", preventDefaultForScrollKeys, false);
}

// call this to Enable
function enableScroll() {
    window.removeEventListener("DOMMouseScroll", preventDefault, false);
    window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
    window.removeEventListener("touchmove", preventDefault, wheelOpt);
    window.removeEventListener("keydown", preventDefaultForScrollKeys, false);
}
export {
    enableScroll,
    disableScroll
}

PrevImg.vue

组件中使用scrollCtrl.js

<template>
    <transition name="slidedown">

        <div class="prev-container" v-show="isShow">
            <div class="mask"></div>
            <div class="indicator">
                <i @click="operate('zoomIn')" class="iconfont icon-jiahao1"></i>
                <i @click="operate('zoomOut')" class="iconfont icon-jian"></i>
                <i @click="operate('rotate')" class="iconfont icon-reset"></i>
                <i @click="close" class="iconfont icon-close"></i>
            </div>
            <div :class="['prev-next', 'left', { 'pointer-allowd': isPrev }]" @click="operate('left')">
                <i class="iconfont icon-houtuishangyige"></i>
            </div>
            <div :class="['prev-next', 'right', { 'pointer-allowd': isNext }]" @click="operate('right')">
                <i class="iconfont icon-qianjinxiayige"></i>
            </div>
            <div ref="imgWrapperRef" class="img-wrapper">
                <img :src="imgPaths[imgIdx]" :style="imgStyle" alt="">
            </div>
        </div>
    </transition>
</template>

<script>

import {enableScroll,disableScroll} from '@/utils/scrollCtrl.js'

export default {
    name: 'PrevImg',
    components: {
    },
    data() {
        return {
            isShow: false,
            imgIdx: 0,
            imgPaths: [],
            transform: {
                rotate: 0,
                scale: 1
            },
            enterAniClass: '',
            winY: null,
        }
    },
    computed: {
        imgStyle() {
            let { rotate, scale } = this.transform
            return {
                transform: `scale(${scale}) rotate(${rotate}deg)`
            }
        },
        isNext() {
            return this.imgIdx != this.imgPaths.length - 1
        },
        isPrev() {
            return this.imgIdx != 0
        },
    },
    mounted() {
        this.$refs['imgWrapperRef'].onclick = e => {
            console.log(e.target, 2323);
            if (e.target == this.$refs['imgWrapperRef']) {
                this.close()
            }
        }

        console.log(enableScroll,disableScroll,'11');
        
    },
    methods: {
        open({ imgIdx, imgPaths }) {
            this.isShow = true
            this.imgIdx = imgIdx
            this.imgPaths = imgPaths

            disableScroll() // 关闭滚动
        },
        
        close() {
            console.log('object');
            this.isShow = false
            this.transform = {
                rotate: 0,
                scale: 1
            }

            enableScroll() // 打开滚动
        },
        operate(command) {
            if (command == 'zoomIn') {
                this.transform.scale += 0.04
            } else if (command == 'zoomOut') {
                this.transform.scale -= 0.04
            } else if (command == 'rotate') {
                this.transform.rotate += 90
            } else {
                this.transform = {
                    rotate: 0,
                    scale: 1
                }
                if (command == 'left') {
                    if (this.imgIdx == 0) {
                        return
                    }
                    this.imgIdx = this.imgIdx - 1
                } else if (command == 'right') {
                    if (this.imgIdx == this.imgPaths.length - 1) {
                        return
                    }
                    this.imgIdx = this.imgIdx + 1
                }

            }
        }
    }
}
</script>

<style lang="scss">
.slidedown-enter-active,
.slidedown-leave-active {
    transition: all 0.5s;
}

.slidedown-enter,
.slidedown-leave-to {
    opacity: 0;
    transform: translateY(-20px);
}

.slidedown-enter-to,
.slidedown-leave {
    opacity: 1;
    transform: translateY(0)
}

.prev-container {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 99999999999;

    .mask {
        position: absolute;
        background-color: rgba(0, 0, 0, .3);
        width: 100%;
        height: 100%;
        z-index: -1;
    }

    .pointer-allowd {
        cursor: pointer !important;
    }

    .prev-next {
        width: 36px;
        height: 36px;
        border-radius: 50%;
        background-color: rgba(255, 255, 255, .3);
        text-align: center;
        line-height: 36px;
        cursor: not-allowed;

        position: absolute;
        top: 0;
        bottom: 0;
        margin: auto;

        z-index: 2000;
    }

    .left {
        left: 30px;
    }

    .right {
        right: 30px;
    }

    .indicator {
        display: flex;
        justify-content: flex-end;
        padding: 20px;
        z-index: 100;

        i {
            padding: 10px;
            background-color: rgba(255, 255, 255, .3);
            border-radius: 50%;
            margin: 0 5px;
            cursor: pointer;
        }
    }

    .img-wrapper {
        position: absolute;
        top: 0;
        width: 100%;
        height: 100%;
        z-index: -1;

        display: flex;
        align-items: center;
        justify-content: center;

        img {
            max-height: 100%;
            max-width: 80%;
            transition: all 0.5s;
        }
    }
}</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/515991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python数据类型-元组】------- PYTHON基础11

内容目录 一、 元组1. 元组的构建2. 元组的索引3. 元组和列表的区别及相互转换3.1. 列表转为元组&#xff0c;通过内置函数tuple()实现&#xff0c;比如&#xff1a;3.2. 元组转为列表&#xff0c;通过内置函数list()实现 4. 元组的基本操作&#xff1a;更新&#xff0c; 删除&…

用于colmap重建结果、pcd/ply、6D位姿的点云可视化工具

工具介绍&#xff1a;提供一款用于点云可视化windows的工具 可视化的对象包括&#xff1a; 1、colmap重建结果 2、pcd\ply格式的点云 3、位姿R|t可视化 4、在线接收点python发送的坐标 其他功能&#xff1a;点云保存、颜色修改、点云隐藏、点云大小调整 工具地址&#xff1a…

Qt之QGraphicsEffect的简单使用(含源码+注释)

文章目录 一、效果示例图1.效果演示图片3.弹窗演示图片 二.问题描述三、源码CFrame.hCFrame.cppCMainWindow.hCMainWindow.cpp 总结 一、效果示例图 1.效果演示图片 3.弹窗演示图片 二.问题描述 &#xff08;因为全是简单使用&#xff0c;毫无技巧&#xff0c;直接描述问题&a…

计算机视觉的深度学习 Lecture3:Linear Classifiers 笔记 EECS 498.008

注意到每一行完成一类的分类 事先思考一下loss的可能值有助于debug。如果W随机为高斯分布&#xff0c;μ为0.001&#xff0c;那么下面sj-syi就会很小&#xff0c;Li的值接近C-1&#xff0c;C为分类数 正则化表达式&#xff1a; 如果score都是随机很小的数&#xff0c;近似意…

博客管理系统--博客详情页、登录页

登录页实现强制登录 URL解决后&#xff1b;现在到查看全文按钮。我们点击这个查看全文我们就跳转到博客详情页。 我们希望就是在这个页面&#xff1b;把这些写死的数据换成从后端获取的。 1&#xff1a;约定前后端交互接口 请求&#xff1a;GET /blog?blogId1 (这样子写和博…

总结848

学习目标&#xff1a; 月目标&#xff1a;5月&#xff08;张宇强化前10讲&#xff0c;背诵15篇短文&#xff0c;熟词僻义300词基础词&#xff09; 周目标&#xff1a;张宇强化前3讲并完成相应的习题并记录&#xff0c;英语背3篇文章并回诵 每日必复习&#xff08;5分钟&#…

【连续介质力学】向量

向量的代数操作 加法 a ⃗ \vec a a , b ⃗ \vec b b 为任意向量 c ⃗ a ⃗ b ⃗ b ⃗ a ⃗ \vec c \vec a \vec b \vec b \vec a c a b b a 减法 d ⃗ a ⃗ − b ⃗ \vec d \vec a- \vec b d a −b 标量乘法 λ a ⃗ \lambda \vec a λa , 与 a ⃗ \vec a a 相同…

ubuntu18 使用matplotlib画图

一、安装virtualenvwrapper 1.确认virtualenvwrapper是否已安装&#xff1a; 打开Termianl终端&#xff0c;执行指令&#xff1a;which virtualenvwrapper.sh查询virtualenvwrapper.sh的路径&#xff0c;如果没有提示&#xff0c;则表明virtualenvwrapper.sh没有安装。 2.安…

Codeforces Round 871 (Div. 4) G 记忆化搜索+二分 你没见过的解法!

G. Hits Different 记dp数组为答案数组 首先 dp[2] 2 2 2^2 22 1 2 1^2 12 dp[3] 3 2 3^2 32 1 2 1^2 12 dp[5] 2 2 2^2 22 3 2 3^2 32 1 2 1^2 12 不难发现dp[5]dp[2]dp[3]-dp[1] 同理dp[25]dp[18]dp[19]-dp[13] 接下来就是愉快的找公式时间 观察到题目中给的每一层塔的级数 (…

Notion AI 进阶【help me write】

对于Notion Ai来说,尽管一直在不断发展&#xff0c;但似乎人们还沉浸在Chat Gpt带来的狂欢&#xff0c;但如果你是国内用户&#xff0c;Notion AI所能提供的是远远大于限制过多的GPT&#xff0c;本篇讲一讲在Notion AI中help me write的使用 在这周我把Notion ai接入到Discord…

PostgresML - PostgreSQL的生成式AI扩展

PostgresML 是 PostgreSQL 的机器学习扩展&#xff0c;支持生成式AI&#xff0c;使你能够使用 SQL 查询对文本和表格数据执行训练和推理。 借助 PostgresML&#xff0c;你可以将机器学习模型无缝集成到你的 PostgreSQL 数据库中&#xff0c;并利用尖端算法的强大功能来高效地处…

【Python数据类型-集合】------- PYTHON基础14

内容目录 一、 集合1. 集合创建1.1. 创建集合1.2. 创建空集合 2. 集合基本操作2.1. add()添加新的元素 set1.add(element)2.2.remove()删除元素 set1.remove(element)2.3.discard()删除元素 set1.discard(element)2.4.clear()清空集合里的所有元素 set1.clear() 3. 集合与列表、…

介绍10款ChatGPT替代产品

ChatGPT 引领着聊天 AI 的世界&#xff0c;许多人已经开始在日常生活中使用它。OpenAI 的 GPT-3 语言模型是聊天机器人的基础&#xff0c;它使得用户能够通过回答问题与 AI 进行交互。 GPT-4 的引入为机器人提供了更强大的功能。然而&#xff0c;它也有一个明显的缺点&#xff…

09-HTML-表格标签(合并单元格)

标签描述<table>定义表格<caption>定义表格标题。<th>定义表格中的表头单元格。<tr>定义表格中的行。<td>定义表格中的单元。<thead>定义表格中的表头内容。<tbody>定义表格中的主体内容。<tfoot>定义表格中的表注内容&#x…

API数据接口可以从几个不同的角度来看待;

一.API数据接口可以从几个不同的角度来看待&#xff1a; 1. 技术角度&#xff1a;API数据接口是一种技术实现&#xff0c;通常是以HTTP协议或其他网络协议为基础&#xff0c;并采用特定的数据格式&#xff08;如JSON或XML&#xff09;来传递数据。 2. 业务角度&#xff1a;API数…

Ubuntu22.04安装PyTorch1.12.1 GPU版本

目录 一、电脑相关信息 1. 电脑显卡环境&#xff1a; 二、安装Pytorch1.12.1/cu116&#xff08;GPU版本&#xff09; 1. 准备&#xff1a;新建虚拟环境 2. 方式一&#xff1a;用pip在线安装torch1.12.1cu116、torchvision0.13.1cu116&#xff1a;&#xff08;在pytorch官网…

堆的实现,以及画图和代码分析,建堆,堆排序,时间复杂度的分析

堆的实现 堆的概念及结构堆的实现初始化销毁返回堆顶元素判空有效数据个数 堆的插入&#xff08;向上调整算法&#xff09;删除堆顶元素&#xff0c;仍然保持堆的形态&#xff08;向下调整算法&#xff09;堆的创建向上调整法建堆向下调整建堆两种建堆方法时间复杂度向下调整法…

shell脚本之数值,冒泡排序算法

目录 一、数组 1.定义数组 2. 数组中数据类型 2.1数值类型 2.2字符类型 二、数组的用法 1. 输出数组中的值 2. 统计数组参数个数 ​编辑 3.查看数组下标列表 4.分割字符串 5.替换数组中的字符 6.删除数组 三、数组追加元素 1.方法1示例 2.方法2示例 3.方法3示例 …

Notion Ai中文指令使用技巧

Notion AI 是一种智能技术&#xff0c;可以自动处理大量数据&#xff0c;并从中提取有用的信息。它能够 智能搜索&#xff1a;通过搜索文本和查询结果进行快速访问 自动归档&#xff1a;可以根据关键字和日期自动将内容归档 内容分类&#xff1a;可以根据内容的标签和内容的…

【嵌入式Linux】MBR分区表 和 GPT分区表

文章目录 GUID以及分区表MBR分区方案GPT 分区方案GPT分区表结构 GPT分区表LBALBA0LBA1LBA 2-33python生成GPT分区表gpt分区表实例 gpt分区表查看查看百问网T113-s3固件查看友善之臂nanopi-m1-plus官方固件查看荣品RV1126固件查看f1c200s固件查看V3s的SD启动卡 原文&#xff1a;…