ElemeFE / vue-infinite-scroll

An infinite scroll directive for vue.js.
2.86k stars 417 forks source link

支持vue3的办法 How to support vue3 #159

Open dingshaohua-com opened 2 years ago

dingshaohua-com commented 2 years ago

使用我修改后的包(Use my modified package)

适合懒人 For lazy people

主要是生命周期vue2和vue3不通导致不兼容,还有一些其他细微变化 The main reason is that the life cycle vue2 and vue3 are incompatible, and there are some other minor changes.

我把改动后的项目,也上传到了npm上,方便大家直接使用 I also uploaded the changed project to npm, which is convenient for everyone to use directly.

也进行了开源,直接安装vue3-infinite-scroll-good ,用法和vue-infinite-scroll一模一样 As like as two peas, vue3-infinite-scroll-good is installed directly, and the usage is exactly the same as vue-infinite-scroll.

直接修改源码替换(Direct modification of source code replacement)

适合有洁癖的人,不愿使用小众包 It is suitable for people who are obsessed with cleanliness and do not want to use crowdsourcing

directive.js

const ctx = "@@InfiniteScroll";

var throttle = function(fn, delay) {
  var now, lastExec, timer, context, args; //eslint-disable-line

  var execute = function() {
    fn.apply(context, args);
    lastExec = now;
  };

  return function() {
    context = this;
    args = arguments;

    now = Date.now();

    if (timer) {
      clearTimeout(timer);
      timer = null;
    }

    if (lastExec) {
      var diff = delay - (now - lastExec);
      if (diff < 0) {
        execute();
      } else {
        timer = setTimeout(() => {
          execute();
        }, diff);
      }
    } else {
      execute();
    }
  };
};

var getScrollTop = function(element) {
  if (element === window) {
    return Math.max(
      window.pageYOffset || 0,
      document.documentElement.scrollTop
    );
  }

  return element.scrollTop;
};

var getComputedStyle = document.defaultView.getComputedStyle;

var getScrollEventTarget = function(element) {
  var currentNode = element;
  // bugfix, see http://w3help.org/zh-cn/causes/SD9013 and http://stackoverflow.com/questions/17016740/onscroll-function-is-not-working-for-chrome
  while (
    currentNode &&
    currentNode.tagName !== "HTML" &&
    currentNode.tagName !== "BODY" &&
    currentNode.nodeType === 1
  ) {
    var overflowY = getComputedStyle(currentNode).overflowY;
    if (overflowY === "scroll" || overflowY === "auto") {
      return currentNode;
    }
    currentNode = currentNode.parentNode;
  }
  return window;
};

var getVisibleHeight = function(element) {
  if (element === window) {
    return document.documentElement.clientHeight;
  }

  return element.clientHeight;
};

var getElementTop = function(element) {
  if (element === window) {
    return getScrollTop(window);
  }
  return element.getBoundingClientRect().top + getScrollTop(window);
};

var isAttached = function(element) {
  var currentNode = element.parentNode;
  while (currentNode) {
    if (currentNode.tagName === "HTML") {
      return true;
    }
    if (currentNode.nodeType === 11) {
      return false;
    }
    currentNode = currentNode.parentNode;
  }
  return false;
};

var doBind = function() {
  if (this.binded) return; // eslint-disable-line
  this.binded = true;

  var directive = myInstance;
  var element = directive.el;

  var throttleDelayExpr = element.getAttribute(
    "infinite-scroll-throttle-delay"
  );
  var throttleDelay = 200;
  if (throttleDelayExpr) {
    throttleDelay = Number(
      directive.vm[throttleDelayExpr] || throttleDelayExpr
    );
    if (isNaN(throttleDelay) || throttleDelay < 0) {
      throttleDelay = 200;
    }
  }
  directive.throttleDelay = throttleDelay;

  directive.scrollEventTarget = getScrollEventTarget(element);
  directive.scrollListener = throttle(
    doCheck.bind(directive),
    directive.throttleDelay
  );
  directive.scrollEventTarget.addEventListener(
    "scroll",
    directive.scrollListener
  );

  var disabledExpr = element.getAttribute("infinite-scroll-disabled");
  var disabled = false;

  if (disabledExpr) {
    this.vm.$watch(disabledExpr, function(value) {
      directive.disabled = value;
      if (!value && directive.immediateCheck) {
        setTimeout(() => {
          doCheck.call(directive);
        });
      }
    });
    disabled = Boolean(directive.vm[disabledExpr]);
  }
  directive.disabled = disabled;

  var distanceExpr = element.getAttribute("infinite-scroll-distance");
  var distance = 0;
  if (distanceExpr) {
    distance = Number(directive.vm[distanceExpr] || distanceExpr);
    if (isNaN(distance)) {
      distance = 0;
    }
  }
  directive.distance = distance;

  var immediateCheckExpr = element.getAttribute(
    "infinite-scroll-immediate-check"
  );
  var immediateCheck = true;
  if (immediateCheckExpr) {
    immediateCheck = Boolean(directive.vm[immediateCheckExpr]);
  }
  directive.immediateCheck = immediateCheck;

  if (immediateCheck) {
    setTimeout(() => {
      doCheck.call(directive);
    });
  }

  var eventName = element.getAttribute("infinite-scroll-listen-for-event");
  if (eventName) {
    directive.vm.$on(eventName, function() {
      setTimeout(() => {
        doCheck.call(directive);
      });
    });
  }
};

var doCheck = function(force) {
  var scrollEventTarget = myInstance.scrollEventTarget;
  var element = myInstance.el;
  var distance = myInstance.distance;

  if (force !== true && myInstance.disabled) return; //eslint-disable-line
  var viewportScrollTop = getScrollTop(scrollEventTarget);
  var viewportBottom = viewportScrollTop + getVisibleHeight(scrollEventTarget);

  var shouldTrigger = false;

  if (scrollEventTarget === element) {
    shouldTrigger = scrollEventTarget.scrollHeight - viewportBottom <= distance;
  } else {
    var elementBottom =
      getElementTop(element) -
      getElementTop(scrollEventTarget) +
      element.offsetHeight +
      viewportScrollTop;

    shouldTrigger = viewportBottom + distance >= elementBottom;
  }

  if (shouldTrigger && myInstance.expression) {
    this.expression();
  }
};

var myInstance = null;

var myDirective = {
  mounted(el, binding, vnode) {
    el[ctx] = {
      el,
      vm: binding.instance,
      expression: binding.value,
    };
    myInstance = el[ctx];
    var args = arguments;

    el[ctx].vm.$nextTick(function() {
      if (isAttached(el)) {
        doBind.call(el[ctx], args);
      }

      el[ctx].bindTryCount = 0;

      var tryBind = function() {
        if (el[ctx].bindTryCount > 10) return; //eslint-disable-line
        el[ctx].bindTryCount++;
        if (isAttached(el)) {
          doBind.call(el[ctx], args);
        } else {
          setTimeout(tryBind, 50);
        }
      };

      tryBind();
    });
  }
};

export default myDirective;