1class TweenSlide { 2 constructor(options) { 3 var containerId = options.containerId.replace(/#/g, ""); 4 this.selectedContainer = options.innerSelectedContainer !== undefined ? document.getElementById(containerId).querySelector(options.innerSelectedContainer) : document.getElementById(containerId); 5 this.tweenContainer = this.selectedContainer.querySelector('.tween-container'); 6 this.leftBtn = this.selectedContainer.querySelector('.left-btn'); 7 this.rightBtn = this.selectedContainer.querySelector('.right-btn'); 8 this.contentName = '.tween-box'; 9 this.animationDelay = options.animationDelay || 3000; 10 this.animationDuration = options.animationDuration || 300; 11 this.autoSlide = options.autoSlide !== undefined ? options.autoSlide : true; 12 this.swipeMode = options.swipeMode !== undefined ? options.swipeMode : false; 13 this.infiniteMode = options.infiniteMode !== undefined ? options.infiniteMode : true; 14 if (!this.infiniteMode) { 15 this.autoSlide = false; 16 this.swipeMode = false; 17 } 18 this.totalDisplay = options.totalDisplay || 4; 19 this.slideTimeout = null; 20 this.itemWidth = 0; 21 this.lastClientX = 0; 22 this.isDragging = false; 23 this.isArea = false; 24 this.isSchedule = false; 25 this.isAnimating = false; 26 this.init(); 27 } 28 29 init() { 30 const firstItem = this.tweenContainer.querySelector(this.contentName); 31 if (!firstItem) return; 32 this.itemWidth = firstItem.offsetWidth; 33 this._bindEvents(); 34 this.controlSlide(true); 35 this._updateButtonsState(); 36 } 37 38 getLeft() { 39 return parseInt(this.tweenContainer.style.left) || 0; 40 } 41 42 setLeft(value) { 43 this.tweenContainer.style.left = value + 'px'; 44 } 45 46 getMinLeft() { 47 return -this.itemWidth * (this._getTotalItems() - this.totalDisplay); 48 } 49 50 getMaxLeft() { 51 return 0; 52 } 53 54 _updateButtonsState() { 55 if (this.infiniteMode) return; 56 const currentLeft = this.getLeft(); 57 const totalItems = this._getTotalItems(); 58 const currentIndex = Math.round(Math.abs(currentLeft) / this.itemWidth); 59 const isAtFirst = currentIndex <= 0; 60 const isAtLast = currentIndex >= totalItems - this.totalDisplay; 61 if (this.leftBtn) { 62 if (isAtFirst) { 63 this.leftBtn.classList.add('inactive'); 64 } else { 65 this.leftBtn.classList.remove('inactive'); 66 } 67 } 68 if (this.rightBtn) { 69 if (isAtLast) { 70 this.rightBtn.classList.add('inactive'); 71 } else { 72 this.rightBtn.classList.remove('inactive'); 73 } 74 } 75 } 76 77 applyRubberBand(value) { 78 const maxLeft = this.getMaxLeft(); 79 const minLeft = this.getMinLeft(); 80 if (value > maxLeft) { 81 const overpull = value - maxLeft; 82 const limitedOverpull = Math.min(overpull, 100); 83 const resistedPull = limitedOverpull * 1; 84 return maxLeft + resistedPull; 85 } else if (value < minLeft) { 86 const overpull = minLeft - value; 87 const limitedOverpull = Math.min(overpull, 100); 88 const resistedPull = limitedOverpull * 1; 89 return minLeft - resistedPull; 90 } 91 return value; 92 } 93 94 animateTo(targetLeft, duration, callback) { 95 const startLeft = this.getLeft(); 96 const diff = targetLeft - startLeft; 97 const startTime = performance.now(); 98 const step = function (now) { 99 const elapsed = now - startTime; 100 const progress = Math.min(elapsed / duration, 1); 101 const eased = 1 - Math.pow(1 - progress, 3); 102 this.setLeft(startLeft + diff * eased); 103 if (progress < 1) { 104 requestAnimationFrame(step); 105 } else { 106 this.setLeft(targetLeft); 107 if (callback) callback(); 108 this._updateButtonsState(); 109 } 110 }.bind(this); 111 requestAnimationFrame(step); 112 } 113 114 controlSlide(run) { 115 this.tweenContainer.querySelectorAll(this.contentName) 116 .forEach(function (el) { el.style.pointerEvents = ''; }); 117 if (run && this._getTotalItems() > this.totalDisplay && this.autoSlide) { 118 if (!this.isArea && !this.isDragging && !this.isSchedule && this.animationDelay > 0) { 119 this.isSchedule = true; 120 clearInterval(this.slideTimeout); 121 this.slideTimeout = setInterval( 122 function () { this.slide(this.animationDuration); }.bind(this), 123 this.animationDelay 124 ); 125 } 126 } else { 127 this.isSchedule = false; 128 clearInterval(this.slideTimeout); 129 } 130 } 131 132 slide(duration) { 133 if (this.isAnimating) return; 134 if (!this.infiniteMode) { 135 return; 136 } 137 this.isAnimating = true; 138 this.animateTo(-this.itemWidth, duration, function () { 139 this._shiftFirst(); 140 this.setLeft(0); 141 this.isAnimating = false; 142 }.bind(this)); 143 } 144 145 _shiftFirst() { 146 const first = this.tweenContainer.querySelector(this.contentName); 147 this.tweenContainer.appendChild(first); 148 this.setLeft(0); 149 } 150 151 _shiftLast() { 152 const items = this.tweenContainer.querySelectorAll(this.contentName); 153 const last = items[items.length - 1]; 154 this.tweenContainer.prepend(last); 155 this.setLeft(-this.itemWidth);
156 } 157 158 dragComplete(duration) { 159 this.isAnimating = true; 160 const currentLeft = this.getLeft(); 161 if (this.infiniteMode) { 162 const count = Math.round(Math.abs(currentLeft) / this.itemWidth); 163 const targetLeft = -(count * this.itemWidth); 164 this.animateTo(targetLeft, duration, function () { 165 for (var i = 0; i < count; i++) { 166 this._shiftFirst(); 167 } 168 this.setLeft(0); 169 this.isAnimating = false; 170 this.controlSlide(true); 171 }.bind(this)); 172 } else { 173 const maxLeft = this.getMaxLeft(); 174 const minLeft = this.getMinLeft(); 175 const totalItems = this._getTotalItems(); 176 let currentIndex = Math.round(Math.abs(currentLeft) / this.itemWidth); 177 currentIndex = Math.max(0, Math.min(currentIndex, totalItems - 1)); 178 const offsetFromCurrent = currentLeft + (currentIndex * this.itemWidth); 179 const dragRatio = Math.abs(offsetFromCurrent) / this.itemWidth; 180 let targetIndex = currentIndex; 181 if (offsetFromCurrent < 0 && dragRatio > 0.1 && currentIndex < totalItems - 1) { 182 targetIndex = currentIndex + 1; 183 } else if (offsetFromCurrent > 0 && dragRatio > 0.1 && currentIndex > 0) { 184 targetIndex = currentIndex - 1; 185 } 186 let targetLeft = -targetIndex * this.itemWidth; 187 targetLeft = Math.max(minLeft, Math.min(maxLeft, targetLeft)); 188 this.animateTo(targetLeft, duration, function () { 189 this.isAnimating = false; 190 this.controlSlide(true); 191 }.bind(this)); 192 } 193 } 194 195 _getTotalItems() { 196 return this.tweenContainer.querySelectorAll(this.contentName).length; 197 } 198 199 _swipeToNext(distance) { 200 if (this.isDragging || this.isAnimating) return; 201 this.isAnimating = true; 202 this.controlSlide(false); 203 204 if (this.infiniteMode) { 205 if (distance > 0) { 206 this._shiftLast(); 207 this.setLeft(-this.itemWidth); 208 this.animateTo(0, this.animationDuration, function () { 209 this.isAnimating = false; 210 this.controlSlide(true); 211 }.bind(this)); 212 } else { 213 this.animateTo(-this.itemWidth, this.animationDuration, function () { 214 this._shiftFirst(); 215 this.setLeft(0); 216 this.isAnimating = false; 217 this.controlSlide(true); 218 }.bind(this)); 219 } 220 } else { 221 const currentLeft = this.getLeft(); 222 const totalItems = this._getTotalItems(); 223 const currentIndex = Math.round(Math.abs(currentLeft) / this.itemWidth); 224 if (distance > 0 && currentIndex > 0) { 225 const targetLeft = -(currentIndex - 1) * this.itemWidth; 226 this.animateTo(targetLeft, this.animationDuration, function () { 227 this.isAnimating = false; 228 this.controlSlide(true); 229 }.bind(this)); 230 } else if (distance < 0 && currentIndex < totalItems - this.totalDisplay) { 231 const targetLeft = -(currentIndex + 1) * this.itemWidth; 232 this.animateTo(targetLeft, this.animationDuration, function () { 233 this.isAnimating = false; 234 this.controlSlide(true); 235 }.bind(this)); 236 } else { 237 this.isAnimating = false; 238 this.controlSlide(true); 239 } 240 } 241 } 242 243 _bindEvents() { 244 this.tweenContainer.addEventListener('mouseenter', function () { 245 this.isArea = true; 246 this.controlSlide(false); 247 }.bind(this)); 248 249 this.tweenContainer.addEventListener('mouseleave', function () { 250 this.isArea = false; 251 this.controlSlide(true); 252 }.bind(this)); 253 254 this.tweenContainer.addEventListener('mousedown', function (e) { 255 e.preventDefault(); 256 this.isDragging = true; 257 this.lastClientX = e.clientX; 258 this.tweenContainer.style.cursor = !this.swipeMode ? 'grabbing' : 'pointer'; 259 this.controlSlide(false); 260 }.bind(this)); 261 262 this.tweenContainer.addEventListener('touchstart', function (e) { 263 this.isDragging = true; 264 this.lastClientX = e.touches[0].pageX; 265 this.tweenContainer.style.cursor = !this.swipeMode ? 'grabbing' : 'pointer'; 266 this.controlSlide(false); 267 }.bind(this), { passive: true });
268 269 if (!this.swipeMode) { 270 const onDragMove = function (e) { 271 if (!this.isDragging || !(this._getTotalItems() > this.totalDisplay)) return; 272 e.preventDefault(); 273 const clientX = e.clientX !== undefined ? e.clientX : e.touches[0].pageX; 274 const diff = clientX - this.lastClientX; 275 var newLeft = this.getLeft() + diff; 276 if (this.infiniteMode) { 277 this.setLeft(newLeft); 278 if (newLeft > 0) { 279 this._shiftLast(); 280 this.setLeft(newLeft - this.itemWidth); 281 } else if (newLeft < -this.itemWidth) { 282 this._shiftFirst(); 283 this.setLeft(newLeft + this.itemWidth); 284 } 285 } else { 286 newLeft = this.applyRubberBand(newLeft); 287 this.setLeft(newLeft); 288 } 289 this.lastClientX = clientX; 290 this.tweenContainer.querySelectorAll(this.contentName) 291 .forEach(function (el) { el.style.pointerEvents = 'none'; }); 292 }.bind(this); 293 const onDragEnd = function (e) { 294 if (!this.isDragging) return; 295 this.isDragging = false; 296 this.tweenContainer.style.cursor = 'grab'; 297 this.dragComplete(100); 298 this.controlSlide(true); 299 }.bind(this); 300 document.addEventListener('mousemove', onDragMove); 301 this.tweenContainer.addEventListener('touchmove', onDragMove, { passive: false }); 302 document.addEventListener('mouseup', onDragEnd); 303 document.addEventListener('touchend', onDragEnd); 304 } else { 305 const onSwipeMove = function (e) { 306 if (!this.isDragging) return; 307 e.preventDefault(); 308 this.tweenContainer.querySelectorAll(this.contentName) 309 .forEach(function (el) { el.style.pointerEvents = 'none'; }); 310 }.bind(this); 311 const onSwipeEnd = function (e) { 312 if (!this.isDragging) return; 313 this.isDragging = false; 314 const clientX = e.clientX !== undefined ? e.clientX : e.changedTouches[0].clientX; 315 const distance = clientX - this.lastClientX; 316 if (Math.abs(distance) > 30) { 317 this._swipeToNext(distance); 318 } else { 319 this.controlSlide(true); 320 } 321 this.tweenContainer.style.cursor = 'pointer'; 322 }.bind(this); 323 this.tweenContainer.addEventListener('mousemove', onSwipeMove); 324 this.tweenContainer.addEventListener('touchmove', onSwipeMove, { passive: false }); 325 this.tweenContainer.addEventListener('mouseup', onSwipeEnd); 326 this.tweenContainer.addEventListener('touchend', onSwipeEnd); 327 } 328 329 if (this.leftBtn) { 330 this.leftBtn.addEventListener('click', function () { 331 this._swipeToNext(10); 332 }.bind(this)); 333 } 334 335 if (this.rightBtn) { 336 this.rightBtn.addEventListener('click', function () { 337 this._swipeToNext(-10); 338 }.bind(this)); 339 } 340 } 341 342 destroy() { 343 clearInterval(this.slideTimeout); 344 } 345}
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.