前端练习八:水平时间轴


水平时间轴(响应式+支持左滑右滑+支持左右方向键) 

horizontalTimeLine.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
        <title>HorizontalTimeline</title>
        <link rel="stylesheet" href="timeline.css">
    </head>
    <body>
        <section class="timeline">
            <ol>

                <li>
                    <div>
                    <time>2017</time> 这是一个水平时间轴,它有有很多优点,容我一一道来。
                    </div>
                </li>
                <li>
                    <div>
                    <time>2017</time> 响应式设计,在屏幕宽度低于 768px 的情况下,会变成垂直的时间轴。
                    </div>
                </li>
                <li>
                    <div>
                    <time>2017</time> 支持左滑右滑操作,利用 Hammer.js 实现。
                    </div>
                </li>
                <li>
                    <div>
                    <time>2017</time> 支持左右方向键,左方向键的键码为 37, 而右方向键为 39。
                    </div>
                </li>
                <li>
                    <div>
                    <time>2017</time> 底下不用看了,因为都是复制的。
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li>
                    <div>
                    <time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.
                    </div>
                </li>
                <li></li>
            </ol>
            <div class="arrows">
                <button class="arrow arrow-pre disabled">
                    <img src="pre.svg" alt="pre timeline arrow">
                </button>

                <button class="arrow arrow-next">
                    <img src="next.svg" alt="next timeline arrow">
                </button>
            </div>
        </section>
        <script src="https://cdn.bootcss.com/hammer.js/2.0.8/hammer.min.js"></script>
        <script src="timeline.js"></script>
    </body>
</html>

 

timeline.css

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
/*box-sizing:content-box是默认值。
意思是:边框border和内边距padding的值是包含在被作用的标签的width和height之内的。
也就是说,如果你将一个元素的width设为100px,那么这100px会包含其它的border和padding,
内容区的实际宽度会是width减去(border + padding)的计算值。
但是,要注意它不包括外边距margin的值在内。
大多数情况下这使得我们更容易的去设定一个元素的宽高。
*/
body {
    background-color: #456990;
    line-height: : 1.5;
}
/*“::selection”用来改变在浏览器中选中文本后的设置。*/
/*如,被选中后的背景色为红色,被选中后的字体颜色为蓝色*/
::selection {
    background-color: red;
    color: blue;
}

/*white-space:它的属性用来设置如何处理元素内的空白。
默认值为,nornmal,空白会被浏览器给忽略。
pre,空白会被浏览器保留,同<pre>标签的作用类似。
nowrap,文本不会换行,在同一行上继续显示,直到遇到换行符。
pre-wrap,保留空白,但是 正常地进行换行。*/

/*overflow-x,用来设置,如果元素中内容的左右边缘,溢出元素的内容区域的话怎么办?
auto:如果溢出框,则应该提供滚动机制。
hidden:    裁剪内容 - 不提供滚动机制。
scroll:裁剪内容 - 提供滚动机制。*/
.timeline {
  white-space: nowrap;
  overflow-x: hidden;
}
.timeline ol {
  font-size: 0;
  padding: 250px 0;
  width: 100vw;
  transition: all 1s;
}
.timeline ol li {
  position: relative;
  display: inline-block;
  width: 160px;
  height: 3px;
  list-style-type: none;
  background-color: #fff;
}
.timeline ol li:last-child {
  width: 280px;
}
.timeline ol li:not(:first-child) {
  margin-left: 14px;
}
.timeline ol li:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: calc(101%);
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background-color: #F45B69;
}
.timeline ol li div {
  position: absolute;
  left: calc(107%);
  width: 280px;
  padding: 15px;
  font-size: 1rem;
  white-space: normal;
  color: #333;
  background-color: #fff;
}
.timeline ol li div::before {
  content: '';
  position: absolute;
  top: 100%;
  left: 0;
  width: 0;
  height: 0;
  border-style: solid;
}
.timeline ol li:nth-child(odd) div {
  top: -16px;
  transform: translateY(-100%);
}
.timeline ol li:nth-child(odd) div::before {
  top: 100%;
  border-width: 8px 8px 0 0 ;
  border-color: #fff transparent transparent transparent;
}
.timeline ol li:nth-child(even) div {
  top: 16px;
}
.timeline ol li:nth-child(even) div::before {
  top: -8px;
  border-width: 0 8px 8px 0;
  border-color: transparent transparent #fff transparent;
}
.timeline div.arrows {
  display: flex;
  justify-content: center;
  margin-bottom: 20px;
}
.timeline div.arrows button {
  border: none;
  background-color: #F45B69;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  outline: none;
  cursor: pointer;
}
.timeline div.arrows button.arrow-pre {
  margin-right: 20px;
}
.timeline div.arrows button.disabled {
  opacity: .5;
}
@media screen and (max-width: 768px) {
  .timeline ol {
    width: auto;
    padding: 0;
    transform: 0 !important;
  }
  .timeline ol li {
    width: auto;
    height: auto;
    display: block;
    background-color: transparent;
  }
  .timeline ol li:first-child {
    margin-top: 20px;
  }
  .timeline ol li:not(:first-child) {
    margin-left: auto;
  }
  .timeline ol li div {
    width: 94%;
    height: auto !important;
    margin: 0 auto 25px;
    position: static;
  }
  .timeline ol li:nth-child(odd) div {
    transform: none;
  }
  .timeline ol li:nth-child(odd) div::before,
  .timeline ol li:nth-child(even) div::before,
  .timeline ol li:not(:last-child)::after {
    display: none;
  }
  .timeline div.arrows {
    display: none;
  }
}

 

