How to animate circular progress bar on page scroll down - javascript

I have a circular progress bar that animates when the page loads, but I want it to animate when the user scrolls down to it, as it will be in the middle of the page. Right now if the page loads, the user does not see the animation.
So essentially, the animation should be paused until the user scrolls down to a certain point, once the bar is in view, the animation starts.
I used this jquery plugin https://www.jqueryscript.net/loading/jQuery-Plugin-SVG-Progress-Circle.html
function makesvg(percentage, inner_text = "") {
var abs_percentage = Math.abs(percentage).toString();
var percentage_str = percentage.toString();
var classes = ""
if (percentage < 0) {
classes = "danger-stroke circle-chart__circle--negative";
} else if (percentage > 0 && percentage <= 30) {
classes = "warning-stroke";
} else {
classes = "success-stroke";
}
var svg = '<svg class="circle-chart" viewbox="0 0 33.83098862 33.83098862"
xmlns = "http://www.w3.org/2000/svg" > ' +
'<circle class="circle-chart__background" cx="16.9" cy="16.9" r="15.9" / >
' +
'<circle class="circle-chart__circle ' + classes + '"' +
'stroke-dasharray="' + abs_percentage + ',100" cx="16.9" cy="16.9"
r = "15.9" / > ' +
'<g class="circle-chart__info">' +
' <text style="color:#fff;" class="circle-chart__percent" x="17.9"
y = "15.5" > '+percentage_str+' % < /text>';
if (inner_text) {
svg += '<text class="circle-chart__subline" x="16.91549431"
y = "22" > '+inner_text+' < /text>'
}
svg += ' </g></svg>';
return svg
}
(function($) {
$.fn.circlechart = function() {
this.each(function() {
var percentage = $(this).data("percentage");
var inner_text = $(this).text();
var inner_text2 = $(this).text();
$(this).html(makesvg(percentage, inner_text));
});
return this;
};
});
$('.circlechart').circlechart(); // Initialization

Checkout answer of Dan
With reference to answer of Dan, I am adding code that you can use to start/stop animation of circular progress bar
function onVisibilityChange(el, callback) {
var old_visible;
return function () {
var visible = isElementInViewport(el);
if (visible != old_visible) {
old_visible = visible;
if (typeof callback == 'function') {
callback(visible);
}
}
}
}
var percentage = 0;
var handler = onVisibilityChange(el, function(visiblity_status) {
/* your code go here */
if (visibility_status) {
if (percentage == 0) {
$('.circlechart').circlechart();
} else {
// Code to resume progress bar if there is any method defined for the plugin you are using.
}
} else {
// Code to stop progress bar if there is any method to stop it.
}
});
//jQuery
$(window).on('DOMContentLoaded load resize scroll', handler);

Related

How to check with d3.js if element is in viewpoint (in visible area)

