JQuery简易时间轴功能

场景:需要对某一系列用户的操作进行时间点记录和时间轴展示

参考资料:时间轴网址 ---> 对应Git地址

需要用到的JS和CSS:

插件自带功能与使用场景有些许不符,需要改造

1.新增自定义宽度属性:

2.显示问题修改:

3.修改部分自己业务需要的css样式,不在这里展开。

详细使用过程

1.定义Html元素(空DIV实体):

html 复制代码
    <div class="regionForTimeAxios"style="height:95%;">
        <div id="report_Time_Axios" class="timeline">
            <div class="timeline__wrap" style="height:100%;width: 100%; overflow-x: auto">
                <div class="timeline__items">
				
                </div>
            </div>
        </div>
    </div>

2.构造时间点Html串,并塞入页面中:

javascript 复制代码
 var timeLineHtml = '';
 //时间轴列表数据构造Html
 $.each(dataList, function (io, vo) {
     timeLineHtml += '<div class="timeline__item"><div class="timeline__content"><div class="reportTimeLineToolTip" style="width:170px;padding:2.5px 0;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"><span>【' + vo.on + '】</span><span>' + convertOpType(vo) + '</span></div><span>' + new Date(vo.time).format('yyyy-MM-dd hh:mm:ss') + '</span></div></div > '
 });
 if (timeLineHtml) {
     //塞入元素
     $('#report_Time_Axios .timeline__wrap .timeline__items').append(timeLineHtml)
     //时间轴渲染
     $('#report_Time_Axios').timeline({
         mode: 'horizontal',
         itemDefaultWidth: 200,
         //更多属性可查看JS
     });
 }

3.页面效果:

(看起来是左右滑动,实际上中间的轴并没有动)

如果设计到数据切换,渲染新数据前直接清空Div里的内容重新渲染即可。

修改后的JS和CSS文件

