react 實現手機端首頁無縫輪播
阿新 • • 發佈:2019-01-24
首先要註明一下本人是首次寫js相關部落格,如有什麼錯誤的地方,還請大家多多指正!謝謝!!!
目前掌握的概念:
實現輪播可以採用transform中的translate,左移為transform: translate(-xx px) + transition:0.5s
一共設定三層結構,最外層div為整個螢幕的寬度;ul為所有圖片的寬度,可以設定為圖片張數*100%;而裡面每個li也是當前螢幕的寬度,可以設定為ul寬度/圖片張數,這樣使用translate時就可以實現translate(-螢幕寬度*當前圖片位置)即可顯示下一張圖
```
html:
<div class="slider">
<ul class="lunbo">
<li class="slider-item" style="width: 25%;"><img src="a"></li>
<li class="slider-item" style="width: 25%;"><img src="b"></li>
<li class="slider-item" style="width: 25%;"><img src="a"></li>
<li class="slider-item" style="width: 25%;"><img src="b"></li>
</ul>
//下面部分為dot
<div class="slider-dots-wrap">
<span class="slider-dot"></span>
<span class="slider-dot"></span>
</div>
</div>
但是這樣的話一旦圖片張數輪播結束,後面就是空白,所以可以當圖片輪播到最後一張時,將當前所在圖片位置設定為0,即回到第一張
但是回到第一張時的動作會與輪播方向相反,因為如果你是左輪播,你的translate從負數轉到0,會導致最後一張到第一張是右移
所以可以將需要輪播的圖片重新一份加在後面,當然也可以複製第一張圖片到最後,方法一樣
本文圖片為 圖a圖b圖a圖b,編號分別為0,1,2,3,0/2相同,1/3相同,當到達3時跳到1繼續輪播
***react部分,其中輪播是一個slider元件
banner.tsx: //這是呼叫元件的那個檔案,公司用到的是typescript
// 從後臺獲取到圖片資料data
getBannerList(data){
data = data.concat(data);//複製所有圖片加在後面
this.setState({BannerList: data, len: data.length});
}
render(){
return (
<div className="slider-box">
<Slider items={this.state.BannerList} len={this.state.len} speed={0.5} delay={3} autoplay={true}/>
</div>
);
}
當圖片到達最後一張時,將this.now = 1,transition = none,transform:translate(-320px),這個過程是瞬間的,肉眼看不出來,然後可以正常左滑
Slider 元件
touchstart = this.touchStart.bind(this);
touchmove = this.touchMove.bind(this);
touchend = this.touchEnd.bind(this);
//設定transform
cssTransform(ele, attr, val){
if(!ele.transform){
ele.transform = {};
};
//當傳入值時對屬性進行設定。
if(arguments.length > 2){
ele.transform[attr] = val;
var sval = '';
for(var s in ele.transform){
if(s === 'translateX'){
sval += s + '(' + ele.transform[s] + 'px)';
}
//如果實在不理解,可以console.log(sval)可以看到到最後一張時會有一個先跳到第二張再快速到第三張的過程
ele.style.WebkitTransform = ele.style.transform = sval;
}
}
else{
val = ele.transform[attr];
if(typeof val === 'undefined'){
if(attr === 'translateX'){
val = 0;
}
};
return val;
}
}
//自動輪播
auto(){
clearInterval(this.timer);
this.timer = setInterval(() => {
//當到達最後一張時
if(this.now === this.props.len - 1){
this.now = this.props.len / 2 - 1;
//這兩句程式碼很重要,是實現的關鍵,none是為了不出現平移而是直接跳變
this.LunBoEle.style.transition = 'none';
this.cssTransform(this.LunBoEle, 'translateX', - this.now * this.sliderWidth);
}
//定時是為了tab函式中執行的csstransform函式與上面到達最後一張時的csstransform有先後,不然仍會導致右移;
setTimeout(() => {
this.now++;
this.tab();
}, 30);
}, this.props.delay * 1000);
}
tab(){
this.LunBoEle.style.transition = '.5s';
this.cssTransform(this.LunBoEle, 'translateX', -this.now * this.sliderWidth);
let SelectIndex = this.now % (this.props.len / 2);
$('.slider-dots-wrap span').eq(SelectIndex).addClass('slider-dot-selected').siblings().removeClass('slider-dot-selected');
}
componentDidMount() {
this.LunBoEle = document.querySelector('ul.lunbo');
this.SliderEle = document.querySelector('.slider');
this.sliderWidth = $('.slider').width();
this.cssTransform(this.LunBoEle, 'translateX', 0);
this.auto.bind(this)();
this.SliderEle.addEventListener('touchstart', this.touchstart, false);
this.SliderEle.addEventListener('touchmove', this.touchmove, false);
this.SliderEle.addEventListener('touchend', this.touchend, false);
}
//觸發
touchStart(e: TouchEvent){
e.stopPropagation();
if(!this.stopped){
clearInterval(this.timer);
this.LunBoEle.style.transition = 'none';
let moveX = this.cssTransform.bind(this)(this.LunBoEle, 'translateX');
this.now = Math.round(-moveX / this.sliderWidth);
if(this.now === 0){
this.now = this.props.len / 2;
}else if(this.now === this.props.len - 1){
this.now = this.props.len / 2 - 1;
}
this.cssTransform(this.LunBoEle, 'translateX', -this.now * this.sliderWidth);
this.startPoint = e.changedTouches[0].pageX;
this.startEle = this.cssTransform.bind(this)(this.LunBoEle, 'translateX');
}
};
//移動
touchMove(e: TouchEvent){
e.preventDefault();
e.stopPropagation();
if(!this.stopped){
let endPoint = e.changedTouches[0].pageX;
let disX = endPoint - this.startPoint;
this.cssTransform.bind(this)(this.LunBoEle, 'translateX', disX + this.startEle);
}
}
//平移結束
touchEnd(e: TouchEvent){
e.stopPropagation();
if(!this.stopped){
let moveX = this.cssTransform.bind(this)(this.LunBoEle, 'translateX');
//這邊我是對移動做了判斷
if(Math.abs(moveX) > Math.abs(this.now * this.sliderWidth)){
this.now = Math.ceil(-moveX / this.sliderWidth);
}else{
this.now = Math.floor(-moveX / this.sliderWidth);
}
this.tab.bind(this)();
this.auto.bind(this)();
}
}
componentWillUnmount(){
//注意這邊的清楚很重要,因為使用者在使用時如果後臺修改,使用者重新整理,會導致下面的dot出現問題
clearInterval(this.timer);
//解除安裝的同時需要將所有事件清除掉
this.SliderEle.removeEventListener('touchstart', this.touchstart, false);
this.SliderEle.removeEventListener('touchmove', this.touchmove, false);
this.SliderEle.removeEventListener('touchend', this.touchend, false);
}
//防止如果只有一張輪播圖時進行輪播
componentDidUpdate(){
if((this.props.len / 2) === 1){
clearInterval(this.timer);
this.stopped = true;
}
else{
this.stopped = false;
}
}
render() {
let itemNodes = this.props.items.map((item, idx) => {
return <SliderItem item={item} count={this.props.len} key={'item' + idx} />;
});
let dotNodes = [];
let count = this.props.len / 2;
for(let i = 0; i < count; i++){
//為第一個dot點加上selected
dotNodes[i] = (
<span key={'dot' + i} className={'slider-dot' + (i === 0 ? ' slider-dot-selected' : '')}>
</span>
);
}
return (
<div className="slider">
<ul className="lunbo" style={{width: (this.props.len) * 100 + '%'}}>
{itemNodes}
</ul>
<div className="slider-dots-wrap">
{dotNodes}
</div>
</div>
);
};
sliderItem.tsx
render(){
//設定li 的寬度,100指的是ul寬度的100%
let width = 100 / (this.props.count) + '%';
let img =<img src={this.props.item.imageurl}/>
return (
<li className="slider-item" style={{width: width}}>
{img}
</li>
);
}
初次分享結束,希望我的程式碼對你有用哈~
我是個妹子哈,所以要噴的話請輕噴~~哈哈~