I'm drawing large number of elements and in many situations majority of elements are outside of view point.
I'd like to avoid processing expensive rotation transformations on hidden elements.
Here's an example:
https://blockchaingraph.org/#ipfs-QmfXtMeUdjWBPQHUNKvF3nkYR57aZz7qarW5qikEUYWJvw
Many elements in this graph are hidden (try to zoom out to see). But currently I have to render each element on every tick and it's getting painfully slow.
Here's my code:
function transformLinks(svgLinks, nodeRadius, arrowSize) {
if (svgLinks) {
var link = svgLinks.selectAll('.link').filter(needRedraw);
//console.log("total:", svgLinks.selectAll('.link').size(), ',needRedraw:', link.size());
transformLinksLines(link);
transformLinksTexts(link.selectAll('.text'));
transformLinksOutlines(link, nodeRadius, arrowSize);
transformLinksOverlays(link.selectAll('.overlay'));
link.each(function (n) {
n.source.lx = n.source.x;
n.source.ly = n.source.y;
n.target.lx = n.target.x;
n.target.ly = n.target.y;
});
}
}
function needRedraw(link) {
if (!link.source) {
link = link.parentNode;
}
return nodeMoved(link.source) || nodeMoved(link.target);
}
var minDistToRedraw = 0.8;
function nodeMoved(n) {
return utils.isNumber(n.x) && utils.isNumber(n.y)
&& !(utils.isNumber(n.lx) && Math.abs(n.x - n.lx) <= minDistToRedraw && Math.abs(n.y - n.ly) <= minDistToRedraw);
}
I'd like to extend needRedraw() function to check for visibility. For now the function just checks if either linked node moved significantly enough.
Since I didn't find any out of box solution I had to get into those coordinate conversion stuff.
First, I created function that translates external container coordinates into SVG internal clients coordianate system - containerToSVG()
Then applied it on .getBoundingClientRect(); to get visible area in SVG coordinate space.
Then in the filter checking if both nodes outsize of visible area - do not redraw link.
There are possible situations when both nodes are outsize the area, but link can still cross the area. But it's not a big concern as long as user don't see link detachments.
Here's the code:
function transformLinks(svgLinks, nodeRadius, arrowSize) {
if (svgLinks) {
var containerRect = container.node().getBoundingClientRect();
var p = containerToSVG(-nodeRadius, -nodeRadius);
var r = containerToSVG(containerRect.width + nodeRadius, containerRect.height + nodeRadius);
svgVisibleRect = {left: p.x, top: p.y, right: r.x, bottom: r.y};
minDistToRedraw = (svgVisibleRect.right - svgVisibleRect.left) / (containerRect.width + nodeRadius * 2);
var link = svgLinks.selectAll('.link').filter(needRedraw);
transformLinksLines(link);
transformLinksTexts(link.selectAll('.text'));
transformLinksOutlines(link, nodeRadius, arrowSize);
transformLinksOverlays(link.selectAll('.overlay'));
link.each(function (n) {
updateLastCoord(n.source);
updateLastCoord(n.target);
});
}
}
function needRedraw(link) {
if (!nodeMoved(link.source) && !nodeMoved(link.target)) {
return false;
}
return isVisible(link.source) || isVisible(link.target);
}
function nodeMoved(n) {
return utils.isNumber(n.x) && utils.isNumber(n.y) &&
!(utils.isNumber(n.lx) && Math.abs(n.x - n.lx) <= minDistToRedraw && Math.abs(n.y - n.ly) <= minDistToRedraw);
}
function isVisible(n) {
var result = n.x > svgVisibleRect.left && n.x < svgVisibleRect.right &&
n.y > svgVisibleRect.top && n.y < svgVisibleRect.bottom;
return result;
}
function updateLastCoord(n) {
n.lx = n.x;
n.ly = n.y;
}
function containerToSVG(containerX, containerY) {
var svgPount = svgNode.createSVGPoint();
svgPount.x = containerX;
svgPount.y = containerY;
return svgPount.matrixTransform(document.getElementById("links-svg").getScreenCTM().inverse());
}
function transformLinksLines(link) {
link.attr('transform', function (d) {
var angle = rotation(d.source, d.target);
return 'translate(' + d.source.x + ', ' + d.source.y + ') rotate(' + angle + ')';
});
}

Extending Touch EventListener to Additional DOM Element

