summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEltrac Koalar <hi@guhub.cn>2022-08-17 00:56:36 +0800
committerEltrac Koalar <hi@guhub.cn>2022-08-17 00:56:36 +0800
commitf2b9fa08f78cfdc0a8359fd000e681cb7ef7fe13 (patch)
tree0a13049ba21a65113eb0a5d64f439ad320148f2c
parent1c0d5437f8397ca08a0057a614bd27a4e6ac4888 (diff)
:dizzy: Add: 使用 SmoothScroll.js 实现平滑滚动
-rw-r--r--README.md8
-rw-r--r--assets/script.js11
-rw-r--r--assets/smoothscroll.js790
-rw-r--r--includes/footer.php1
4 files changed, 800 insertions, 10 deletions
diff --git a/README.md b/README.md
index 2e9ff3b..c86dcd4 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ Matcha 是基于 [Ringo](https://github.com/memset0/typecho-theme-ringo) 二次å
- 增加了 Pjax 无刷新功能
- 使用 Pangu.js 在中英文之间自动用空格分隔
- 优化了文章内容的样式
+- 页面滚动更加平滑
- 动画等细节上的调整
具体的外观可以访问[我的博客](https://blog.guhub.cn/)查看。
@@ -38,15 +39,10 @@ Enjoy~
## 计划
-- [ ] 平滑滚动
+- [x] 平滑滚动
- [x] 优化页面滚动条的样式
- [ ] 重写评论板块的样式
- [ ] ajax 评论无刷新
-- [ ] 方便编辑文章的短代码
- - 下拉框(`<details>`)
- - 强调框
- - 注音
- - ...
---
diff --git a/assets/script.js b/assets/script.js
index 934030e..a00fe02 100644
--- a/assets/script.js
+++ b/assets/script.js
@@ -71,8 +71,12 @@ $(window).scroll(function() {
*/
//Must load when page finish
-hljs.highlightAll();
-panguLoad();
+JSLoad = function(){
+ hljs.highlightAll();
+ panguLoad();
+ SmoothScroll();
+}
+JSLoad();
//Load Pjax
$(document).pjax('a[href^="' + siteurl + '"]:not(a[target="_blank"], a[no-pjax])', {
@@ -85,8 +89,7 @@ $(document).pjax('a[href^="' + siteurl + '"]:not(a[target="_blank"], a[no-pjax])
NProgress.start();
}).on('pjax:complete', function() {
$("#main").addClass("fadein").hide().fadeIn(300);
- hljs.highlightAll();
- panguLoad();
+ JSLoad();
//加载条完成
NProgress.done();
}) \ No newline at end of file
diff --git a/assets/smoothscroll.js b/assets/smoothscroll.js
new file mode 100644
index 0000000..03cfb09
--- /dev/null
+++ b/assets/smoothscroll.js
@@ -0,0 +1,790 @@
+//
+// SmoothScroll for websites v1.4.10 (Balazs Galambosi)
+// http://www.smoothscroll.net/
+//
+// Licensed under the terms of the MIT license.
+//
+// You may use it in your theme if you credit me.
+// It is also free to use on any individual website.
+//
+// Exception:
+// The only restriction is to not publish any
+// extension for browsers or native application
+// without getting a written permission first.
+//
+
+(function () {
+
+ // Scroll Variables (tweakable)
+ var defaultOptions = {
+
+ // Scrolling Core
+ frameRate : 150, // [Hz]
+ animationTime : 400, // [ms]
+ stepSize : 100, // [px]
+
+ // Pulse (less tweakable)
+ // ratio of "tail" to "acceleration"
+ pulseAlgorithm : true,
+ pulseScale : 4,
+ pulseNormalize : 1,
+
+ // Acceleration
+ accelerationDelta : 50, // 50
+ accelerationMax : 3, // 3
+
+ // Keyboard Settings
+ keyboardSupport : true, // option
+ arrowScroll : 50, // [px]
+
+ // Other
+ fixedBackground : true,
+ excluded : ''
+ };
+
+ var options = defaultOptions;
+
+
+ // Other Variables
+ var isExcluded = false;
+ var isFrame = false;
+ var direction = { x: 0, y: 0 };
+ var initDone = false;
+ var root = document.documentElement;
+ var activeElement;
+ var observer;
+ var refreshSize;
+ var deltaBuffer = [];
+ var deltaBufferTimer;
+ var isMac = /^Mac/.test(navigator.platform);
+
+ var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32,
+ pageup: 33, pagedown: 34, end: 35, home: 36 };
+ var arrowKeys = { 37: 1, 38: 1, 39: 1, 40: 1 };
+
+ /***********************************************
+ * INITIALIZE
+ ***********************************************/
+
+ /**
+ * Tests if smooth scrolling is allowed. Shuts down everything if not.
+ */
+ function initTest() {
+ if (options.keyboardSupport) {
+ addEvent('keydown', keydown);
+ }
+ }
+
+ /**
+ * Sets up scrolls array, determines if frames are involved.
+ */
+ function init() {
+
+ if (initDone || !document.body) return;
+
+ initDone = true;
+
+ var body = document.body;
+ var html = document.documentElement;
+ var windowHeight = window.innerHeight;
+ var scrollHeight = body.scrollHeight;
+
+ // check compat mode for root element
+ root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
+ activeElement = body;
+
+ initTest();
+
+ // Checks if this script is running in a frame
+ if (top != self) {
+ isFrame = true;
+ }
+
+ /**
+ * Safari 10 fixed it, Chrome fixed it in v45:
+ * This fixes a bug where the areas left and right to
+ * the content does not trigger the onmousewheel event
+ * on some pages. e.g.: html, body { height: 100% }
+ */
+ else if (isOldSafari &&
+ scrollHeight > windowHeight &&
+ (body.offsetHeight <= windowHeight ||
+ html.offsetHeight <= windowHeight)) {
+
+ var fullPageElem = document.createElement('div');
+ fullPageElem.style.cssText = 'position:absolute; z-index:-10000; ' +
+ 'top:0; left:0; right:0; height:' +
+ root.scrollHeight + 'px';
+ document.body.appendChild(fullPageElem);
+
+ // DOM changed (throttled) to fix height
+ var pendingRefresh;
+ refreshSize = function () {
+ if (pendingRefresh) return; // could also be: clearTimeout(pendingRefresh);
+ pendingRefresh = setTimeout(function () {
+ if (isExcluded) return; // could be running after cleanup
+ fullPageElem.style.height = '0';
+ fullPageElem.style.height = root.scrollHeight + 'px';
+ pendingRefresh = null;
+ }, 500); // act rarely to stay fast
+ };
+
+ setTimeout(refreshSize, 10);
+
+ addEvent('resize', refreshSize);
+
+ // TODO: attributeFilter?
+ var config = {
+ attributes: true,
+ childList: true,
+ characterData: false
+ // subtree: true
+ };
+
+ observer = new MutationObserver(refreshSize);
+ observer.observe(body, config);
+
+ if (root.offsetHeight <= windowHeight) {
+ var clearfix = document.createElement('div');
+ clearfix.style.clear = 'both';
+ body.appendChild(clearfix);
+ }
+ }
+
+ // disable fixed background
+ if (!options.fixedBackground && !isExcluded) {
+ body.style.backgroundAttachment = 'scroll';
+ html.style.backgroundAttachment = 'scroll';
+ }
+ }
+
+ /**
+ * Removes event listeners and other traces left on the page.
+ */
+ function cleanup() {
+ observer && observer.disconnect();
+ removeEvent(wheelEvent, wheel);
+ removeEvent('mousedown', mousedown);
+ removeEvent('keydown', keydown);
+ removeEvent('resize', refreshSize);
+ removeEvent('load', init);
+ }
+
+
+ /************************************************
+ * SCROLLING
+ ************************************************/
+
+ var que = [];
+ var pending = false;
+ var lastScroll = Date.now();
+
+ /**
+ * Pushes scroll actions to the scrolling queue.
+ */
+ function scrollArray(elem, left, top) {
+
+ directionCheck(left, top);
+
+ if (options.accelerationMax != 1) {
+ var now = Date.now();
+ var elapsed = now - lastScroll;
+ if (elapsed < options.accelerationDelta) {
+ var factor = (1 + (50 / elapsed)) / 2;
+ if (factor > 1) {
+ factor = Math.min(factor, options.accelerationMax);
+ left *= factor;
+ top *= factor;
+ }
+ }
+ lastScroll = Date.now();
+ }
+
+ // push a scroll command
+ que.push({
+ x: left,
+ y: top,
+ lastX: (left < 0) ? 0.99 : -0.99,
+ lastY: (top < 0) ? 0.99 : -0.99,
+ start: Date.now()
+ });
+
+ // don't act if there's a pending queue
+ if (pending) {
+ return;
+ }
+
+ var scrollRoot = getScrollRoot();
+ var isWindowScroll = (elem === scrollRoot || elem === document.body);
+
+ // if we haven't already fixed the behavior,
+ // and it needs fixing for this sesh
+ if (elem.$scrollBehavior == null && isScrollBehaviorSmooth(elem)) {
+ elem.$scrollBehavior = elem.style.scrollBehavior;
+ elem.style.scrollBehavior = 'auto';
+ }
+
+ var step = function (time) {
+
+ var now = Date.now();
+ var scrollX = 0;
+ var scrollY = 0;
+
+ for (var i = 0; i < que.length; i++) {
+
+ var item = que[i];
+ var elapsed = now - item.start;
+ var finished = (elapsed >= options.animationTime);
+
+ // scroll position: [0, 1]
+ var position = (finished) ? 1 : elapsed / options.animationTime;
+
+ // easing [optional]
+ if (options.pulseAlgorithm) {
+ position = pulse(position);
+ }
+
+ // only need the difference
+ var x = (item.x * position - item.lastX) >> 0;
+ var y = (item.y * position - item.lastY) >> 0;
+
+ // add this to the total scrolling
+ scrollX += x;
+ scrollY += y;
+
+ // update last values
+ item.lastX += x;
+ item.lastY += y;
+
+ // delete and step back if it's over
+ if (finished) {
+ que.splice(i, 1); i--;
+ }
+ }
+
+ // scroll left and top
+ if (isWindowScroll) {
+ window.scrollBy(scrollX, scrollY);
+ }
+ else {
+ if (scrollX) elem.scrollLeft += scrollX;
+ if (scrollY) elem.scrollTop += scrollY;
+ }
+
+ // clean up if there's nothing left to do
+ if (!left && !top) {
+ que = [];
+ }
+
+ if (que.length) {
+ requestFrame(step, elem, (1000 / options.frameRate + 1));
+ } else {
+ pending = false;
+ // restore default behavior at the end of scrolling sesh
+ if (elem.$scrollBehavior != null) {
+ elem.style.scrollBehavior = elem.$scrollBehavior;
+ elem.$scrollBehavior = null;
+ }
+ }
+ };
+
+ // start a new queue of actions
+ requestFrame(step, elem, 0);
+ pending = true;
+ }
+
+
+ /***********************************************
+ * EVENTS
+ ***********************************************/
+
+ /**
+ * Mouse wheel handler.
+ * @param {Object} event
+ */
+ function wheel(event) {
+
+ if (!initDone) {
+ init();
+ }
+
+ var target = event.target;
+
+ // leave early if default action is prevented
+ // or it's a zooming event with CTRL
+ if (event.defaultPrevented || event.ctrlKey) {
+ return true;
+ }
+
+ // leave embedded content alone (flash & pdf)
+ if (isNodeName(activeElement, 'embed') ||
+ (isNodeName(target, 'embed') && /\.pdf/i.test(target.src)) ||
+ isNodeName(activeElement, 'object') ||
+ target.shadowRoot) {
+ return true;
+ }
+
+ var deltaX = -event.wheelDeltaX || event.deltaX || 0;
+ var deltaY = -event.wheelDeltaY || event.deltaY || 0;
+
+ if (isMac) {
+ if (event.wheelDeltaX && isDivisible(event.wheelDeltaX, 120)) {
+ deltaX = -120 * (event.wheelDeltaX / Math.abs(event.wheelDeltaX));
+ }
+ if (event.wheelDeltaY && isDivisible(event.wheelDeltaY, 120)) {
+ deltaY = -120 * (event.wheelDeltaY / Math.abs(event.wheelDeltaY));
+ }
+ }
+
+ // use wheelDelta if deltaX/Y is not available
+ if (!deltaX && !deltaY) {
+ deltaY = -event.wheelDelta || 0;
+ }
+
+ // line based scrolling (Firefox mostly)
+ if (event.deltaMode === 1) {
+ deltaX *= 40;
+ deltaY *= 40;
+ }
+
+ var overflowing = overflowingAncestor(target);
+
+ // nothing to do if there's no element that's scrollable
+ if (!overflowing) {
+ // except Chrome iframes seem to eat wheel events, which we need to
+ // propagate up, if the iframe has nothing overflowing to scroll
+ if (isFrame && isChrome) {
+ // change target to iframe element itself for the parent frame
+ Object.defineProperty(event, "target", {value: window.frameElement});
+ return parent.wheel(event);
+ }
+ return true;
+ }
+
+ // check if it's a touchpad scroll that should be ignored
+ if (isTouchpad(deltaY)) {
+ return true;
+ }
+
+ // scale by step size
+ // delta is 120 most of the time
+ // synaptics seems to send 1 sometimes
+ if (Math.abs(deltaX) > 1.2) {
+ deltaX *= options.stepSize / 120;
+ }
+ if (Math.abs(deltaY) > 1.2) {
+ deltaY *= options.stepSize / 120;
+ }
+
+ scrollArray(overflowing, deltaX, deltaY);
+ event.preventDefault();
+ scheduleClearCache();
+ }
+
+ /**
+ * Keydown event handler.
+ * @param {Object} event
+ */
+ function keydown(event) {
+
+ var target = event.target;
+ var modifier = event.ctrlKey || event.altKey || event.metaKey ||
+ (event.shiftKey && event.keyCode !== key.spacebar);
+
+ // our own tracked active element could've been removed from the DOM
+ if (!document.body.contains(activeElement)) {
+ activeElement = document.activeElement;
+ }
+
+ // do nothing if user is editing text
+ // or using a modifier key (except shift)
+ // or in a dropdown
+ // or inside interactive elements
+ var inputNodeNames = /^(textarea|select|embed|object)$/i;
+ var buttonTypes = /^(button|submit|radio|checkbox|file|color|image)$/i;
+ if ( event.defaultPrevented ||
+ inputNodeNames.test(target.nodeName) ||
+ isNodeName(target, 'input') && !buttonTypes.test(target.type) ||
+ isNodeName(activeElement, 'video') ||
+ isInsideYoutubeVideo(event) ||
+ target.isContentEditable ||
+ modifier ) {
+ return true;
+ }
+
+ // [spacebar] should trigger button press, leave it alone
+ if ((isNodeName(target, 'button') ||
+ isNodeName(target, 'input') && buttonTypes.test(target.type)) &&
+ event.keyCode === key.spacebar) {
+ return true;
+ }
+
+ // [arrwow keys] on radio buttons should be left alone
+ if (isNodeName(target, 'input') && target.type == 'radio' &&
+ arrowKeys[event.keyCode]) {
+ return true;
+ }
+
+ var shift, x = 0, y = 0;
+ var overflowing = overflowingAncestor(activeElement);
+
+ if (!overflowing) {
+ // Chrome iframes seem to eat key events, which we need to
+ // propagate up, if the iframe has nothing overflowing to scroll
+ return (isFrame && isChrome) ? parent.keydown(event) : true;
+ }
+
+ var clientHeight = overflowing.clientHeight;
+
+ if (overflowing == document.body) {
+ clientHeight = window.innerHeight;
+ }
+
+ switch (event.keyCode) {
+ case key.up:
+ y = -options.arrowScroll;
+ break;
+ case key.down:
+ y = options.arrowScroll;
+ break;
+ case key.spacebar: // (+ shift)
+ shift = event.shiftKey ? 1 : -1;
+ y = -shift * clientHeight * 0.9;
+ break;
+ case key.pageup:
+ y = -clientHeight * 0.9;
+ break;
+ case key.pagedown:
+ y = clientHeight * 0.9;
+ break;
+ case key.home:
+ if (overflowing == document.body && document.scrollingElement)
+ overflowing = document.scrollingElement;
+ y = -overflowing.scrollTop;
+ break;
+ case key.end:
+ var scroll = overflowing.scrollHeight - overflowing.scrollTop;
+ var scrollRemaining = scroll - clientHeight;
+ y = (scrollRemaining > 0) ? scrollRemaining + 10 : 0;
+ break;
+ case key.left:
+ x = -options.arrowScroll;
+ break;
+ case key.right:
+ x = options.arrowScroll;
+ break;
+ default:
+ return true; // a key we don't care about
+ }
+
+ scrollArray(overflowing, x, y);
+ event.preventDefault();
+ scheduleClearCache();
+ }
+
+ /**
+ * Mousedown event only for updating activeElement
+ */
+ function mousedown(event) {
+ activeElement = event.target;
+ }
+
+
+ /***********************************************
+ * OVERFLOW
+ ***********************************************/
+
+ var uniqueID = (function () {
+ var i = 0;
+ return function (el) {
+ return el.uniqueID || (el.uniqueID = i++);
+ };
+ })();
+
+ var cacheX = {}; // cleared out after a scrolling session
+ var cacheY = {}; // cleared out after a scrolling session
+ var clearCacheTimer;
+ var smoothBehaviorForElement = {};
+
+ //setInterval(function () { cache = {}; }, 10 * 1000);
+
+ function scheduleClearCache() {
+ clearTimeout(clearCacheTimer);
+ clearCacheTimer = setInterval(function () {
+ cacheX = cacheY = smoothBehaviorForElement = {};
+ }, 1*1000);
+ }
+
+ function setCache(elems, overflowing, x) {
+ var cache = x ? cacheX : cacheY;
+ for (var i = elems.length; i--;)
+ cache[uniqueID(elems[i])] = overflowing;
+ return overflowing;
+ }
+
+ function getCache(el, x) {
+ return (x ? cacheX : cacheY)[uniqueID(el)];
+ }
+
+ // (body) (root)
+ // | hidden | visible | scroll | auto |
+ // hidden | no | no | YES | YES |
+ // visible | no | YES | YES | YES |
+ // scroll | no | YES | YES | YES |
+ // auto | no | YES | YES | YES |
+
+ function overflowingAncestor(el) {
+ var elems = [];
+ var body = document.body;
+ var rootScrollHeight = root.scrollHeight;
+ do {
+ var cached = getCache(el, false);
+ if (cached) {
+ return setCache(elems, cached);
+ }
+ elems.push(el);
+ if (rootScrollHeight === el.scrollHeight) {
+ var topOverflowsNotHidden = overflowNotHidden(root) && overflowNotHidden(body);
+ var isOverflowCSS = topOverflowsNotHidden || overflowAutoOrScroll(root);
+ if (isFrame && isContentOverflowing(root) ||
+ !isFrame && isOverflowCSS) {
+ return setCache(elems, getScrollRoot());
+ }
+ } else if (isContentOverflowing(el) && overflowAutoOrScroll(el)) {
+ return setCache(elems, el);
+ }
+ } while ((el = el.parentElement));
+ }
+
+ function isContentOverflowing(el) {
+ return (el.clientHeight + 10 < el.scrollHeight);
+ }
+
+ // typically for <body> and <html>
+ function overflowNotHidden(el) {
+ var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
+ return (overflow !== 'hidden');
+ }
+
+ // for all other elements
+ function overflowAutoOrScroll(el) {
+ var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
+ return (overflow === 'scroll' || overflow === 'auto');
+ }
+
+ // for all other elements
+ function isScrollBehaviorSmooth(el) {
+ var id = uniqueID(el);
+ if (smoothBehaviorForElement[id] == null) {
+ var scrollBehavior = getComputedStyle(el, '')['scroll-behavior'];
+ smoothBehaviorForElement[id] = ('smooth' == scrollBehavior);
+ }
+ return smoothBehaviorForElement[id];
+ }
+
+
+ /***********************************************
+ * HELPERS
+ ***********************************************/
+
+ function addEvent(type, fn, arg) {
+ window.addEventListener(type, fn, arg || false);
+ }
+
+ function removeEvent(type, fn, arg) {
+ window.removeEventListener(type, fn, arg || false);
+ }
+
+ function isNodeName(el, tag) {
+ return el && (el.nodeName||'').toLowerCase() === tag.toLowerCase();
+ }
+
+ function directionCheck(x, y) {
+ x = (x > 0) ? 1 : -1;
+ y = (y > 0) ? 1 : -1;
+ if (direction.x !== x || direction.y !== y) {
+ direction.x = x;
+ direction.y = y;
+ que = [];
+ lastScroll = 0;
+ }
+ }
+
+ if (window.localStorage && localStorage.SS_deltaBuffer) {
+ try { // #46 Safari throws in private browsing for localStorage
+ deltaBuffer = localStorage.SS_deltaBuffer.split(',');
+ } catch (e) { }
+ }
+
+ function isTouchpad(deltaY) {
+ if (!deltaY) return;
+ if (!deltaBuffer.length) {
+ deltaBuffer = [deltaY, deltaY, deltaY];
+ }
+ deltaY = Math.abs(deltaY);
+ deltaBuffer.push(deltaY);
+ deltaBuffer.shift();
+ clearTimeout(deltaBufferTimer);
+ deltaBufferTimer = setTimeout(function () {
+ try { // #46 Safari throws in private browsing for localStorage
+ localStorage.SS_deltaBuffer = deltaBuffer.join(',');
+ } catch (e) { }
+ }, 1000);
+ var dpiScaledWheelDelta = deltaY > 120 && allDeltasDivisableBy(deltaY); // win64
+ var tp = !allDeltasDivisableBy(120) && !allDeltasDivisableBy(100) && !dpiScaledWheelDelta;
+ if (deltaY < 50) return true;
+ return tp;
+ }
+
+ function isDivisible(n, divisor) {
+ return (Math.floor(n / divisor) == n / divisor);
+ }
+
+ function allDeltasDivisableBy(divisor) {
+ return (isDivisible(deltaBuffer[0], divisor) &&
+ isDivisible(deltaBuffer[1], divisor) &&
+ isDivisible(deltaBuffer[2], divisor));
+ }
+
+ function isInsideYoutubeVideo(event) {
+ var elem = event.target;
+ var isControl = false;
+ if (document.URL.indexOf ('www.youtube.com/watch') != -1) {
+ do {
+ isControl = (elem.classList &&
+ elem.classList.contains('html5-video-controls'));
+ if (isControl) break;
+ } while ((elem = elem.parentNode));
+ }
+ return isControl;
+ }
+
+ var requestFrame = (function () {
+ return (window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ function (callback, element, delay) {
+ window.setTimeout(callback, delay || (1000/60));
+ });
+ })();
+
+ var MutationObserver = (window.MutationObserver ||
+ window.WebKitMutationObserver ||
+ window.MozMutationObserver);
+
+ var getScrollRoot = (function() {
+ var SCROLL_ROOT = document.scrollingElement;
+ return function() {
+ if (!SCROLL_ROOT) {
+ var dummy = document.createElement('div');
+ dummy.style.cssText = 'height:10000px;width:1px;';
+ document.body.appendChild(dummy);
+ var bodyScrollTop = document.body.scrollTop;
+ var docElScrollTop = document.documentElement.scrollTop;
+ window.scrollBy(0, 3);
+ if (document.body.scrollTop != bodyScrollTop)
+ (SCROLL_ROOT = document.body);
+ else
+ (SCROLL_ROOT = document.documentElement);
+ window.scrollBy(0, -3);
+ document.body.removeChild(dummy);
+ }
+ return SCROLL_ROOT;
+ };
+ })();
+
+
+ /***********************************************
+ * PULSE (by Michael Herf)
+ ***********************************************/
+
+ /**
+ * Viscous fluid with a pulse for part and decay for the rest.
+ * - Applies a fixed force over an interval (a damped acceleration), and
+ * - Lets the exponential bleed away the velocity over a longer interval
+ * - Michael Herf, http://stereopsis.com/stopping/
+ */
+ function pulse_(x) {
+ var val, start, expx;
+ // test
+ x = x * options.pulseScale;
+ if (x < 1) { // acceleartion
+ val = x - (1 - Math.exp(-x));
+ } else { // tail
+ // the previous animation ended here:
+ start = Math.exp(-1);
+ // simple viscous drag
+ x -= 1;
+ expx = 1 - Math.exp(-x);
+ val = start + (expx * (1 - start));
+ }
+ return val * options.pulseNormalize;
+ }
+
+ function pulse(x) {
+ if (x >= 1) return 1;
+ if (x <= 0) return 0;
+
+ if (options.pulseNormalize == 1) {
+ options.pulseNormalize /= pulse_(1);
+ }
+ return pulse_(x);
+ }
+
+
+ /***********************************************
+ * FIRST RUN
+ ***********************************************/
+
+ var userAgent = window.navigator.userAgent;
+ var isEdge = /Edge/.test(userAgent); // thank you MS
+ var isChrome = /chrome/i.test(userAgent) && !isEdge;
+ var isSafari = /safari/i.test(userAgent) && !isEdge;
+ var isMobile = /mobile/i.test(userAgent);
+ var isIEWin7 = /Windows NT 6.1/i.test(userAgent) && /rv:11/i.test(userAgent);
+ var isOldSafari = isSafari && (/Version\/8/i.test(userAgent) || /Version\/9/i.test(userAgent));
+ var isEnabledForBrowser = (isChrome || isSafari || isIEWin7) && !isMobile;
+
+ var supportsPassive = false;
+ try {
+ window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
+ get: function () {
+ supportsPassive = true;
+ }
+ }));
+ } catch(e) {}
+
+ var wheelOpt = supportsPassive ? { passive: false } : false;
+ var wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel';
+
+ if (wheelEvent && isEnabledForBrowser) {
+ addEvent(wheelEvent, wheel, wheelOpt);
+ addEvent('mousedown', mousedown);
+ addEvent('load', init);
+ }
+
+
+ /***********************************************
+ * PUBLIC INTERFACE
+ ***********************************************/
+
+ function SmoothScroll(optionsToSet) {
+ for (var key in optionsToSet)
+ if (defaultOptions.hasOwnProperty(key))
+ options[key] = optionsToSet[key];
+ }
+ SmoothScroll.destroy = cleanup;
+
+ if (window.SmoothScrollOptions) // async API
+ SmoothScroll(window.SmoothScrollOptions);
+
+ if (typeof define === 'function' && define.amd)
+ define(function() {
+ return SmoothScroll;
+ });
+ else if ('object' == typeof exports)
+ module.exports = SmoothScroll;
+ else
+ window.SmoothScroll = SmoothScroll;
+
+ })(); \ No newline at end of file
diff --git a/includes/footer.php b/includes/footer.php
index 5248d66..4911b42 100644
--- a/includes/footer.php
+++ b/includes/footer.php
@@ -55,6 +55,7 @@ MathJax.Hub.Config({
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.pjax/2.0.1/jquery.pjax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
<script src="<?php Utils::indexTheme('assets/pangu.js'); ?>"></script>
+<script src="<?php Utils::indexTheme('assets/smoothscroll.js'); ?>"></script>
<script>siteurl='<?php Utils::indexHome(); ?>';</script>
<script src="<?php Utils::indexTheme('assets/script.js'); ?>"></script>