javascript 复制代码
// https://squarechip.github.io/timeline/ 来源地址
function timeline(collection, options) {
  const timelines = [];
  const warningLabel = 'Timeline:';
  let winWidth = window.innerWidth;
  let resizeTimer;
  let currentIndex = 0;
  // Set default settings
    const defaultSettings = {
        forceVerticalMode: {
            type: 'integer',
            defaultValue: 600
        },
        horizontalStartPosition: {
            type: 'string',
            acceptedValues: ['bottom', 'top'],
            defaultValue: 'top'
        },
        mode: {
            type: 'string',
            acceptedValues: ['horizontal', 'vertical'],
            defaultValue: 'vertical'
        },
        moveItems: {
            type: 'integer',
            defaultValue: 1
        },
        rtlMode: {
            type: 'boolean',
            acceptedValues: [true, false],
            defaultValue: false
        },
        startIndex: {
            type: 'integer',
            defaultValue: 0
        },
        verticalStartPosition: {
            type: 'string',
            acceptedValues: ['left', 'right'],
            defaultValue: 'left'
        },
        verticalTrigger: {
            type: 'string',
            defaultValue: '15%'
        },
        visibleItems: {
            type: 'integer',
            defaultValue: 3
        },
        itemDefaultWidth: {
            type: 'integer',
            defaultValue: 0
        }
    };

  // Helper function to test whether values are an integer
  function testValues(value, settingName) {
    if (typeof value !== 'number' && value % 1 !== 0) {
      console.warn(`${warningLabel} The value "${value}" entered for the setting "${settingName}" is not an integer.`);
      return false;
    }
    return true;
  }

  // Helper function to wrap an element in another HTML element
  function itemWrap(el, wrapper, classes) {
    wrapper.classList.add(classes);
    el.parentNode.insertBefore(wrapper, el);
    wrapper.appendChild(el);
  }

  // Helper function to wrap each element in a group with other HTML elements
  function wrapElements(items) {
    items.forEach((item) => {
      itemWrap(item.querySelector('.timeline__content'), document.createElement('div'), 'timeline__content__wrap');
      itemWrap(item.querySelector('.timeline__content__wrap'), document.createElement('div'), 'timeline__item__inner');
    });
  }

  // Helper function to check if an element is partially in the viewport
  function isElementInViewport(el, triggerPosition) {
    const rect = el.getBoundingClientRect();
    const windowHeight = window.innerHeight || document.documentElement.clientHeight;
    const defaultTrigger = defaultSettings.verticalTrigger.defaultValue.match(/(\d*\.?\d*)(.*)/);
    let triggerUnit = triggerPosition.unit;
    let triggerValue = triggerPosition.value;
    let trigger = windowHeight;
    if (triggerUnit === 'px' && triggerValue >= windowHeight) {
      console.warn('The value entered for the setting "verticalTrigger" is larger than the window height. The default value will be used instead.');
      [, triggerValue, triggerUnit] = defaultTrigger;
    }
    if (triggerUnit === 'px') {
      trigger = parseInt(trigger - triggerValue, 10);
    } else if (triggerUnit === '%') {
      trigger = parseInt(trigger * ((100 - triggerValue) / 100), 10);
    }
    return (
      rect.top <= trigger
      && rect.left <= (window.innerWidth || document.documentElement.clientWidth)
      && (rect.top + rect.height) >= 0
      && (rect.left + rect.width) >= 0
    );
  }

  // Helper function to add transform styles
  function addTransforms(el, transform) {
    el.style.webkitTransform = transform;
    el.style.msTransform = transform;
    el.style.transform = transform;
  }

  // Create timelines
  function createTimelines(timelineEl) {
    const timelineName = timelineEl.id ? `#${timelineEl.id}` : `.${timelineEl.className}`;
    const errorPart = 'could not be found as a direct descendant of';
    const data = timelineEl.dataset;
    let wrap;
    let scroller;
    let items;
    const settings = {};

    // Test for correct HTML structure
    try {
      wrap = timelineEl.querySelector('.timeline__wrap');
      if (!wrap) {
        throw new Error(`${warningLabel} .timeline__wrap ${errorPart} ${timelineName}`);
      } else {
        scroller = wrap.querySelector('.timeline__items');
        if (!scroller) {
          throw new Error(`${warningLabel} .timeline__items ${errorPart} .timeline__wrap`);
        } else {
          items = [].slice.call(scroller.children, 0);
        }
      }
    } catch (e) {
      console.warn(e.message);
      return false;
    }

    // Test setting input values
    Object.keys(defaultSettings).forEach((key) => {
      settings[key] = defaultSettings[key].defaultValue;

      if (data[key]) {
        settings[key] = data[key];
      } else if (options && options[key]) {
        settings[key] = options[key];
      }

      if (defaultSettings[key].type === 'integer') {
        if (!settings[key] || !testValues(settings[key], key)) {
          settings[key] = defaultSettings[key].defaultValue;
        }
      } else if (defaultSettings[key].type === 'string') {
        if (defaultSettings[key].acceptedValues && defaultSettings[key].acceptedValues.indexOf(settings[key]) === -1) {
          console.warn(`${warningLabel} The value "${settings[key]}" entered for the setting "${key}" was not recognised.`);
          settings[key] = defaultSettings[key].defaultValue;
        }
      }
    });

    // Further specific testing of input values
    const defaultTrigger = defaultSettings.verticalTrigger.defaultValue.match(/(\d*\.?\d*)(.*)/);
    const triggerArray = settings.verticalTrigger.match(/(\d*\.?\d*)(.*)/);
    let [, triggerValue, triggerUnit] = triggerArray;
    let triggerValid = true;
    if (!triggerValue) {
      console.warn(`${warningLabel} No numercial value entered for the 'verticalTrigger' setting.`);
      triggerValid = false;
    }
    if (triggerUnit !== 'px' && triggerUnit !== '%') {
      console.warn(`${warningLabel} The setting 'verticalTrigger' must be a percentage or pixel value.`);
      triggerValid = false;
    }
    if (triggerUnit === '%' && (triggerValue > 100 || triggerValue < 0)) {
      console.warn(`${warningLabel} The 'verticalTrigger' setting value must be between 0 and 100 if using a percentage value.`);
      triggerValid = false;
    } else if (triggerUnit === 'px' && triggerValue < 0) {
      console.warn(`${warningLabel} The 'verticalTrigger' setting value must be above 0 if using a pixel value.`);
      triggerValid = false;
    }

    if (triggerValid === false) {
      [, triggerValue, triggerUnit] = defaultTrigger;
    }

    settings.verticalTrigger = {
      unit: triggerUnit,
      value: triggerValue
    };

    if (settings.moveItems > settings.visibleItems) {
      console.warn(`${warningLabel} The value of "moveItems" (${settings.moveItems}) is larger than the number of "visibleItems" (${settings.visibleItems}). The value of "visibleItems" has been used instead.`);
      settings.moveItems = settings.visibleItems;
    }

    if (settings.startIndex > (items.length - settings.visibleItems) && items.length > settings.visibleItems) {
      console.warn(`${warningLabel} The 'startIndex' setting must be between 0 and ${items.length - settings.visibleItems} for this timeline. The value of ${items.length - settings.visibleItems} has been used instead.`);
      settings.startIndex = items.length - settings.visibleItems;
    } else if (items.length <= settings.visibleItems) {
      console.warn(`${warningLabel} The number of items in the timeline must exceed the number of visible items to use the 'startIndex' option.`);
      settings.startIndex = 0;
    } else if (settings.startIndex < 0) {
      console.warn(`${warningLabel} The 'startIndex' setting must be between 0 and ${items.length - settings.visibleItems} for this timeline. The value of 0 has been used instead.`);
      settings.startIndex = 0;
    }

    timelines.push({
      timelineEl,
      wrap,
      scroller,
      items,
      settings
    });
  }

  if (collection.length) {
    [].forEach.call(collection, createTimelines);
  }

  // Set height and widths of timeline elements and viewport
  function setHeightandWidths(tl) {
    // Set widths of items and viewport
    function setWidths() {
      tl.itemWidth = tl.settings.itemDefaultWidth > 0 ? tl.settings.itemDefaultWidth: tl.wrap.offsetWidth / tl.settings.visibleItems;
      tl.items.forEach((item) => {
        item.style.width = `${tl.itemWidth}px`;
      });
      tl.scrollerWidth = tl.itemWidth * tl.items.length;
      tl.scroller.style.width = `${tl.scrollerWidth}px`;
    }

    // Set height of items and viewport
    function setHeights() {
      let oddIndexTallest = 0;
      let evenIndexTallest = 0;
      tl.items.forEach((item, i) => {
        item.style.height = 'auto';
        const height = item.offsetHeight;
        if (i % 2 === 0) {
          evenIndexTallest = height > evenIndexTallest ? height : evenIndexTallest;
        } else {
          oddIndexTallest = height > oddIndexTallest ? height : oddIndexTallest;
        }
      });

      const transformString = `translateY(${evenIndexTallest}px)`;
      tl.items.forEach((item, i) => {
        if (i % 2 === 0) {
          item.style.height = `${evenIndexTallest}px`;
          if (tl.settings.horizontalStartPosition === 'bottom') {
            item.classList.add('timeline__item--bottom');
            addTransforms(item, transformString);
          } else {
            item.classList.add('timeline__item--top');
          }
        } else {
          item.style.height = `${oddIndexTallest}px`;
          if (tl.settings.horizontalStartPosition !== 'bottom') {
            item.classList.add('timeline__item--bottom');
            addTransforms(item, transformString);
          } else {
            item.classList.add('timeline__item--top');
          }
        }
      });
      if (tl.items.length == 1) {
          tl.scroller.style.height = `${evenIndexTallest * 2}px`;
      } else {
          tl.scroller.style.height = `${evenIndexTallest + oddIndexTallest}px`;
      }
    }

    if (window.innerWidth > tl.settings.forceVerticalMode) {
      setWidths();
      setHeights();
    }
  }

  // Create and add arrow controls to horizontal timeline
  function addNavigation(tl) {
    if (tl.items.length > tl.settings.visibleItems) {
      const prevArrow = document.createElement('button');
      const nextArrow = document.createElement('button');
      const topPosition = tl.items[0].offsetHeight;
      prevArrow.className = 'timeline-nav-button timeline-nav-button--prev';
      nextArrow.className = 'timeline-nav-button timeline-nav-button--next';
      prevArrow.textContent = 'Previous';
      nextArrow.textContent = 'Next';
      prevArrow.style.top = `${topPosition}px`;
      nextArrow.style.top = `${topPosition}px`;
      if (currentIndex === 0) {
        prevArrow.disabled = true;
      } else if (currentIndex === (tl.items.length - tl.settings.visibleItems)) {
        nextArrow.disabled = true;
      }
      //tl.timelineEl.appendChild(prevArrow);
      //tl.timelineEl.appendChild(nextArrow);
    }
  }

  // Add the centre line to the horizontal timeline
  function addHorizontalDivider(tl) {
    const divider = tl.timelineEl.querySelector('.timeline-divider');
    if (divider) {
      tl.timelineEl.removeChild(divider);
    }
    const topPosition = tl.items[0].offsetHeight;
    const horizontalDivider = document.createElement('span');
    horizontalDivider.className = 'timeline-divider';
    horizontalDivider.style.top = `${topPosition}px`;
    tl.timelineEl.appendChild(horizontalDivider);
  }

  // Calculate the new position of the horizontal timeline
  function timelinePosition(tl) {
    const position = tl.items[currentIndex].offsetLeft;
    const str = `translate3d(-${position}px, 0, 0)`;
    addTransforms(tl.scroller, str);
  }

  // Make the horizontal timeline slide
  function slideTimeline(tl) {
    const navArrows = tl.timelineEl.querySelectorAll('.timeline-nav-button');
    const arrowPrev = tl.timelineEl.querySelector('.timeline-nav-button--prev');
    const arrowNext = tl.timelineEl.querySelector('.timeline-nav-button--next');
    const maxIndex = tl.items.length - tl.settings.visibleItems;
    const moveItems = parseInt(tl.settings.moveItems, 10);
    [].forEach.call(navArrows, (arrow) => {
      arrow.addEventListener('click', function(e) {
        e.preventDefault();
        currentIndex = this.classList.contains('timeline-nav-button--next') ? (currentIndex += moveItems) : (currentIndex -= moveItems);
        if (currentIndex === 0 || currentIndex < 0) {
          currentIndex = 0;
          arrowPrev.disabled = true;
          arrowNext.disabled = false;
        } else if (currentIndex === maxIndex || currentIndex > maxIndex) {
          currentIndex = maxIndex;
          arrowPrev.disabled = false;
          arrowNext.disabled = true;
        } else {
          arrowPrev.disabled = false;
          arrowNext.disabled = false;
        }
        timelinePosition(tl);
      });
    });
  }

  // Set up horizontal timeline
  function setUpHorinzontalTimeline(tl) {
    if (tl.settings.rtlMode) {
      currentIndex = tl.items.length > tl.settings.visibleItems ? tl.items.length - tl.settings.visibleItems : 0;
    } else {
      currentIndex = tl.settings.startIndex;
    }
    tl.timelineEl.classList.add('timeline--horizontal');
    setHeightandWidths(tl);
    timelinePosition(tl);
    addNavigation(tl);
    addHorizontalDivider(tl);
    slideTimeline(tl);
  }

  // Set up vertical timeline
  function setUpVerticalTimeline(tl) {
    let lastVisibleIndex = 0;
    tl.items.forEach((item, i) => {
      item.classList.remove('animated', 'fadeIn');
      if (!isElementInViewport(item, tl.settings.verticalTrigger) && i > 0) {
        item.classList.add('animated');
      } else {
        lastVisibleIndex = i;
      }
      const divider = tl.settings.verticalStartPosition === 'left' ? 1 : 0;
      if (i % 2 === divider && window.innerWidth > tl.settings.forceVerticalMode) {
        item.classList.add('timeline__item--right');
      } else {
        item.classList.add('timeline__item--left');
      }
    });
    for (let i = 0; i < lastVisibleIndex; i += 1) {
      tl.items[i].classList.remove('animated', 'fadeIn');
    }
    // Bring elements into view as the page is scrolled
    window.addEventListener('scroll', () => {
      tl.items.forEach((item) => {
        if (isElementInViewport(item, tl.settings.verticalTrigger)) {
          item.classList.add('fadeIn');
        }
      });
    });
  }

  // Reset timelines
  function resetTimelines(tl) {
    tl.timelineEl.classList.remove('timeline--horizontal', 'timeline--mobile');
    tl.scroller.removeAttribute('style');
    tl.items.forEach((item) => {
      item.removeAttribute('style');
      item.classList.remove('animated', 'fadeIn', 'timeline__item--left', 'timeline__item--right');
    });
    const navArrows = tl.timelineEl.querySelectorAll('.timeline-nav-button');
    [].forEach.call(navArrows, (arrow) => {
      arrow.parentNode.removeChild(arrow);
    });
  }

  // Set up the timelines
  function setUpTimelines() {
    timelines.forEach((tl) => {
      tl.timelineEl.style.opacity = 0;
      if (!tl.timelineEl.classList.contains('timeline--loaded')) {
        wrapElements(tl.items);
      }
      resetTimelines(tl);
      if (window.innerWidth <= tl.settings.forceVerticalMode) {
        tl.timelineEl.classList.add('timeline--mobile');
      }
      if (tl.settings.mode === 'horizontal' && window.innerWidth > tl.settings.forceVerticalMode) {
        setUpHorinzontalTimeline(tl);
      } else {
        setUpVerticalTimeline(tl);
      }
      tl.timelineEl.classList.add('timeline--loaded');
      setTimeout(() => {
        tl.timelineEl.style.opacity = 1;
      }, 500);
    });
  }

  // Initialise the timelines on the page
  setUpTimelines();

  window.addEventListener('resize', () => {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(() => {
      const newWinWidth = window.innerWidth;
      if (newWinWidth !== winWidth) {
        setUpTimelines();
        winWidth = newWinWidth;
      }
    }, 250);
  });
}

