PageSourceSearch

https://www.town-echizen.jp/js/SmoothScroll.js

js town-echizen.jp collected 2026-10-02 07:19:20 UTC 13,794 bytes, 522 lines download raw bytes

1// SmoothScroll for websites v1.2.1
2// Licensed under the terms of the MIT license.
3
4// People involved
5//  - Balazs Galambosi (maintainer)  
6//  - Michael Herf     (Pulse Algorithm)
7
8(function(){
9// Scroll Variables (tweakable)
10var defaultOptions = {
11
12    // Scrolling Core
13    frameRate        : 150, // [Hz]
14    animationTime    : 500, // [px]
15    stepSize         : 150, // [px]
16
17    // Pulse (less tweakable)
18    // ratio of "tail" to "acceleration"
19    pulseAlgorithm   : true,
20    pulseScale       : 6,
21    pulseNormalize   : 1,
22
23    // Acceleration
24    accelerationDelta : 20,  // 20
25    accelerationMax   : 1,   // 1
26
27    // Keyboard Settings
28    keyboardSupport   : true,  // option
29    arrowScroll       : 50,     // [px]
30
31    // Other
32    touchpadSupport   : true,
33    fixedBackground   : true, 
34    excluded          : ""    
35};
36
37var options = defaultOptions;
38
39
40// Other Variables
41var isExcluded = false;
42var isFrame = false;
43var direction = { x: 0, y: 0 };
44var initDone  = false;
45var root = document.documentElement;
46var activeElement;
47var observer;
48var deltaBuffer = [ 120, 120, 120 ];
49
50var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32, 
51            pageup: 33, pagedown: 34, end: 35, home: 36 };
52
53
54/***********************************************
55 * SETTINGS
56 ***********************************************/
57
58var options = defaultOptions;
59
60
61/***********************************************
62 * INITIALIZE
63 ***********************************************/
64
65/**
66 * Tests if smooth scrolling is allowed. Shuts down everything if not.
67 */
68function initTest() {
69
70    var disableKeyboard = false; 
71    
72    // disable keyboard support if anything above requested it
73    if (disableKeyboard) {
74        removeEvent("keydown", keydown);
75    }
76
77    if (options.keyboardSupport && !disableKeyboard) {
78        addEvent("keydown", keydown);
79    }
80}
81
82/**
83 * Sets up scrolls array, determines if frames are involved.
84 */
85function init() {
86  
87    if (!document.body) return;
88
89    var body = document.body;
90    var html = document.documentElement;
91    var windowHeight = window.innerHeight; 
92    var scrollHeight = body.scrollHeight;
93    
94    // check compat mode for root element
95    root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
96    activeElement = body;
97    
98    initTest();
99    initDone = true;
100
101    // Checks if this script is running in a frame
102    if (top != self) {
103        isFrame = true;
104    }
105
106    /**
107     * This fixes a bug where the areas left and right to 
108     * the content does not trigger the onmousewheel event
109     * on some pages. e.g.: html, body { height: 100% }
110     */
111    else if (scrollHeight > windowHeight &&
112            (body.offsetHeight <= windowHeight || 
113             html.offsetHeight <= windowHeight)) {
114
115        html.style.height = 'auto';
116        //setTimeout(refresh, 10);
117
118        // clearfix
119        if (root.offsetHeight <= windowHeight) {
120            var underlay = document.createElement("div"); 	
121            underlay.style.clear = "both";
122            body.appendChild(underlay);
123        }
124    }
125
126    // disable fixed background
127    if (!options.fixedBackground && !isExcluded) {
128        body.style.backgroundAttachment = "scroll";
129        html.style.backgroundAttachment = "scroll";
130    }
131}
132
133
134/************************************************
135 * SCROLLING 
136 ************************************************/
137 
138var que = [];
139var pending = false;
140var lastScroll = +new Date;
141
142/**
143 * Pushes scroll actions to the scrolling queue.
144 */
145function scrollArray(elem, left, top, delay) {
146    
147    delay || (delay = 1000);
148    directionCheck(left, top);
149
150    if (options.accelerationMax != 1) {
151        var now = +new Date;
152        var elapsed = now - lastScroll;
153        if (elapsed < options.accelerationDelta) {
154            var factor = (1 + (30 / elapsed)) / 2;
155            if (factor > 1) {
156                factor = Math.min(factor, options.accelerationMax);
157                left *= factor;
158                top  *= factor;
159            }
160        }
161        lastScroll = +new Date;
162    }          
163    
164    // push a scroll command
165    que.push({
166        x: left, 
167        y: top, 
168        lastX: (left < 0) ? 0.99 : -0.99,
169        lastY: (top  < 0) ? 0.99 : -0.99, 
170        start: +new Date
171    });
172        
173    // don't act if there's a pending queue
174    if (pending) {
175        return;
176    }  
177
178    var scrollWindow = (elem === document.body);
179    
180    var step = function (time) {
181        
182        var now = +new Date;
183        var scrollX = 0;
184        var scrollY = 0; 
185    
186        for (var i = 0; i < que.length; i++) {
187            
188            var item = que[i];
189            var elapsed  = now - item.start;
190            var finished = (elapsed >= options.animationTime);
191            
192            // scroll position: [0, 1]
193            var position = (finished) ? 1 : elapsed / options.animationTime;
194            
195            // easing [optional]
196            if (options.pulseAlgorithm) {
197                position = pulse(position);
198            }
199            
200            // only need the difference
201            var x = (item.x * position - item.lastX) >> 0;
202            var y = (item.y * position - item.lastY) >> 0;
203            
204            // add this to the total scrolling
205            scrollX += x;
206            scrollY += y;            
207            
208            // update last values
209            item.lastX += x;
210            item.lastY += y;
211        
212            // delete and step back if it's over
213            if (finished) {
214                que.splice(i, 1); i--;
215            }           
216        }
217
218        // scroll left and top
219        if (scrollWindow) {
220            window.scrollBy(scrollX, scrollY);
221        } 
222        else {
223            if (scrollX) elem.scrollLeft += scrollX;
224            if (scrollY) elem.scrollTop  += scrollY;                    
225        }
226        
227        // clean up if there's nothing left to do
228        if (!left && !top) {
229            que = [];
230        }
231        
232        if (que.length) { 
233            requestFrame(step, elem, (delay / options.frameRate + 1)); 
234        } else { 
235            pending = false;
236        }
237    };
238    
239    // start a new queue of actions
240    requestFrame(step, elem, 0);
241    pending = true;
242}
243
244
245/***********************************************
246 * EVENTS
247 ***********************************************/
248
249/**
250 * Mouse wheel handler.
251 * @param {Object} event
252 */
253function wheel(event) {
254
255    if (!initDone) {
256        init();
257    }
258    
259    var target = event.target;
260    var overflowing = overflowingAncestor(target);
261    
262    // use default if there's no overflowing
263    // element or default action is prevented    
264    if (!overflowing || event.defaultPrevented ||
265        isNodeName(activeElement, "embed") ||
266       (isNodeName(target, "embed") && /\.pdf/i.test(target.src))) {
267        return true;
268    }
269
270    var deltaX = event.wheelDeltaX || 0;
271    var deltaY = event.wheelDeltaY || 0;
272    
273    // use wheelDelta if deltaX/Y is not available
274    if (!deltaX && !deltaY) {
275        deltaY = event.wheelDelta || 0;
276    }
277
278    // check if it's a touchpad scroll that should be ignored
279    if (!options.touchpadSupport && isTouchpad(deltaY)) {
280        return true;
281    }
282
283    // scale by step size
284    // delta is 120 most of the time
285    // synaptics seems to send 1 sometimes
286    if (Math.abs(deltaX) > 1.2) {
287        deltaX *= options.stepSize / 120;
288    }
289    if (Math.abs(deltaY) > 1.2) {
290        deltaY *= options.stepSize / 120;
291    }
292    
293    scrollArray(overflowing, -deltaX, -deltaY);
294    event.preventDefault();
295}
296
297/**
298 * Keydown event handler.
299 * @param {Object} event
300 */
301function keydown(event) {
302
303    var target   = event.target;
304    var modifier = event.ctrlKey || event.altKey || event.metaKey || 
305                  (event.shiftKey && event.keyCode !== key.spacebar);
306    
307    // do nothing if user is editing text
308    // or using a modifier key (except shift)
309    // or in a dropdown
310    if ( /input|textarea|select|embed/i.test(target.nodeName) ||
311         target.isContentEditable || 
312         event.defaultPrevented   ||
313         modifier ) {
314      return true;
315    }
316    // spacebar should trigger button press
317    if (isNodeName(target, "button") &&
318        event.keyCode === key.spacebar) {
319      return true;
320    }
321    
322    var shift, x = 0, y = 0;
323    var elem = overflowingAncestor(activeElement);
324    var clientHeight = elem.clientHeight;
325
326    if (elem == document.body) {
327        clientHeight = window.innerHeight;
328    }
329
330    switch (event.keyCode) {
331        case key.up:
332            y = -options.arrowScroll;
333            break;
334        case key.down:
335            y = options.arrowScroll;
336            break;         
337        case key.spacebar: // (+ shift)
338            shift = event.shiftKey ? 1 : -1;
339            y = -shift * clientHeight * 0.9;
340            break;
341        case key.pageup:
342            y = -clientHeight * 0.9;
343            break;
344        case key.pagedown:
345            y = clientHeight * 0.9;
346            break;
347        case key.home:
348            y = -elem.scrollTop;
349            break;
350        case key.end:
351            var damt = elem.scrollHeight - elem.scrollTop - clientHeight;
352            y = (damt > 0) ? damt+10 : 0;
353            break;
354        case key.left:
355            x = -options.arrowScroll;
356            break;
357        case key.right:
358            x = options.arrowScroll;
359            break;            
360        default:
361            return true; // a key we don't care about
362    }
363
364    scrollArray(elem, x, y);
365    event.preventDefault();
366}
367
368/**
369 * Mousedown event only for updating activeElement
370 */
371function mousedown(event) {
372    activeElement = event.target;
373}
374
375
376/***********************************************
377 * OVERFLOW
378 ***********************************************/
379 
380var cache = {}; // cleared out every once in while
381setInterval(function () { cache = {}; }, 10 * 1000);
382
383var uniqueID = (function () {
384    var i = 0;
385    return function (el) {
386        return el.uniqueID || (el.uniqueID = i++);
387    };
388})();
389
390function setCache(elems, overflowing) {
391    for (var i = elems.length; i--;)
392        cache[uniqueID(elems[i])] = overflowing;
393    return overflowing;
394}
395
396function overflowingAncestor(el) {
397    var elems = [];
398    var rootScrollHeight = root.scrollHeight;
399    do {
400        var cached = cache[uniqueID(el)];
401        if (cached) {
402            return setCache(elems, cached);
403        }
404        elems.push(el);
405        if (rootScrollHeight === el.scrollHeight) {
406            if (!isFrame || root.clientHeight + 10 < rootScrollHeight) {
407                return setCache(elems, document.body); // scrolling root in WebKit
408            }
409        } else if (el.clientHeight + 10 < el.scrollHeight) {
410            overflow = getComputedStyle(el, "").getPropertyValue("overflow-y");
411            if (overflow === "scroll" || overflow === "auto") {
412                return setCache(elems, el);
413            }
414        }
415    } while (el = el.parentNode);
416}
417
418
419/***********************************************
420 * HELPERS
421 ***********************************************/
422
423function addEvent(type, fn, bubble) {
424    window.addEventListener(type, fn, (bubble||false));
425}
426
427function removeEvent(type, fn, bubble) {
428    window.removeEventListener(type, fn, (bubble||false));  
429}
430
431function isNodeName(el, tag) {
432    return (el.nodeName||"").toLowerCase() === tag.toLowerCase();
433}
434
435function directionCheck(x, y) {
436    x = (x > 0) ? 1 : -1;
437    y = (y > 0) ? 1 : -1;
438    if (direction.x !== x || direction.y !== y) {
439        direction.x = x;
440        direction.y = y;
441        que = [];
442        lastScroll = 0;
443    }
444}
445
446var deltaBufferTimer;
447
448function isTouchpad(deltaY) {
449    if (!deltaY) return;
450    deltaY = Math.abs(deltaY)
451    deltaBuffer.push(deltaY);
452    deltaBuffer.shift();
453    clearTimeout(deltaBufferTimer);
454
455    var allEquals    = (deltaBuffer[0] == deltaBuffer[1] && 
456                        deltaBuffer[1] == deltaBuffer[2]);
457    var allDivisable = (isDivisible(deltaBuffer[0], 120) &&
458                        isDivisible(deltaBuffer[1], 120) &&
459                        isDivisible(deltaBuffer[2], 120));
460    return !(allEquals || allDivisable);
461} 
462
463function isDivisible(n, divisor) {
464    return (Math.floor(n / divisor) == n / divisor);
465}
466
467var requestFrame = (function () {
468      return  window.requestAnimationFrame       || 
469              window.webkitRequestAnimationFrame || 
470              function (callback, element, delay) {
471                  window.setTimeout(callback, delay || (1000/60));
472              };
473})();
474
475
476/***********************************************
477 * PULSE
478 ***********************************************/
479 
480/**
481 * Viscous fluid with a pulse for part and decay for the rest.
482 * - Applies a fixed force over an interval (a damped acceleration), and
483 * - Lets the exponential bleed away the velocity over a longer interval
484 * - Michael Herf, http://stereopsis.com/stopping/
485 */
486function pulse_(x) {
487    var val, start, expx;
488    // test
489    x = x * options.pulseScale;
490    if (x < 1) { // acceleartion
491        val = x - (1 - Math.exp(-x));
492    } else {     // tail
493        // the previous animation ended here:
494        start = Math.exp(-1);
495        // simple viscous drag
496        x -= 1;
497        expx = 1 - Math.exp(-x);
498        val = start + (expx * (1 - start));
499    }
500    return val * options.pulseNormalize;
501}
502
503function pulse(x) {
504    if (x >= 1) return 1;
505    if (x <= 0) return 0;
506
507    if (options.pulseNormalize == 1) {
508        options.pulseNormalize /= pulse_(1);
509    }
510    return pulse_(x);
511}
512
513var isChrome = /chrome/i.test(window.navigator.userAgent);
514var isMouseWheelSupported = 'onmousewheel' in document; 
515
516if (isMouseWheelSupported && isChrome) {
517	addEvent("mousedown", mousedown);
518	addEvent("mousewheel", wheel);
519	addEvent("load", init);
520};
521
522})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.