I used a Codrops article/experiment to create an interactive environment for a local group to use at their conferences. The problem with this is the default interaction is not very intuitive. The template used Flickity.js and what seems like classie.js to create this sliding interface I am having trouble with.
The page can be found here:
www.eyeconic.tv/ky-ffa/
Issue: The only way to activate the view-full is by clicking on the html element:
<h2 class=".stack-title">
// After the stack is active you should be able to activate the full view by clicking on the first .stack-item used to create the thumbnail below it. This entire div should be clickable. Users are touching everywhere all over the screen and not actually clicking the title for the desired action. I hope this makes sense.
In other words you should be able to click the stack-title and the image below the title of each stack to pull the stack into the full view mode on the screen. Then click the x or anywhere else on the screen to close the full view.
The following is located in main.js and the reference I found to create the events I am referring to.
//
function initEvents() {
stacks.forEach(function(stack) {
var titleEl = stack.querySelector('.stack-title');
// expand/close the stack
titleEl.addEventListener('click', function(ev) {
ev.preventDefault();
if( classie.has(stack, 'is-selected') ) { // current stack
if( classie.has(bodyEl, 'view-full') ) { // stack is opened
var closeStack = function() {
classie.remove(bodyEl, 'move-items');
onEndTransition(slider, function() {
classie.remove(bodyEl, 'view-full');
bodyEl.style.height = '';
flkty.bindDrag();
flkty.options.accessibility = true;
canMoveHeroImage = true;
});
};
// if the user scrolled down, let's first scroll all up before closing the stack.
var scrolled = scrollY();
if( scrolled > 0 ) {
smooth_scroll_to(isFirefox ? docElem : bodyEl || docElem, 0, 500).then(function() {
closeStack();
});
}
else {
closeStack();
}
}
else if( canOpen ) { // stack is closed
canMoveHeroImage = false;
classie.add(bodyEl, 'view-full');
setTimeout(function() { classie.add(bodyEl, 'move-items'); }, 25);
bodyEl.style.height = stack.offsetHeight + 'px';
flkty.unbindDrag();
flkty.options.accessibility = false;
}
}
else if( classie.has(stack, 'stack-prev') ) {
flkty.previous(true);
}
else if( classie.has(stack, 'stack-next') ) {
flkty.next(true);
}
});
titleEl.addEventListener('mouseenter', function(ev) {
if( classie.has(stack, 'is-selected') ) {
canMoveHeroImage = false;
imghero.style.WebkitTransform = 'perspective(1000px) translate3d(0,0,0) rotate3d(1,1,1,0deg)';
imghero.style.transform = 'perspective(1000px) translate3d(0,0,0) rotate3d(1,1,1,0deg)';
}
});
titleEl.addEventListener('mouseleave', function(ev) {
// if current stack and it's not opened..
if( classie.has(stack, 'is-selected') && !classie.has(bodyEl, 'view-full') ) {
canMoveHeroImage = true;
}
});
});
window.addEventListener('mousemove', throttle(function(ev) {
if( !canMoveHeroImage ) return false;
var xVal = -1/(win.height/2)*ev.clientY + 1,
yVal = 1/(win.width/2)*ev.clientX - 1,
transX = 20/(win.width)*ev.clientX - 10,
transY = 20/(win.height)*ev.clientY - 10,
transZ = 100/(win.height)*ev.clientY - 50;
imghero.style.WebkitTransform = 'perspective(1000px) translate3d(' + transX + 'px,' + transY + 'px,' + transZ + 'px) rotate3d(' + xVal + ',' + yVal + ',0,2deg)';
imghero.style.transform = 'perspective(1000px) translate3d(' + transX + 'px,' + transY + 'px,' + transZ + 'px) rotate3d(' + xVal + ',' + yVal + ',0,2deg)';
}, 100));
// window resize
window.addEventListener( 'resize', throttle(function(ev) {
// recalculate window width/height
win = { width: window.innerWidth, height: window.innerHeight };
// reset body height if stack is opened
if( classie.has(bodyEl, 'view-full') ) { // stack is opened
bodyEl.style.height = stacks[flkty.selectedIndex].offsetHeight + 'px';
}
}, 50));
// Flickity events:
flkty.on('cellSelect', function() {
canOpen = false;
classie.remove(bodyEl, 'item-clickable');
var prevStack = stacksWrapper.querySelector('.stack-prev'),
nextStack = stacksWrapper.querySelector('.stack-next'),
selidx = flkty.selectedIndex,
cellsCount = flkty.cells.length,
previdx = selidx > 0 ? selidx - 1 : cellsCount - 1;
nextidx = selidx < cellsCount - 1 ? selidx + 1 : 0;
if( prevStack ) {
classie.remove(prevStack, 'stack-prev');
}
if( nextStack ) {
classie.remove(nextStack, 'stack-next');
}
classie.add(stacks[previdx], 'stack-prev');
classie.add(stacks[nextidx], 'stack-next');
});
flkty.on('dragStart', function() {
canOpen = false;
classie.remove(bodyEl, 'item-clickable');
});
flkty.on('settle', function() {
classie.add(bodyEl, 'item-clickable');
canOpen = true;
});
}
init();
})();
I wrapped the title and the first stack item in a div class .touch-me and it worked fairly well. I had previously tried to do this and received an error. But I may have mistyped something because it only made sense.
ISSUE: It works on mouseclick, but it is not working with touch on windows. I have untested it in any other environment because it will be deployed on a windows touch screen.
Although I cannot tell the layer not to close on touch when you swipe or touch the header image for the stack.... I'm afraid I do not have the skillset to properly modify the logic in the javascript since I do not entirely understand the plugins being used.

Building css properties via javascript events

I'm trying to display an arrow which guides the user to the top of the page on click:
html code
<a id="top" href="#"><img src="/img/arrow.png"></a>
css code
.scroll #top{
opacity: .2;
}
js code
o = $;
// misc junk
o(function(){
var width = window.innerWidth;
var height = window.innerHeight;
var doc = o(document);
// .onload
o('html').addClass('onload');
// top link
o('#top').click(function(e){
o('body').animate({ scrollTop: 0 }, 'fast');
e.preventDefault();
});
// scrolling links
var added;
doc.scroll(function(e){
if (doc.scrollTop() > 5) {
if (added) return;
added = true;
o('body').addClass('scroll');
} else {
o('body').removeClass('scroll');
added = false;
}
})
// highlight code
o('pre.js code').each(function(){
o(this).html(highlight(o(this).text()));
})
})
// active menu junk
o(function(){
var prev;
var n = 0;
var headings = o('h3').map(function(i, el){
return {
top: o(el).offset().top,
id: el.id
}
});
function closest() {
var h;
var top = o(window).scrollTop();
var i = headings.length;
while (i--) {
h = headings[i];
if (top >= h.top - 1) return h;
}
}
o(document).scroll(function(){
var h = closest();
if (!h) return;
if (prev) {
prev.removeClass('active');
prev.parent().parent().removeClass('active');
}
var a = o('a[href="#' + h.id + '"]');
a.addClass('active');
a.parent().parent().addClass('active');
prev = a;
})
})
/**
* Highlight the given `js`.
*/
function highlight(js) {
return js
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
.replace(/('.*?')/gm, '<span class="string">$1</span>')
.replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
.replace(/(\d+)/gm, '<span class="number">$1</span>')
.replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
.replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
}
Setting this in place, wouldn't makes appear the arrow on scroll of page. How to fix this, please ?

jQuery animation works with only one element

I need to make it working for all my elements under Which nothing should fade (images).
Currently works only for the div.logo tag
I guess at the moment the reason mine <h1> element is now allowing animations underneath is that when I am getting the .top(), .left() and so on, you are doing it for the last element in the list.
I need to get the borders of each element in the resulting list.
Any help would be much appreciated
Demo: http://jsfiddle.net/z9b8S/
JS:
function displayThese(selectorString) {
var $heading = $(selectorString);
var h1top = $heading.position().top;
var h1bottom = h1top + $heading.height();
var h1left = $heading.position().left;
var h1right = h1top + $heading.width();
var divs = $('li').filter(function () {
var $e = $(this);
var top = $e.position().top;
var bottom = top + $e.height();
var left = $e.position().left;
var right = left + $e.width();
return top > h1bottom || bottom < h1top || left > h1right || right < h1left;
});
return divs;
}
(function fadeInDiv() {
var divsToChange = displayThese('h1, div.logo');
var elem = divsToChange.eq(Math.floor(Math.random() * divsToChange.length));
if (!elem.is(':visible')) {
elem.prev().remove();
elem.animate({
opacity: 1
}, Math.floor(Math.random() * 1000), fadeInDiv);
} else {
elem.animate({
opacity: (Math.random() * 1)
}, Math.floor(Math.random() * 1000), function () {
window.setTimeout(fadeInDiv);
});
}
})();
$(window).resize(function () {
// Get items that do not change
var divs = $('li').not(displayThese());
divs.css({
opacity: 0.3
});
});

JQuery menu float and display submenus on page