// Register as a jQuery plugin if the jQuery library is present
if (window.jQuery) {
  (($) => {
    $.fn.timeline = function(opts) {
      timeline(this, opts);
      return this;
    };
  })(window.jQuery);
}

--------------------------------------------

html 复制代码
.timeline {
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    position: relative
}

    .timeline *, .timeline :after, .timeline :before {
        -webkit-box-sizing: inherit;
        box-sizing: inherit
    }

    .timeline:not(.timeline--horizontal):before {
        background-color: #ddd;
        bottom: 0;
        content: '';
        left: 50%;
        margin-left: -2px;
        position: absolute;
        top: 0;
        width: 4px;
        z-index: 1
    }

.timeline__wrap {
    overflow: hidden;
    position: relative;
    z-index: 2
}

.timeline__item {
    font-size: 16px;
    font-size: 1rem;
    padding: .625rem 2.5rem .625rem 0;
    position: relative;
    width: 50%;
    z-index: 2
}

    .timeline__item:after {
        background-color: #fff;
        border: 2px solid #000;
        border-radius: 50%;
        content: '';
        height: 10px;
        position: absolute;
        right: -10px;
        -webkit-transform: translateY(-50%);
        -ms-transform: translateY(-50%);
        transform: translateY(-50%);
        top: 50%;
        width: 10px;
        z-index: 1
    }

    .timeline__item.animated {
        -webkit-animation-duration: 1s;
        animation-duration: 1s;
        -webkit-animation-fill-mode: both;
        animation-fill-mode: both;
        opacity: 0
    }

    .timeline__item.fadeIn {
        -webkit-animation-name: fadeIn;
        animation-name: fadeIn
    }

