Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUGFIX: fix the bug that 'Element's will not scroll , while the second time u enter the component and click the 'Scroll' #114

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
87 changes: 76 additions & 11 deletions build/npm/lib/mixins/Helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@ var protoTypes = {
delay: React.PropTypes.number,
isDynamic: React.PropTypes.bool,
onClick: React.PropTypes.func,
duration: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.func])
duration: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.func]),
defaultActive: React.PropTypes.bool
};

// 保存最近一次点击的 link
var clickLink = null;

// 保存 scrollContainer
var scrollSpyContainer = null;

var Helpers = {

Scroll: function (Component, customScroller) {
Expand All @@ -41,7 +48,6 @@ var Helpers = {
},

handleClick: function(event) {

/*
* give the posibility to override onClick
*/
Expand All @@ -57,6 +63,13 @@ var Helpers = {
if (event.stopPropagation) event.stopPropagation();
if (event.preventDefault) event.preventDefault();

clickLink = this.props.to;

// TODO 取消之前的事件并停止滚动!

// 在底部时, 触发 scroll 事件(否则不会切换 activeLink)
scrollSpyContainer.scrollTop -= 1;

/*
* do the magic!
*/
Expand All @@ -79,7 +92,6 @@ var Helpers = {
if (isOutside && activeLink === to) {
scroller.setActiveLink(void 0);
this.setState({ active : false });

} else if (isInside && activeLink != to) {
scroller.setActiveLink(to);
this.setState({ active : true });
Expand All @@ -92,15 +104,33 @@ var Helpers = {
}
},

// 判断是否当前滚动条已到底部
isBottom: function (){
var contentElements = scrollSpyContainer.children;
return (scrollSpyContainer.clientHeight + 1) >= contentElements[contentElements.length - 1].getBoundingClientRect().bottom;
},


// 判断 link 对应的 element 是否全部可视
isExposed: function (link){
if (!link) return false;

var activeElement = scroller.get(link);
var cords = activeElement.getBoundingClientRect();
var topHeight = cords.top;// element 顶部距离 container 的距离
var bottomHeight = cords.bottom;// element 底部距离 container 的距离

// 假如顶部低于 container 顶部,而且底部高于 container 底部
return (Math.floor(topHeight) + 1 >= 0 && Math.floor(bottomHeight) <= scrollSpyContainer.clientHeight);
},

componentDidMount: function() {

var containerId = this.props.containerId;

var scrollSpyContainer = containerId ? document.getElementById(containerId) : document;
scrollSpyContainer = scrollSpyContainer || ( this.props.containerId ? document.getElementById(this.props.containerId) : document );

scrollSpy.mount(scrollSpyContainer);


if(this.props.spy) {
var to = this.props.to;
var element = null;
Expand All @@ -114,6 +144,7 @@ var Helpers = {
}).bind(this));

var spyHandler = function(y) {

if(!element || this.props.isDynamic) {
element = scroller.get(to);
if(!element){ return;}
Expand All @@ -124,14 +155,29 @@ var Helpers = {
}

var offsetY = y - this.props.offset;
var isInside = (offsetY >= elemTopBound && offsetY <= elemBottomBound);
var isOutside = (offsetY < elemTopBound || offsetY > elemBottomBound);
var isInside = (offsetY >= Math.floor(elemTopBound) && offsetY <= Math.floor(elemBottomBound));// 判断是否当前元素已接触到上边界(逻辑有问题: 假如是底部的,没法接触上边界,怎么办?)
var isOutside = (offsetY < Math.floor(elemTopBound) || offsetY > Math.floor(elemBottomBound));
var activeLink = scroller.getActiveLink();

// 判断当前 activeLink 是否全部可视
var isActiveLinkExposed = this.isExposed(clickLink);

// 假如此时已处于底部, 而且 clickLink 的全部内容都可视, 那么设置 clickLink
// to 为当前 link 对应的 element
if (this.isBottom() && isActiveLinkExposed) {
// 假如命中, 那么设为 active 状态
if (to === clickLink) {
scroller.setActiveLink(clickLink);
this.setState({ active : true });
scrollSpy.updateStates();

return true;
}
};

if (isOutside && activeLink === to) {
scroller.setActiveLink(void 0);
this.setState({ active : false });

} else if (isInside && activeLink != to) {
scroller.setActiveLink(to);
this.setState({ active : true });
Expand All @@ -141,23 +187,41 @@ var Helpers = {
}

scrollSpy.updateStates();

}
}.bind(this);

scrollSpy.addSpyHandler(spyHandler);
}

// initial active
if (this.props.defaultActive) {
scroller.setActiveLink(this.props.to);
this.setState({
active : true
}, function (){
scrollSpy.updateStates();
});

if(this.props.onSetActive) {
this.props.onSetActive(this.props.to);
}
}
},
componentWillUnmount: function() {
scrollSpy.unmount();
animateScroll.resetContainer();

// 清除闭包缓存
clickLink = null;
scrollSpyContainer = null;
},
render: function() {

var className = "";
if(this.state && this.state.active) {
className = ((this.props.className || "") + " " + (this.props.activeClass || "active")).trim();
} else {
className = this.props.className
className = this.props.className;
}

var props = assign({}, this.props);
Expand Down Expand Up @@ -188,6 +252,7 @@ var Helpers = {
},
componentWillUnmount: function() {
defaultScroller.unregister(this.props.name);
animateScroll.resetContainer();
},
render: function() {
return React.createElement(Component, this.props);
Expand Down
5 changes: 5 additions & 0 deletions build/npm/lib/mixins/animate-scroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,15 @@ var scrollMore = function(toY, options) {
startAnimateTopScroll(currentPositionY() + toY, assign(options || {}, { absolute : true }));
};

var resetContainer = function() {
__containerElement = undefined;
};

module.exports = {
animateTopScroll: startAnimateTopScroll,
scrollToTop: scrollToTop,
scrollToBottom: scrollToBottom,
scrollTo: scrollTo,
scrollMore: scrollMore,
resetContainer: resetContainer
};
85 changes: 75 additions & 10 deletions modules/mixins/Helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@ var protoTypes = {
delay: React.PropTypes.number,
isDynamic: React.PropTypes.bool,
onClick: React.PropTypes.func,
duration: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.func])
duration: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.func]),
defaultActive: React.PropTypes.bool
};

// 保存最近一次点击的 link
var clickLink = null;

// 保存 scrollContainer
var scrollSpyContainer = null;

var Helpers = {

Scroll: function (Component, customScroller) {
Expand All @@ -41,7 +48,6 @@ var Helpers = {
},

handleClick: function(event) {

/*
* give the posibility to override onClick
*/
Expand All @@ -57,6 +63,13 @@ var Helpers = {
if (event.stopPropagation) event.stopPropagation();
if (event.preventDefault) event.preventDefault();

clickLink = this.props.to;

// TODO 取消之前的事件并停止滚动!

// 在底部时, 触发 scroll 事件(否则不会切换 activeLink)
scrollSpyContainer.scrollTop -= 1;

/*
* do the magic!
*/
Expand All @@ -79,7 +92,6 @@ var Helpers = {
if (isOutside && activeLink === to) {
scroller.setActiveLink(void 0);
this.setState({ active : false });

} else if (isInside && activeLink != to) {
scroller.setActiveLink(to);
this.setState({ active : true });
Expand All @@ -92,15 +104,33 @@ var Helpers = {
}
},

// 判断是否当前滚动条已到底部
isBottom: function (){
var contentElements = scrollSpyContainer.children;
return (scrollSpyContainer.clientHeight + 1) >= contentElements[contentElements.length - 1].getBoundingClientRect().bottom;
},


// 判断 link 对应的 element 是否全部可视
isExposed: function (link){
if (!link) return false;

var activeElement = scroller.get(link);
var cords = activeElement.getBoundingClientRect();
var topHeight = cords.top;// element 顶部距离 container 的距离
var bottomHeight = cords.bottom;// element 底部距离 container 的距离

// 假如顶部低于 container 顶部,而且底部高于 container 底部
return (Math.floor(topHeight) + 1 >= 0 && Math.floor(bottomHeight) <= scrollSpyContainer.clientHeight);
},

componentDidMount: function() {

var containerId = this.props.containerId;

var scrollSpyContainer = containerId ? document.getElementById(containerId) : document;
scrollSpyContainer = scrollSpyContainer || ( this.props.containerId ? document.getElementById(this.props.containerId) : document );

scrollSpy.mount(scrollSpyContainer);


if(this.props.spy) {
var to = this.props.to;
var element = null;
Expand All @@ -114,6 +144,7 @@ var Helpers = {
}).bind(this));

var spyHandler = function(y) {

if(!element || this.props.isDynamic) {
element = scroller.get(to);
if(!element){ return;}
Expand All @@ -124,14 +155,29 @@ var Helpers = {
}

var offsetY = y - this.props.offset;
var isInside = (offsetY >= elemTopBound && offsetY <= elemBottomBound);
var isOutside = (offsetY < elemTopBound || offsetY > elemBottomBound);
var isInside = (offsetY >= Math.floor(elemTopBound) && offsetY <= Math.floor(elemBottomBound));// 判断是否当前元素已接触到上边界(逻辑有问题: 假如是底部的,没法接触上边界,怎么办?)
var isOutside = (offsetY < Math.floor(elemTopBound) || offsetY > Math.floor(elemBottomBound));
var activeLink = scroller.getActiveLink();

// 判断当前 activeLink 是否全部可视
var isActiveLinkExposed = this.isExposed(clickLink);

// 假如此时已处于底部, 而且 clickLink 的全部内容都可视, 那么设置 clickLink
// to 为当前 link 对应的 element
if (this.isBottom() && isActiveLinkExposed) {
// 假如命中, 那么设为 active 状态
if (to === clickLink) {
scroller.setActiveLink(clickLink);
this.setState({ active : true });
scrollSpy.updateStates();

return true;
}
};

if (isOutside && activeLink === to) {
scroller.setActiveLink(void 0);
this.setState({ active : false });

} else if (isInside && activeLink != to) {
scroller.setActiveLink(to);
this.setState({ active : true });
Expand All @@ -141,15 +187,33 @@ var Helpers = {
}

scrollSpy.updateStates();

}
}.bind(this);

scrollSpy.addSpyHandler(spyHandler);
}

// initial active
if (this.props.defaultActive) {
scroller.setActiveLink(this.props.to);
this.setState({
active : true
}, function (){
scrollSpy.updateStates();
});

if(this.props.onSetActive) {
this.props.onSetActive(this.props.to);
}
}
},
componentWillUnmount: function() {
scrollSpy.unmount();
animateScroll.resetContainer();

// 清除闭包缓存
clickLink = null;
scrollSpyContainer = null;
},
render: function() {

Expand Down Expand Up @@ -188,6 +252,7 @@ var Helpers = {
},
componentWillUnmount: function() {
defaultScroller.unregister(this.props.name);
animateScroll.resetContainer();
},
render: function() {
return React.createElement(Component, this.props);
Expand Down
5 changes: 5 additions & 0 deletions modules/mixins/animate-scroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,15 @@ var scrollMore = function(toY, options) {
startAnimateTopScroll(currentPositionY() + toY, assign(options || {}, { absolute : true }));
};

var resetContainer = function() {
__containerElement = undefined;
};

module.exports = {
animateTopScroll: startAnimateTopScroll,
scrollToTop: scrollToTop,
scrollToBottom: scrollToBottom,
scrollTo: scrollTo,
scrollMore: scrollMore,
resetContainer: resetContainer
};