This is my first time using JQuery in any of my projects.
I have implemented the superfish menu.
On some of my pages I have a horizontal scroll. I would like to make the menu float on the center of the page as the page is scrolled.
Also I need to make sure that the submenu on the far right hand side of the menu does not open up off the page. When I hover on the right most element it opens up half off the page.
Any ideas on how to fix these two things?
I'm perfectly willing to use a different Jquery menu if there is a better one that has these features built in...
Thanks!
javascrupt call in my page:
$(document).ready(function () {
$("ul.sf-menu").supersubs({
minWidth: 12, // minimum width of sub-menus in em units
maxWidth: 27, // maximum width of sub-menus in em units
extraWidth: 1 // extra width can ensure lines don't sometimes turn over
// due to slight rounding differences and font-family
}).superfish({ animation: { opacity: 'show', height: 'show' }, autoArrows: false }); // call supersubs first, then superfish, so that subs are
// not display:none when measuring. Call before initialising
// containing tabs for same reason.
I can post any more code that is needed, but there is quite a lot of code in the superfish files so i'm not sure what I should post.
I found this script and it works well, however when I scroll right the horizonal menu starts to stack so the menu items are side by side rather then vertical. I want to modify this to keep the menu horizonal...
<script type="text/javascript"><!--
var floatingMenuId = 'floatdiv';
var floatingMenu =
{
targetX: -1000,
targetY: 10,
hasInner: typeof (window.innerWidth) == 'number',
hasElement: document.documentElement
&& document.documentElement.clientWidth,
menu:
document.getElementById
? document.getElementById(floatingMenuId)
: document.all
? document.all[floatingMenuId]
: document.layers[floatingMenuId]
};
floatingMenu.move = function () {
if (document.layers) {
floatingMenu.menu.left = floatingMenu.nextX;
floatingMenu.menu.top = floatingMenu.nextY;
}
else {
floatingMenu.menu.style.left = floatingMenu.nextX + 'px';
floatingMenu.menu.style.top = floatingMenu.nextY + 'px';
}
}
floatingMenu.computeShifts = function () {
var de = document.documentElement;
floatingMenu.shiftX =
floatingMenu.hasInner
? pageXOffset
: floatingMenu.hasElement
? de.scrollLeft
: document.body.scrollLeft;
if (floatingMenu.targetX < 0) {
if (floatingMenu.hasElement && floatingMenu.hasInner) {
// Handle Opera 8 problems
floatingMenu.shiftX +=
de.clientWidth > window.innerWidth
? window.innerWidth
: de.clientWidth
}
else {
floatingMenu.shiftX +=
floatingMenu.hasElement
? de.clientWidth
: floatingMenu.hasInner
? window.innerWidth
: document.body.clientWidth;
}
}
floatingMenu.shiftY =
floatingMenu.hasInner
? pageYOffset
: floatingMenu.hasElement
? de.scrollTop
: document.body.scrollTop;
if (floatingMenu.targetY < 0) {
if (floatingMenu.hasElement && floatingMenu.hasInner) {
// Handle Opera 8 problems
floatingMenu.shiftY +=
de.clientHeight > window.innerHeight
? window.innerHeight
: de.clientHeight
}
else {
floatingMenu.shiftY +=
floatingMenu.hasElement
? document.documentElement.clientHeight
: floatingMenu.hasInner
? window.innerHeight
: document.body.clientHeight;
}
}
}
floatingMenu.doFloat = function () {
var stepX, stepY;
floatingMenu.computeShifts();
stepX = (floatingMenu.shiftX +
floatingMenu.targetX - floatingMenu.nextX) * .07;
if (Math.abs(stepX) < .5) {
stepX = floatingMenu.shiftX +
floatingMenu.targetX - floatingMenu.nextX;
}
stepY = (floatingMenu.shiftY +
floatingMenu.targetY - floatingMenu.nextY) * .07;
if (Math.abs(stepY) < .5) {
stepY = floatingMenu.shiftY +
floatingMenu.targetY - floatingMenu.nextY;
}
if (Math.abs(stepX) > 0 ||
Math.abs(stepY) > 0) {
floatingMenu.nextX += stepX;
floatingMenu.nextY += stepY;
floatingMenu.move();
}
setTimeout('floatingMenu.doFloat()', 20);
};
// addEvent designed by Aaron Moore
floatingMenu.addEvent = function (element, listener, handler) {
if (typeof element[listener] != 'function' ||
typeof element[listener + '_num'] == 'undefined') {
element[listener + '_num'] = 0;
if (typeof element[listener] == 'function') {
element[listener + 0] = element[listener];
element[listener + '_num']++;
}
element[listener] = function (e) {
var r = true;
e = (e) ? e : window.event;
for (var i = element[listener + '_num'] - 1; i >= 0; i--) {
if (element[listener + i](e) == false)
r = false;
}
return r;
}
}
//if handler is not already stored, assign it
for (var i = 0; i < element[listener + '_num']; i++)
if (element[listener + i] == handler)
return;
element[listener + element[listener + '_num']] = handler;
element[listener + '_num']++;
};
floatingMenu.init = function () {
floatingMenu.initSecondary();
floatingMenu.doFloat();
};
// Some browsers init scrollbars only after
// full document load.
floatingMenu.initSecondary = function () {
floatingMenu.computeShifts();
floatingMenu.nextX = floatingMenu.shiftX +
floatingMenu.targetX;
floatingMenu.nextY = floatingMenu.shiftY +
floatingMenu.targetY;
floatingMenu.move();
}
if (document.layers)
floatingMenu.addEvent(window, 'onload', floatingMenu.init);
else {
floatingMenu.init();
floatingMenu.addEvent(window, 'onload',
floatingMenu.initSecondary);
}
</script>
I'm not sure on how you mean centering, but if you mean horizontally centered:
Could you separate the main page (that horizontally overflows) and the menu into separate div's? e.g.
<div id="menu"><center><ul class="sf-menu">...</ul></center></div>
<div id="mainpage" style="overflow:auto;">Contents goes here</div>
(the <center> tag might have to be <div style="width:X;margin:0 auto;"> depending on how superfish works)
On the menu going over the page, sorry I'll have to defer to someone more knowable to answer that.

Categories