.timeline__item--left {
    left: 0
}

.timeline__item--right {
    left: 50%;
    padding: .625rem 0 .625rem 2.5rem
}

    .timeline__item--right:after {
        left: -10px
    }

    .timeline__item--right .timeline__content:before {
        border-bottom: 10px solid transparent;
        border-right: 12px solid #ccc;
        border-left: none;
        border-top: 10px solid transparent;
        left: -12px
    }

    .timeline__item--right .timeline__content:after {
        border-bottom: 9px solid transparent;
        border-right: 11px solid #fff;
        border-left: none;
        border-top: 9px solid transparent;
        left: -10px
    }

.timeline__content {
    background-color: #fff;
    border: 1px solid #ccc;
    border-radius: 10px;
    color: #333;
    display: block;
    padding: 10px;
    position: relative;
}

    .timeline__content:after, .timeline__content:before {
        content: '';
        height: 0;
        position: absolute;
        -webkit-transform: translateY(-50%);
        -ms-transform: translateY(-50%);
        transform: translateY(-50%);
        top: 50%;
        width: 0
    }

    .timeline__content:before {
        border-bottom: 10px solid transparent;
        border-left: 12px solid #ccc;
        border-top: 10px solid transparent;
        right: -12px;
        z-index: 1
    }

    .timeline__content:after {
        border-bottom: 9px solid transparent;
        border-left: 11px solid #fff;
        border-top: 9px solid transparent;
        right: -10px;
        z-index: 2
    }

    .timeline__content h2 {
        font-size: 1.25rem;
        font-weight: 700;
        margin: 0 0 .625rem
    }

    .timeline__content p {
        font-size: .9375rem;
        line-height: 1.5;
        margin-bottom: 10px
    }

.timeline--horizontal {
    font-size: 0;
    padding: 0;
    overflow: hidden;
    white-space: nowrap
}

    .timeline--horizontal .timeline-divider {
        background-color: #ddd;
        display: block;
        height: 4px;
        left: 0px;
        position: absolute;
        -webkit-transform: translateY(-50%);
        -ms-transform: translateY(-50%);
        transform: translateY(-50%);
        right: 0px;
        z-index: 1
    }

    .timeline--horizontal .timeline__items {
        -webkit-transition: all .8s;
        -o-transition: all .8s;
        transition: all .8s;
        will-change: transform
    }

    .timeline--horizontal .timeline__item {
        display: inline-block;
        left: 0;
        padding: 1rem 0 1.5rem;
        position: relative;
        -webkit-transition: none;
        -o-transition: none;
        transition: none;
        vertical-align: top;
        white-space: normal
    }

        .timeline--horizontal .timeline__item:after {
            left: 50%;
            right: auto;
            -webkit-transform: translate(-50%,-50%);
            -ms-transform: translate(-50%,-50%);
            transform: translate(-50%,-50%);
            top: 100%
        }

        .timeline--horizontal .timeline__item .timeline__item__inner {
            display: table;
            height: 100%;
            width: 100%
        }

        .timeline--horizontal .timeline__item .timeline__content__wrap {
            display: table-cell;
            margin: 0;
            padding: 0 5px;
            vertical-align: bottom
        }

        .timeline--horizontal .timeline__item .timeline__content:before {
            border-left: 12px solid transparent;
            border-right: 12px solid transparent;
            border-top: 12px solid #ccc;
            left: 50%;
            right: auto;
            -webkit-transform: translateX(-50%);
            -ms-transform: translateX(-50%);
            transform: translateX(-50%);
            top: 100%
        }

        .timeline--horizontal .timeline__item .timeline__content:after {
            border-left: 10px solid transparent;
            border-right: 10px solid transparent;
            border-top: 10px solid #fff;
            left: 50%;
            right: auto;
            -webkit-transform: translateX(-50%);
            -ms-transform: translateX(-50%);
            transform: translateX(-50%);
            top: 100%
        }

    .timeline--horizontal .timeline__item--bottom {
        padding: 1.5rem 0 1rem
    }

        .timeline--horizontal .timeline__item--bottom:after {
            top: 0
        }

        .timeline--horizontal .timeline__item--bottom .timeline__content__wrap {
            vertical-align: top
        }

        .timeline--horizontal .timeline__item--bottom .timeline__content:before {
            border-bottom: 12px solid #ccc;
            border-left: 12px solid transparent;
            border-right: 12px solid transparent;
            border-top: none;
            bottom: 100%;
            top: auto
        }

        .timeline--horizontal .timeline__item--bottom .timeline__content:after {
            border-bottom: 10px solid #fff;
            border-left: 10px solid transparent;
            border-right: 10px solid transparent;
            border-top: none;
            bottom: 100%;
            top: auto
        }