timeline.js

const timeline = document.querySelector('.timeline ol'),
      elH = document.querySelectorAll('.timeline ol div'),
      arrows = document.querySelectorAll('.arrows .arrow'),
      arrowPrev = document.querySelector('.timeline .arrows .arrow-pre'),
      arrowNext = document.querySelector('.timeline .arrows .arrow-next'),
      firstItem = document.querySelector('.timeline li:first-child'),
      lastItem = document.querySelector('.timeline ol li:last-child'),
      xScrolling = 280,
      disabledClass = 'disabled';

window.addEventListener('load', init, false);
function init() {
    setEqualHeight(elH);
    arrowPrev.disabled = true;
    animateTl(xScrolling, arrows, timeline);
    setSwipeFn(timeline, arrowPrev, arrowNext);
    setKeyboardFn(arrowPrev, arrowNext);
}

function setEqualHeight(el) {
    var largestHeight = (el[0] && el[0].offsetHeight) || 0,
        length = el.length;

    for(var i = 1; i < length; i++) {
        if (el[i].offsetHeight > largestHeight) {
            largestHeight = el[i].offsetHeight;
        }
    }

    for(var i = 0; i < length; i++) {
        el[i].style.height = largestHeight + 'px';
    }
}

function animateTl(scrolling, el, tl) {
    for(var i = 0; i < el.length; i++) {
        var count = 0;
        el[i].addEventListener('click', function() {

            if (!arrowPrev.disabled) {
                arrowPrev.disabled = true;
            }

            if (!arrowNext.disabled) {
                arrowNext.disabled = true;
            }

            var sign = (this.classList.contains('arrow-pre')) ? "" : "-";

            if (count === 0) {
                tl.style.transform = "translateX( -" + scrolling + 'px )';
            } else {
                var tlStyle = getComputedStyle(tl),
                    values = parseInt(tlStyle.getPropertyValue('transform').split(',')[4]) + 
                             parseInt(sign + scrolling);
                tl.style.transform = 'translateX(' + values + 'px )';
            }
            count += 1;

            setTimeout(()=> {
                isElementInViewport(firstItem) ? setBtnState(arrowPrev) : setBtnState(arrowPrev, false);
                isElementInViewport(lastItem) ? setBtnState(arrowNext) : setBtnState(arrowNext, false);
            }, 1100);
        }, false);
    }
}

function isElementInViewport(el) {
    var rect = el.getBoundingClientRect();
    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
}

function setBtnState(el, flag = true) {
    if (flag) {
        el.classList.add(disabledClass);
    } else {
        if (el.classList.contains(disabledClass)) {
            el.classList.remove(disabledClass);
        }
        el.disabled = false;
    }
}

function setSwipeFn(tl, prev, next) {
    const hammer = new Hammer(tl);
    hammer.on('swipeleft', ()=>{ next.click(); });
    hammer.on('swiperight', ()=>{ prev.click(); });
}

function setKeyboardFn(prev, next) {
    document.addEventListener('keydown', (e)=> {
        if (e.which === 37 || e.which === 39) {
            var timelineOffsetTop = timeline.offsetTop,
                y = window.pageYOffset;

            if (timelineOffsetTop !== y) {
                window.scrollTo(0, timelineOffsetTop);
            }

            if (e.which === 37) {
                prev.click();
            }

            if (e.which === 39) {
                next.click();
            }
        }
    });
}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM