<a href="#" class="a-progress-button" data-progress-button>
<svg class="a-progress-button__svg">
<circle class="a-progress-button__indicator" r="15" cx="16" cy="16" />
</svg>
<svg class="a-icon" role="img" title="">
<use xlink:href="#close" />
</svg>
</a>
<a href="{{ buttonUrl }}" class="a-progress-button" data-progress-button>
<svg class="a-progress-button__svg">
<circle class="a-progress-button__indicator" r="15" cx="16" cy="16" />
</svg>
{{> @a-icon name="close" }}
</a>
$buttonWidth: 32px; // NOTE: If you change this value you need to change the r, cx & cy attributes in the markup
$stroke-width: 2px;
$transition-duration: 100ms;
$svg-radius: ($buttonWidth / 2) - ($stroke-width / 2);
$offset: $buttonWidth / 2;
$circle-circumference: 2 * 3.14159 * $svg-radius; // 2 * π * R = C
.a-progress-button {
z-index: 5;
display: block;
position: fixed;
top: 52px;
right: 20px;
background-color: white;
border-radius: 50%;
width: $buttonWidth;
height: $buttonWidth;
box-shadow: 0 1px 7px 0 rgba(0, 0, 0, 0.1);
@include media($sm-breakpoint) {
display: none;
}
&__svg {
transform: rotate(-90deg);
width: 100%;
height: 100%;
}
&__indicator {
fill: $color-text-negative;
stroke: $color-primary;
stroke-width: 2px;
stroke-dasharray: $circle-circumference;
stroke-dashoffset: $circle-circumference;
transition: stroke-dashoffset $transition-duration;
}
.a-icon {
position: absolute;
fill: $color-primary;
display: block;
width: $buttonWidth / 2;
height: $buttonWidth / 2;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
}
.a-progress-button {
opacity: 1;
transition: opacity $transition-fast ease;
&--hidden {
opacity: 0;
}
}
import BaseView from "base-view";
import throttle from "lodash.throttle";
const PROGRESS_INDICATOR_SELECTOR = '.a-progress-button__indicator';
const HIDDEN_STATE = 'a-progress-button--hidden';
export default class ProgressButton extends BaseView {
constructor(element) {
super(element);
this.article = document.querySelector('[data-article-progress]');
this.progressIndicator = this.element.querySelector(PROGRESS_INDICATOR_SELECTOR);
this.circumference = this.progressIndicator.r.baseVal.value * 2 * Math.PI; // Progress button circumference
this.header = false;
this.maybeHidden();
this.updateProgress(); // Init progress on page load / page refresh
}
bind() {
viewport_service.on( 'change', this.viewportChange.bind(this) );
this.resizeHanlder = throttle(this.updateProgress, 500).bind(this);
this.scrollHanlder = throttle(this.updateProgress, 100).bind(this);
this.hiddenHanlder = throttle(this.toggleHidden, 100).bind(this);
this.viewportChange();
}
bindProgress() {
window.addEventListener( 'resize', this.resizeHanlder );
window.addEventListener( 'scroll', this.scrollHanlder );
if ( this.header ) window.addEventListener( 'scroll', this.hiddenHanlder );
}
unbindProgress() {
window.removeEventListener('resize', this.resizeHanlder );
window.removeEventListener('scroll', this.scrollHanlder );
if ( this.header ) window.removeEventListener('scroll', this.hiddenHanlder );
}
viewportChange() {
this.maybeHidden();
if ( viewport_service.isMobile() ) {
this.bindProgress();
}
else {
this.unbindProgress();
}
}
updateProgress() {
this.relativeScroll = (window.scrollY - this.article.offsetTop) / (this.article.clientHeight - window.innerHeight);
this.progress = (
(this.relativeScroll < 0) ? 0 : (
(this.relativeScroll > 1) ? 1 : this.relativeScroll
)
);
this.progressIndicator.style.strokeDashoffset = this.circumference * (1 - (this.progress / 1));
}
toggleHidden() {
if( (this.header.getBoundingClientRect().top - parseInt(getComputedStyle(this.header).top)) <= 1 ) {
this.removeClass( HIDDEN_STATE );
}
else {
this.addClass( HIDDEN_STATE );
}
}
maybeHidden() {
if ( !document.body.classList.contains('ads') ) {
return;
}
// Hide the button by default
this.addClass( HIDDEN_STATE );
// Get the header element reference to check the sticky state
this.header = document.querySelector('.o-header');
}
destroy() {
super.destroy();
this.unbindProgress();
}
}
{
"buttonUrl": "#"
}
To make this button work, assign HTML attribute data-article-progress
to the corresponding section / article.
Example
<div class="news-article" data-article-progress>