.timeline-nav-button {
    background-color: #fff;
    border: 2px solid #ddd;
    border-radius: 50px;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    -webkit-box-shadow: none;
    box-shadow: none;
    cursor: pointer;
    display: block;
    height: 40px;
    outline: 0;
    position: absolute;
    text-indent: -9999px;
    -webkit-transform: translateY(-50%);
    -ms-transform: translateY(-50%);
    transform: translateY(-50%);
    top: 50%;
    width: 40px;
    z-index: 10
}

    .timeline-nav-button:disabled {
        opacity: .5;
        pointer-events: none
    }

    .timeline-nav-button:before {
        background-position: center center;
        background-repeat: no-repeat;
        content: '';
        display: block;
        height: 14px;
        left: 50%;
        position: absolute;
        -webkit-transform: translateX(-50%) translateY(-50%);
        -ms-transform: translateX(-50%) translateY(-50%);
        transform: translateX(-50%) translateY(-50%);
        top: 50%;
        width: 8px
    }

.timeline-nav-button--prev {
    left: 0
}

    .timeline-nav-button--prev:before {
        background-image: url(../images/arrow-left.svg)
    }

.timeline-nav-button--next {
    right: 0
}

    .timeline-nav-button--next:before {
        background-image: url(../images/arrow-right.svg)
    }

