这是一款带阅读进度指示的侧边栏列表项UI设计效果。该设计的目的是在用用户滚动鼠标时,在侧边栏列表项中每一个段落标题前都带有一个指示当前段落阅读进度的圆形进度条,使用户可以看到当前的阅读进度,提升用户体验。
该阅读指示UI设计使用SVG,CSS3和jQuery来制作。另外在页面滚动浏览时,URL地址会随着页面的段落而改变,处于安全原因,在本地浏览时Chrome浏览器可能会看不到效果。
该阅读指示效果在移动手机等小屏幕设备中是隐藏的。
HTML结构
该阅读指示效果的HTML结构由多个<article>
元素组成,另外使用一个<aside>
元素来作为文章列表的侧边栏。
<div class="cd-articles"> <article> <header> <img src="img/img-1.png" alt="article image"> <h1>......</h1> </header> <p>......</p> <!-- additional content here --> </article> <article> <!-- article content here --> </article> <!-- additional articles here --> <aside class="cd-read-more"> <ul> <li> <a href="index.html"> <em>20 Star Wars Secrets Revealed</em> <b>by J. Morrison</b> <svg x="0px" y="0px" width="36px" height="36px" viewBox="0 0 36 36"> <circle fill="none" stroke="#2a76e8" stroke-width="2" cx="18" cy="18" r="16" stroke-dasharray="100 100" stroke-dashoffset="100" transform="rotate(-90 18 18)"></circle> </svg> </a> </li> <!-- additional links to articles --> </ul> </aside> <!-- .cd-read-more --> </div> <!-- .cd-articles -->
CSS样式
<aside>
元素仅仅在大屏幕设备(viewport宽度大于1100像素)中可见,它使用绝对定位,并且它放置在.cd-articles
元素的右上角位置。.fixed
class类用于使它位置固定,一直跟着页面滚动。
@media only screen and (min-width: 1100px) { .cd-articles { position: relative; width: 970px; padding-right: 320px; } } .cd-read-more { /* hide on mobile */ display: none; } @media only screen and (min-width: 1100px) { .cd-read-more { display: block; width: 290px; position: absolute; top: 3em; right: 0; } .cd-read-more.fixed { position: fixed; right: calc(50% - 485px); } }
为了创建进度条效果,这里使用了2个SVG属性:stroke-dasharray
和stroke-dashoffset
。你可以将圆形进度条想象为一条虚线,stroke-dasharray
属性可以让你控制虚线和它下面的空槽,而stroke-dashoffset
属性可以让你控制虚线的开始位置。
假如我们初始化stroke-dasharray="100 100"
,以及stroke-dashoffset="100"
,(100是SVG圆形的周长),这样,虚线和空槽的长度都等于圆周长,stroke-dashoffset
也等于圆周长,而此时只有空槽是可见的。要创建圆形进度条效果,只需要修改stroke-dashoffset
的值,使它从100变为0既可。
JavaScript
在大屏幕设备中(viewport宽度大于1100像素),特效为窗口的滚动事件绑定了2个方法:updateArticle()
和updateSidebarPosition()
。第一个方法用于检测用户正在阅读文章的哪一个段落,并相应的更新SVG stroke-dashoffset
属性来更新圆形进度条的进度。
第二个方法用于根据窗口滚动的值来更新侧边栏的位置属性,(使用 .fixed
)。
另外还有一个changeUrl()
函数用于根据页面段落来更新地址栏的URL。
function updateArticle() { var scrollTop = $(window).scrollTop(); articles.each(function(){ //articles = $('.cd-articles').children('article'); var article = $(this), articleSidebarLink = articleSidebarLinks.eq(article.index()).children('a'); //articleSidebarLinks = $('.cd-read-more').find('li') if( articleTop > scrollTop) { //articleTop = $(this).offset().top articleSidebarLink.removeClass('read reading'); } else if( scrollTop >= articleTop && articleTop + articleHeight > scrollTop) { //articleHeight = $(this).outerHeight() var dashoffsetValue = svgCircleLength*( 1 - (scrollTop - articleTop)/articleHeight); //svgCircleLength = 100 articleSidebarLink.addClass('reading').removeClass('read').find('circle').attr({ 'stroke-dashoffset': dashoffsetValue }); changeUrl(articleSidebarLink.attr('href')); } else { articleSidebarLink.removeClass('reading').addClass('read'); } }); } function updateSidebarPosition() { var scrollTop = $(window).scrollTop(); if( scrollTop > articlesWrapperTop) { //$('.cd-articles').offset().top aside.removeClass('fixed').attr('style', ''); //aside = $('.cd-read-more') } else if( scrollTop >= articlesWrapperTop && scrollTop < articlesWrapperTop + articlesWrapperHeight - windowHeight) { // articlesWrapperHeight = $('.cd-articles').outerHeight() aside.addClass('fixed').attr('style', ''); } else { if( aside.hasClass('fixed') ) aside.removeClass('fixed').css('top', articlesWrapperHeight + articlePaddingTop - windowHeight + 'px');//articlePaddingTop = Number($('.cd-articles').children('article').eq(1).css('padding-top').replace('px', '')) } }