1/** 2 * ã¹ã ã¼ã¹ã¹ã¯ãã¼ã« ã¤ã³ã©ã¤ã³SVGå ã¢ã³ã«ã¼ã¿ã°å¯¾å¿ 3 * 4 * usage: 5 * <script src="smoothscroll.js"></script> //ãã®ãã¡ã¤ã« 6 * 以ä¸ãå¿ è¦ã«å¿ãã¦ï¼ 7 * <script> smoothScroll.setOption({offset:-200}); </script> //ãªãã»ããä½ç½®è¨å®(ã¹ã¯ãã¼ã«ä½ç½®ãããã) 8 * <script> smoothScroll.setOption({speed:20}); </script> //é度è¨å® 9 * <script> smoothScroll.reInit(); </script> //åçã«è¦ç´ ã追å ããã¨ãã«åã¹ãã£ã³ 10 * <script> smoothScroll.addRender(elm); </script> //aã¿ã°ãåçã«è¿½å ããå ´åã«ã¤ãã³ããªã¹ãã¼è¿½å 11 * <script> smoothScroll.elementScroll(elm); </script> //ã¨ã¬ã¡ã³ãã¸ã¹ã¯ãã¼ã« 12 * <script> smoothScroll.anchorScroll('anchorName'); </script> //ãã¼ã¸èªã¿è¾¼ã¿å¾ã«ã¢ã³ã«ã¼åã¸ã¹ã¯ãã¼ã« 13 * <script> smoothScroll.nameScroll('anchorName'); </script> //ã¢ã³ã«ã¼åã¸ã¹ã¯ãã¼ã« 14 * <script> smoothScroll.idScroll('idName'); </script> //idåã¸ã¹ã¯ãã¼ã« 15 * <script> smoothScroll.positionScroll(800); </script> //æ°å¤æå®ã§ã¹ã¯ãã¼ã« 16 * <script> smoothScroll.bodyScroll(); </script> //bodyã¸ã¹ã¯ãã¼ã« 17 * 18 * @author ao-system, Inc. 19 * 20 * @date 2015-04-26 21 * @date 2017-07-25 22 * @date 2017-09-22 23 * @date 2017-10-03 24 * @date 2018-06-20 25 * @date 2018-09-11 26 * @date 2019-07-18 27 * @date 2021-01-19 28 * @date 2022-01-06 29 * @date 2024-03-04 classã§æ¸ãç´ããå ¨ã¦ã®æ©è½ã試ããããã§ã¯ãªããidã¸ã¹ã¯ãã¼ã« 30 * @date 2025-06-20 ã¬ã¿ã¬ã¿éããå ´åãæã£ãã®ã§ä¿®æ£ 31 * @date 2025-07-18 iPhoneã§ã®ã¹ã¯ãã¼ã«ãéãããåé¡ãä¿®æ£ãrequestAnimationFrameã使ç¨ããã¹ã¯ãã¼ã«è·é¢ã«å¿ããæéãã¼ã¹ã®ã¢ãã¡ã¼ã·ã§ã³ã«å¤æ´ã 32 */ 33 34class SmoothScroll { 35 #offset = 0; 36 #speed = 15; // Lower value = faster scroll. 37 #minDuration = 400; // ms 38 #maxDuration = 1500; // ms 39 40 #animationFrame = null; 41 42 // For time-based animation 43 #startTime = null; 44 #startPos = 0; 45 #targetPos = 0; 46 #duration = 0; 47 48 #handleClickHandler = this.#handleClick.bind(this); 49 #preventDefaultHandler = this.#preventDefault.bind(this); 50 51 constructor() { 52 this.#init(); 53 } 54 55 //åæè¨å® 56 #init(){ 57 window.addEventListener('load',() => {this.#attachAnchorListeners();}); 58 } 59 60 /** 61 * è¦ç´ ã®ãã¼ã¸ä¸é¨ããã®æ£ç¢ºãªä½ç½®ãè¨ç®ãã¾ãã 62 * offsetParentãå帰çã«ãã©ãããã¹ã¦ã®ãªãã»ãããåè¨ãã¾ãã 63 * @param {HTMLElement} elm - ä½ç½®ãè¨ç®ããè¦ç´ ã 64 * @returns {number} ãã¼ã¸ä¸é¨ããã®ãã¯ã»ã«åä½ã®ãªãã»ããã 65 */ 66 #offsettop(elm) { 67 let offsettop = elm.offsetTop; 68 if (elm.offsetParent) { 69 let parent = elm.offsetParent; 70 while (parent) { 71 offsettop += parent.offsetTop; 72 parent = parent.offsetParent; 73 } 74 } 75 return Math.floor(Math.max(0, offsettop + this.#offset)); 76 } 77 78 //ã¹ã¯ãã¼ã«ä½ç½®ãè¿ã 79 #scrolltop() { 80 return parseInt(window.scrollY ?? window.pageYOffset ?? document.documentElement.scrollTop) || 0; 81 } 82 83 //ã¤ãã³ãããã£ã³ã»ã«ãã 84 #killEvt(event) { 85 event.preventDefault(); 86 event.stopPropagation(); 87 } 88 89 //ããã©ã«ãã®ãã¤ã¼ã«åä½ãç¡å¹ã«ãã颿° 90 #preventDefault(event) { 91 event.preventDefault(); 92 } 93 94 //ãã¤ã¼ã«æä½ãç¡å¹å 95 #disableWheel() { 96 window.addEventListener('wheel', this.#preventDefaultHandler, { passive: false }); 97 } 98 99 //ãã¤ã¼ã«æä½ãå度æå¹ã«ãã 100 #enableWheel() { 101 window.removeEventListener('wheel', this.#preventDefaultHandler, { passive: false }); 102 } 103 104 /** 105 * ã¹ã¯ãã¼ã«ã¢ãã¡ã¼ã·ã§ã³ã®ã¡ã¤ã³ã«ã¼ãã§ãã 106 * requestAnimationFrameã使ç¨ãã¦ããã©ã¦ã¶ã®æç»ã¿ã¤ãã³ã°ã«åããã¦æ»ããã«å®è¡ããã¾ãã 107 * çµéæéã¨ã¤ã¼ã¸ã³ã°é¢æ°ã«åºã¥ããç¾å¨ã®ã¹ã¯ãã¼ã«ä½ç½®ãè¨ç®ãã¦æ´æ°ãã¾ãã 108 */ 109 #runAnimation() { 110 const elapsedTime = performance.now() - this.#startTime; 111 const progress = Math.min(elapsedTime / this.#duration, 1); 112 113 // easeOutQuint easing function: starts fast, ends slower. 114 const ease = (t) => 1 - Math.pow(1 - t, 5); 115 const easedProgress = ease(progress); 116 117 const newPos = this.#startPos + (this.#targetPos - this.#startPos) * easedProgress; 118 window.scrollTo(0, newPos); 119 120 if (progress < 1) { 121 this.#animationFrame = requestAnimationFrame(() => { this.#runAnimation(); }); 122 } else { 123 // Animation finished 124 window.scrollTo(0, this.#targetPos); // Ensure it ends at the exact position 125 this.#enableWheel(); 126 this.#animationFrame = null; 127 } 128 } 129
130 /** 131 * ã¢ã³ã«ã¼ãªã³ã¯ãã¯ãªãã¯ãããéã®ã¤ãã³ããå¦çãã¾ãã 132 * ã¤ãã³ãããã£ã³ã»ã«ãããªã³ã¯ã®href屿§ããã¿ã¼ã²ããIDãåå¾ãã¦ã¹ã¯ãã¼ã«ãéå§ãã¾ãã 133 * @param {MouseEvent} event - ã¯ãªãã¯ã¤ãã³ããªãã¸ã§ã¯ãã 134 */ 135 #handleClick(event) { 136 this.#killEvt(event); 137 const anchor = event.target.closest('a'); 138 if (!anchor) { 139 return; 140 } 141 let anchorTagName = ''; 142 // SVG <a> ã¿ã°å¯¾å¿ 143 if (typeof anchor.href === 'object' && 'baseVal' in anchor.href) { 144 anchorTagName = anchor.href.baseVal.replace(/^#/, ''); 145 } else if (anchor.hash) { 146 anchorTagName = anchor.hash.replace(/^#/, ''); 147 } else { 148 return; 149 } 150 151 if (this.#animationFrame) { 152 cancelAnimationFrame(this.#animationFrame); 153 this.#animationFrame = null; 154 } 155 156 const targetElement = document.getElementById(anchorTagName); 157 if (targetElement) { 158 this.elementScroll(targetElement); 159 } 160 } 161 162 /** 163 * ãã¼ã¸å ã®ãã¹ã¦ã®ã¢ã³ã«ã¼ãªã³ã¯(`a[href*="#"]`)ã«ã¯ãªãã¯ã¤ãã³ããªã¹ãã¼ãè¨å®ãã¾ãã 164 * SVGå ã®ãªã³ã¯ã«ã対å¿ãã¦ãã¾ãã 165 */ 166 #attachAnchorListeners() { 167 const anchorTags = document.querySelectorAll('a[href]'); 168 anchorTags.forEach((anchorTag) => { 169 if (anchorTag.href) { 170 if (anchorTag.href.baseVal) { //SVGã®å ´å e.g. <svg><a href="#footer"><rect .. /></a></svg> 171 if (anchorTag.href.baseVal.startsWith('#')) { 172 ((elm) => { 173 elm.removeEventListener('click',this.#handleClickHandler); 174 elm.addEventListener('click',this.#handleClickHandler); 175 })(anchorTag); 176 } 177 } else { 178 if (anchorTag.href.indexOf('#') != -1 && ((anchorTag.pathname == location.pathname) || ('/' + anchorTag.pathname == location.pathname))) { 179 ((elm) => { 180 elm.removeEventListener('click',this.#handleClickHandler); 181 elm.addEventListener('click',this.#handleClickHandler); 182 })(anchorTag); 183 } 184 } 185 } 186 }); 187 } 188 189 /** 190 * ã¹ã¯ãã¼ã«åä½ã®ãªãã·ã§ã³ãè¨å®ãã¾ãã 191 * @param {object} obj - ãªãã·ã§ã³ãå«ããªãã¸ã§ã¯ãã e.g. { offset: -100, speed: 20 } 192 */ 193 setOption(obj) { 194 if (obj.offset !== undefined) { 195 this.#offset = obj.offset; //ã¹ã¯ãã¼ã«ä½ç½®ã®ãªãã»ãã 196 } 197 if (obj.speed !== undefined) { 198 this.#speed = obj.speed; //é度è¨å® 199 } 200 } 201 202 //åæè¨å® å度 203 reInit() { 204 this.#attachAnchorListeners(); 205 } 206 207 //ã¤ãã³ããªã¹ãã¼ å¾ããåå¥ã«è¿½å 208 addRender(anchorElm) { 209 if (anchorElm.href) { 210 if (anchorElm.href.baseVal) { //SVGã®å ´å e.g. <svg><a href="#footer"><rect .. /></a></svg> 211 if (anchorElm.href.baseVal.slice(0,1) == '#') { 212 anchorElm.removeEventListener('click',this.#handleClickHandler); 213 anchorElm.addEventListener('click',this.#handleClickHandler); 214 } 215 } else { 216 if (anchorElm.href.indexOf('#') != -1 && ((anchorElm.pathname == location.pathname) || ('/' + anchorElm.pathname == location.pathname))) { 217 anchorElm.removeEventListener('click',this.#handleClickHandler); 218 anchorElm.addEventListener('click',this.#handleClickHandler); 219 } 220 } 221 } 222 } 223 224 /** 225 * æå®ãããHTMLè¦ç´ ã¸ã®ã¹ã¯ãã¼ã«ãéå§ãã¾ãã 226 * ã¢ãã¡ã¼ã·ã§ã³ã®åæè¨å®(éå§ä½ç½®ãç®æ¨ä½ç½®ããã¥ã¬ã¼ã·ã§ã³)ãè¡ããã«ã¼ããéå§ãã¾ãã 227 * @param {HTMLElement} elm - ã¹ã¯ãã¼ã«å ã®è¦ç´ ã 228 */ 229 elementScroll(elm) { 230 if (elm) { 231 this.#disableWheel(); 232 this.#startPos = this.#scrolltop(); 233 this.#targetPos = this.#offsettop(elm); 234 235 const distance = Math.abs(this.#targetPos - this.#startPos); 236 237 // Calculate a dynamic duration based on the scroll distance. 238 const calculatedDuration = Math.sqrt(distance) * this.#speed; 239 240 // Clamp the duration to the configured min/max range. 241 this.#duration = Math.max(this.#minDuration, Math.min(calculatedDuration, this.#maxDuration)); 242 243 if (this.#animationFrame) { 244 cancelAnimationFrame(this.#animationFrame); 245 } 246 247 this.#startTime = performance.now(); 248 this.#animationFrame = requestAnimationFrame(() => { this.#runAnimation(); }); 249 } 250 } 251
252 //ã¢ã³ã«ã¼åã¸ã¹ã¯ãã¼ã«ãã Loadæã«ã»ãã 253 anchorScroll(nameValue) { 254 window.addEventListener('load',() => { 255 if (document.getElementById('loading')) { //ãã¼ã¸ãã¼ãä¸è¡¨ç¤ºãããã°ãããæ¶ã 256 document.getElementById('loading').outerHTML = ''; 257 } 258 this.elementScroll(document.getElementsByName(nameValue)[0] ?? null); 259 }); 260 } 261 262 //nameåã¸ã¹ã¯ãã¼ã«ããããã¤ã¬ã¯ãå®è¡ 263 nameScroll(nameValue) { 264 this.elementScroll(document.getElementsByName(nameValue)[0] ?? null); 265 } 266 267 //IDåã¸ã¹ã¯ãã¼ã«ããããã¤ã¬ã¯ãå®è¡ 268 idScroll(idValue) { 269 this.elementScroll(document.getElementById(idValue)); 270 } 271 272 //bodyã¸ã¹ã¯ãã¼ã«ããããã¤ã¬ã¯ãå®è¡ 273 bodyScroll() { 274 this.elementScroll(document.body); 275 } 276} 277const smoothScroll = new SmoothScroll();
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.