.timeline--mobile {
    padding: 0
}

    .timeline--mobile:before {
        left: 10px !important;
        margin: 0 !important
    }

    .timeline--mobile .timeline__item {
        left: 0;
        padding-left: 40px;
        padding-right: 0;
        width: 100%
    }

        .timeline--mobile .timeline__item:after {
            left: 2px;
            margin: 0
        }

        .timeline--mobile .timeline__item .timeline__content:before {
            left: -12px;
            border-bottom: 12px solid transparent;
            border-right: 12px solid #ccc;
            border-left: none;
            border-top: 12px solid transparent
        }

        .timeline--mobile .timeline__item .timeline__content:after {
            left: -10px;
            border-bottom: 10px solid transparent;
            border-right: 10px solid #fff;
            border-left: none;
            border-top: 10px solid transparent
        }

@-webkit-keyframes fadeIn {
    0% {
        opacity: 0;
        top: 70px
    }

    100% {
        opacity: 1;
        top: 0
    }
}

@keyframes fadeIn {
    0% {
        opacity: 0;
        top: 70px
    }

    100% {
        opacity: 1;
        top: 0
    }
}

@-webkit-keyframes liftUp {
    0% {
        top: 0
    }

    100% {
        top: -15px
    }
}

@keyframes liftUp {
    0% {
        top: 0
    }

    100% {
        top: -15px
    }
}
相关推荐
匹马夕阳2 分钟前
详细对比JS中XMLHttpRequest和fetch的使用
开发语言·javascript·ecmascript
长风清留扬31 分钟前
小程序开发实战项目:构建简易待办事项列表
javascript·css·微信小程序·小程序·apache
程序员_三木32 分钟前
从 0 到 1 实现鼠标联动粒子动画
javascript·计算机外设·webgl·three.js
点点滴滴的记录38 分钟前
Java的CompletableFuture实现原理
java·开发语言·javascript
web Rookie1 小时前
React 中 createContext 和 useContext 的深度应用与优化实战
前端·javascript·react.js
男孩121 小时前
react高阶组件及hooks
前端·javascript·react.js
hhzz2 小时前
vue前端项目中实现电子签名功能(附完整源码)
前端·javascript·vue.js
秋雨凉人心2 小时前
上传npm包加强
开发语言·前端·javascript·webpack·npm·node.js
JoeChen.3 小时前
PostCSS插件——postcss-pxtorem结合动态调整rem实现字体自适应
javascript·ecmascript·postcss
前端切圖仔3 小时前
失业,仲裁,都赶上了(二)
前端·javascript·程序员