index.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { isHidden } from '../utils/dom/style';
  2. import { unitToPx } from '../utils/format/unit';
  3. import { createNamespace, isDef, isServer } from '../utils';
  4. import { getScrollTop, getElementTop, getScroller } from '../utils/dom/scroll';
  5. import { BindEventMixin } from '../mixins/bind-event';
  6. var _createNamespace = createNamespace('sticky'),
  7. createComponent = _createNamespace[0],
  8. bem = _createNamespace[1];
  9. export default createComponent({
  10. mixins: [BindEventMixin(function (bind, isBind) {
  11. if (!this.scroller) {
  12. this.scroller = getScroller(this.$el);
  13. }
  14. if (this.observer) {
  15. var method = isBind ? 'observe' : 'unobserve';
  16. this.observer[method](this.$el);
  17. }
  18. bind(this.scroller, 'scroll', this.onScroll, true);
  19. this.onScroll();
  20. })],
  21. props: {
  22. zIndex: [Number, String],
  23. container: null,
  24. offsetTop: {
  25. type: [Number, String],
  26. default: 0
  27. }
  28. },
  29. data: function data() {
  30. return {
  31. fixed: false,
  32. height: 0,
  33. transform: 0
  34. };
  35. },
  36. computed: {
  37. offsetTopPx: function offsetTopPx() {
  38. return unitToPx(this.offsetTop);
  39. },
  40. style: function style() {
  41. if (!this.fixed) {
  42. return;
  43. }
  44. var style = {};
  45. if (isDef(this.zIndex)) {
  46. style.zIndex = this.zIndex;
  47. }
  48. if (this.offsetTopPx && this.fixed) {
  49. style.top = this.offsetTopPx + "px";
  50. }
  51. if (this.transform) {
  52. style.transform = "translate3d(0, " + this.transform + "px, 0)";
  53. }
  54. return style;
  55. }
  56. },
  57. watch: {
  58. fixed: function fixed(isFixed) {
  59. this.$emit('change', isFixed);
  60. }
  61. },
  62. created: function created() {
  63. var _this = this;
  64. // compatibility: https://caniuse.com/#feat=intersectionobserver
  65. if (!isServer && window.IntersectionObserver) {
  66. this.observer = new IntersectionObserver(function (entries) {
  67. // trigger scroll when visibility changed
  68. if (entries[0].intersectionRatio > 0) {
  69. _this.onScroll();
  70. }
  71. }, {
  72. root: document.body
  73. });
  74. }
  75. },
  76. methods: {
  77. onScroll: function onScroll() {
  78. var _this2 = this;
  79. if (isHidden(this.$el)) {
  80. return;
  81. }
  82. this.height = this.$el.offsetHeight;
  83. var container = this.container,
  84. offsetTopPx = this.offsetTopPx;
  85. var scrollTop = getScrollTop(window);
  86. var topToPageTop = getElementTop(this.$el);
  87. var emitScrollEvent = function emitScrollEvent() {
  88. _this2.$emit('scroll', {
  89. scrollTop: scrollTop,
  90. isFixed: _this2.fixed
  91. });
  92. }; // The sticky component should be kept inside the container element
  93. if (container) {
  94. var bottomToPageTop = topToPageTop + container.offsetHeight;
  95. if (scrollTop + offsetTopPx + this.height > bottomToPageTop) {
  96. var distanceToBottom = this.height + scrollTop - bottomToPageTop;
  97. if (distanceToBottom < this.height) {
  98. this.fixed = true;
  99. this.transform = -(distanceToBottom + offsetTopPx);
  100. } else {
  101. this.fixed = false;
  102. }
  103. emitScrollEvent();
  104. return;
  105. }
  106. }
  107. if (scrollTop + offsetTopPx > topToPageTop) {
  108. this.fixed = true;
  109. this.transform = 0;
  110. } else {
  111. this.fixed = false;
  112. }
  113. emitScrollEvent();
  114. }
  115. },
  116. render: function render() {
  117. var h = arguments[0];
  118. var fixed = this.fixed;
  119. var style = {
  120. height: fixed ? this.height + "px" : null
  121. };
  122. return h("div", {
  123. "style": style
  124. }, [h("div", {
  125. "class": bem({
  126. fixed: fixed
  127. }),
  128. "style": this.style
  129. }, [this.slots()])]);
  130. }
  131. });