我不確定這是怎麼回事。但是,這是一個故事。本文更多地是關於一個概念,該概念將幫助您以不同的方式思考動畫。碰巧的是,這個特定的示例具有無限的滾動 - 特別是無需複制任何一個卡片的“完美”無限滾動。
我為什麼在這裡?好吧,這一切都始於一條推文。一條推文讓我思考了佈局和側滾動內容。
我接受了這個概念,並在我的網站上使用了它。而且它在寫作時仍在行動。
然後,我開始考慮畫廊的觀點和側滾動概念。我們跳上了一個直播,決定嘗試製作類似舊的蘋果“覆蓋流”圖案之類的東西。還記得嗎?
我對此做出的第一個想法,假設我會做到這一點,這樣就可以在沒有JavaScript的情況下使用,就像上面的演示中一樣,它使用“漸進式增強”。我抓住了Greensock和Scrolltrigger,我們走了。我離開了這項工作,這讓我失望了。我有一些東西,但不能完全滾動滾動以操作我想要的方式。 “下一個”和“以前”按鈕不想打球。您可以在這裡看到它,並且需要水平滾動。
因此,我在Greensock論壇上打開了一個新線程。我幾乎不知道我要開放一些認真的學習!我們用按鈕解決了問題。但是,成為我,我不得不問是否可能還有其他事情。有沒有一種“乾淨”的方法來進行無限的滾動?我在溪流上嘗試了一些東西,但沒有運氣。我很好奇。我嘗試了這支筆在Scrolltrigger版本中創建的類似技術。
最初的答案是,要做有點棘手:
關於滾動上無限事物的困難部分是,滾動條是有限的,而您想要的效果不是。因此,您必須像此演示(在Scrolltrigger演示部分中找到)一樣循環滾動位置,或直接掛在與捲軸相關的導航事件(例如車輪事件)中,而不是實際使用實際的滾動位置。
我認為就是這種情況,很高興將其“原樣”。幾天過去了,當我開始挖掘它時,傑克(Jack)放棄了我的想法。現在,經過一大堆經過,我在這裡與您分享該技術。
動畫任何東西
GSAP通常會忽略的一件事是,您幾乎可以用它來動畫。這通常是因為視覺事物在思考動畫時會想到什麼 - 事物的實際身體運動。我們的第一個想法並不是要將該過程帶到元級別並從退後一步。
但是,考慮動畫的工作,然後將其分解為層。例如,您玩動畫片。卡通是一系列作品。每個構圖都是場景。然後,您有能力擦洗與遙控器的構圖集,無論是在YouTube上,使用電視遙控器還是其他任何內容。正在發生的事情幾乎有三個層次。
這是我們創建不同類型的無限循環所需的技巧。這是這裡的主要概念。我們用時間軸為時間表的播放頭部位置動畫。然後,我們可以用滾動位置擦洗該時間表。
不用擔心這聽起來令人困惑。我們將分解它。
去“元”
讓我們從一個示例開始。我們將創建一個補間,將一些盒子從左到右移動。這裡是。
十箱不斷向右到右。 Greensock非常簡單。在這裡,我們使用“從圖”和重複來保持動畫的運行。但是,我們在每次迭代的開始時都有差距。我們還在使用同路大車來擺脫運動,這將在我們繼續時發揮重要作用。
gsap.fromto('。box',{ XPercent:100 },{ Xpercent:-200, 交錯:0.5, 持續時間:1, 重複:-1, 輕鬆:'無', }))
現在是有趣的部分。讓我們暫停補間,並將其分配給變量。然後,讓我們創建一個播放它的補間。我們可以通過對補間的總時間進行補充來做到這一點,這使我們可以在考慮重複和重複延遲的同時獲得或設置Tween的播放頭。
const shift = gsap.fromto('。box',{ XPercent:100 },{ 暫停:是的, Xpercent:-200, 交錯:0.5, 持續時間:1, 重複:-1, 輕鬆:'無', })) 固定持續時間= shift.duration() gsap.to(shift,{ 總時間:持續時間, 重複:-1, 持續時間:持續時間, 輕鬆:'無', }))
這是我們的第一個“元”補間。它看起來完全相同,但我們正在添加另一個控制級別。我們可以在此層上更改內容而不會影響原始層。例如,我們可以將補間更改為power4.in。這完全改變了動畫,但不會影響基礎動畫。我們有點以後備為保護自己。
不僅如此,我們可能會選擇僅重複時間表的某個部分。我們可以與另一個從這樣做的事情來做到這一點:
代碼將是這樣的。
gsap.fromto(shift,{ 總時間:2, },{ 總時間:持續時間-1, 重複:-1, 持續時間:持續時間, 輕鬆:'無' }))
你看到這要去哪裡了嗎?看那個補間。儘管它一直在循環,但每次重複的數字都會翻轉。但是,盒子處於正確的位置。
實現“完美”循環
如果我們回到原始示例,則每個重複之間存在明顯的差距。
這是訣竅。解鎖一切的部分。我們需要建立一個完美的循環。
讓我們從重複三次轉移開始。它等於使用重複:3。請注意,我們如何刪除重複:-1。
const getShift =()=> gsap.fromto('。box',{ XPercent:100 },{ Xpercent:-200, 交錯:0.5, 持續時間:1, 輕鬆:'無', })) const loop = gsap.timeline() .add(getShift()) .add(getShift()) .add(getShift())
我們將最初的補間變成了返回補間的函數,然後將其添加到新的時間軸中。這給了我們以下內容。
好的。但是,仍然存在差距。現在,我們可以提出添加和定位這些二聚體的位置參數。我們希望它是無縫的。這意味著在前一個結束之前插入每組兩組。這是基於交錯和元素數量的價值。
常量性交錯= 0.5 //在我們的轉移之間使用 const box = gsap.utils.toarray('。box') const loop = gsap.timeline({{ 重複:-1 })) .add(getShift(),0) .add(getShift(),boxes.length * stagger) .add(getShift(),boxes.length * stagger * 2)
如果我們更新時間表以重複並觀看(同時調整交錯以查看它如何影響事物)…
您會注意到中間有SA窗口會產生一個“無縫”循環。回想起我們操縱時間的早期技能嗎?這就是我們在這裡需要做的:循環循環“無縫”的時間窗口。
我們可以嘗試通過循環的那個窗口對總時間進行補充。
const loop = gsap.timeline({{ 暫停:是的, 重複:-1, })) .add(getShift(),0) .add(getShift(),boxes.length * stagger) .add(getShift(),boxes.length * stagger * 2) gsap.fromto(loop,{ 總時間:4.75, },, { 總時間:'= 5', 持續時間:10, 輕鬆:'無', 重複:-1, }))
在這裡,我們說的是總時間為4.75,並將周期的長度添加到此。一個週期的長度為5。這是時間軸的中間窗口。我們可以使用GSAP的nifty =做到這一點,這給了我們這一點:
花點時間消化那裡發生的事情。這可能是將您的頭纏住的最棘手的部分。我們正在計算時間表的時間窗口。很難可視化,但我已經走了。
這是一款手錶的演示,只有12秒鐘的手走一次。它是無限地循環的,重複:-1,然後我們使用從給定持續時間的特定時間窗口進行動畫。如果您減少說2和6的時間窗口,然後將持續時間更改為1,雙手重複時將從2點鐘到6點。但是,我們從未改變過基本動畫。
嘗試配置值以查看其如何影響事物。
在這一點上,最好為我們的窗戶位置建立一個公式。我們還可以在每個盒子過渡的持續時間內使用變量。
固定持續時間= 1 const cycle_duration = boxes.length *交錯 const start_time = cycle_duration(持續時間 * 0.5) const end_time = start_time cycle_duration
我們可以在三倍的元素上循環循環,而不是使用三個堆疊的時間表,在那裡我們獲得了不需要計算位置的好處。不過,將其視為三個堆疊的時間表是一種巧妙的方式,可以理解這一概念,這是一種很好的方法。
讓我們更改我們的實現,從一開始就創建一個重要的時間表。
恆量交錯= 0.5 const box = gsap.utils.toarray('。box') const loop = gsap.timeline({{ 暫停:是的, 重複:-1, })) const shifts = [...盒子,...盒子,...盒子] shifts.foreach((框,索引)=> { loop.fromto(box,{ XPercent:100 },{ Xpercent:-200, 持續時間:1, 輕鬆:'無', },索引 *交錯) }))
這更容易組合在一起,並為我們提供相同的窗口。但是,我們不需要考慮數學。現在,我們循環瀏覽三組盒子,並根據交錯定位每個動畫。
如果我們調整交錯,那看起來會怎樣?它會將盒子擠在一起。
但是,它破了窗口,因為現在總時間已經過去了。我們需要重新計算窗口。現在是插入我們之前計算出的公式的好時機。
固定持續時間= 1 const cycle_duration =交錯 * boxes.length const start_time = cycle_duration(持續時間 * 0.5) const end_time = start_time cycle_duration gsap.fromto(loop,{ 總時間:start_time, },, { 總時間:end_time, 持續時間:10, 輕鬆:'無', 重複:-1, }))
固定的!
如果我們想更改起始位置,我們甚至可以引入“偏移”。
恆量交錯= 0.5 const偏移= 5 *交錯 const start_time =(Cycle_duration(差異 * 0.5))偏移
現在,我們的窗口從不同的位置開始。
但是,這並不是很棒,因為它在兩端都給了我們這些尷尬的堆棧。為了擺脫這種效果,我們需要考慮一個框的“物理”窗口。或考慮他們如何進入和退出現場。
我們將使用Document.Body作為我們的示例的窗口。讓我們更新框,以作為單個時間表,盒子在Enter上和退出時向下擴展。我們可以使用Yoyo並重複:1進入進入和退出。
shifts.foreach((框,索引)=> { const box_tl = gsap .timeline() .fromto( 盒子, { Xpercent:100, },, { Xpercent:-200, 持續時間:1, 輕鬆:'無', },0 ) .fromto( 盒子, { 比例:0, },, { 比例:1, 重複:1, Yoyo:是的, 輕鬆:'無', 持續時間:0.5, },, 0 ) loop.Add(box_tl,索引 *交錯) }))
為什麼我們使用時間軸持續時間為1?它使事情更容易遵循。我們知道盒子位於中點時的時間為0.5。值得注意的是,放鬆將沒有我們通常在這裡想到的效果。實際上,放鬆實際上將在盒子的定位方式中發揮作用。例如,一個輕鬆的插件會在右邊的盒子上束縛,然後再移動。
上面的代碼為我們提供了。
幾乎。但是,我們的盒子在中間消失了一段時間。為了解決此問題,讓我們介紹即時屬性。它的作用類似於Animation-Fill模式:CSS中無。我們告訴GSAP,我們不想保留或預記錄任何盒子上的任何樣式。
shifts.foreach((框,索引)=> { const box_tl = gsap .timeline() .fromto( 盒子, { Xpercent:100, },, { Xpercent:-200, 持續時間:1, 輕鬆:'無', 直系程序:false, },0 ) .fromto( 盒子, { 比例:0, },, { 比例:1, 重複:1, zindex:boxes.length 1, Yoyo:是的, 輕鬆:'無', 持續時間:0.5, 直系程序:false, },, 0 ) loop.Add(box_tl,索引 *交錯) }))
那個小的變化為我們解決了!請注意我們還如何包括z index:boxes.length。這應該保護我們免受任何z指數問題。
我們有它!我們的第一個無縫無縫循環。沒有重複的元素和完美的延續。我們正在彎曲時間!如果您走了這麼遠,請輕拍自己的後背! ?
如果我們想一次看到更多的盒子,我們可以修補時機,交錯和輕鬆。在這裡,我們的交錯為0.2,我們還將不透明度引入了混合。
這裡的關鍵部分是我們可以利用retoperdelay,以便不透明度過渡比刻度更快。在0.25秒內褪色。等待0.5秒。淡出0.25秒以上。
.fromto( 盒子, { 不透明度:0, },{ 不透明度:1, 持續時間:0.25, 重複:1, repoydelay:0.5, 直系程序:false, 輕鬆:'無', Yoyo:是的, },0)
涼爽的!我們可以對那些進出過渡的人做任何想做的事情。這裡的主要內容是,我們有時間窗口為我們提供無限的循環。
將其連接到滾動
現在我們有了一個無縫的循環,讓我們將其連接到滾動中。為此,我們可以使用GSAP的Scrolltrigger。這需要額外的補間來擦洗我們的循環窗口。請注意,我們現在也設置了現在要暫停的循環。
const loop_head = gsap.fromto(loop,{ 總時間:start_time, },, { 總時間:end_time, 持續時間:10, 輕鬆:'無', 重複:-1, 暫停:是的, })) const scrub = gsap.to(loop_head,{ 總時間:0, 暫停:是的, 持續時間:1, 輕鬆:'無', }))
這裡的訣竅是使用Scrolltrigger通過更新擦洗的總時間來擦洗循環的播放頭。我們可以通過多種方式來設置此滾動。我們可以水平或綁定到容器。但是,我們要做的就是將盒子包裹在.boxes元素中,然後將其固定在視口上。 (這可以修復其在視口上的位置。)我們還將堅持垂直滾動。檢查演示以查看.box的樣式,以將事物設置為視口的大小。
從'https://cdn.skypack.dev/gsap/scrolltrigger'導入scrolltrigger' gsap.registerplugin(scrolltrigger) scrolltrigger.create({{ 開始:0, 結束:'= 2000', 水平:錯誤, PIN:'.boxes', onupdate:self => { scrub.vars.totaltime = loop_head.duration() * self.progress scrub.invalidate()。 restart() } }))
重要的部分是在update內部。那就是我們根據滾動進度設置補間的總時間。無效的呼叫將沖洗磨砂膏的任何內部記錄的位置。然後,重新啟動將位置設置為我們設置的新總時間。
嘗試一下!我們可以在時間軸中來回來回更新位置。
那有多酷?我們可以滾動以擦洗一個時間表,該時間表擦洗時間線,該時間表是時間表的窗口。消化一秒鐘,因為那是這裡發生的事情。
無限滾動時間旅行
到目前為止,我們一直在操縱時間。現在我們要去旅行!
為此,我們將使用其他一些GSAP實用程序,並且我們將不再擦洗LOOP_HEAD的總時間。相反,我們將通過代理進行更新。這是進行“ meta” GSAP的另一個很好的例子。
讓我們從標記播放頭位置的代理對像開始。
const playhead = {位置:0}
現在,我們可以更新磨砂膏以更新位置。同時,我們可以使用GSAP的包裝實用程序,該實用程序將位置值包裹在Loop_head持續時間周圍。例如,如果持續時間為10,並且我們提供值11,我們將返回1。
const position_wrap = gsap.utils.wrap(0,loop_head.duration()) const scrub = gsap.to(playhead,{ 位置:0, onupdate :()=> { loop_head.totaltime(position_wrap(playhead.position)) },, 暫停:是的, 持續時間:1, 輕鬆:'無', }))
最後但並非最不重要的一點是,我們需要修改Scrolltrigger,以便更新磨砂膏上的正確變量。那是位置,而不是總時間。
scrolltrigger.create({{ 開始:0, 結束:'= 2000', 水平:錯誤, PIN:'.boxes', onupdate:self => { scrub.vars.position = loop_head.duration() * self.progress scrub.invalidate()。 restart() } }))
在這一點上,我們已經轉到了代理,我們看不到任何更改。
滾動時,我們想要無限的循環。我們的第一個想法可能是當我們完成滾動進度時滾動開始。這將做到這一點,向後滾動。儘管這就是我們想做的,但我們不希望播放頭向後擦洗。這是總時間到來的地方。還記得嗎?它根據總體策略獲得或設置播放頭的位置,其中包括任何重複和重複延遲。
例如,假設循環頭的持續時間為5,我們到達那裡,我們不會擦洗0。相反,我們將繼續將循環頭擦洗至10。如果我們繼續前進,它將達到15,等等。同時,我們將跟踪迭代變量,因為這告訴我們我們在磨砂膏中的位置。我們還將確保只有在達到進度閾值時才更新迭代。
讓我們從迭代變量開始:
讓迭代= 0
現在,讓我們更新我們的Scrolltrigger實現:
const trigger = scrolltrigger.create({{ 開始:0, 結束:'= 2000', 水平:錯誤, PIN:'.boxes', onupdate:self => { const scroll = self.scroll() 如果(滾動> self.end -end -1){ //及時前進 包裹(1,1) } else if(scroll <p>請注意,我們現在如何將迭代考慮到位置計算中。請記住,這與洗滌器包裹在一起。我們還檢測到何時達到滾動的極限,這就是我們包裝的重點。此功能設置適當的迭代值並設置新的滾動位置。</p><pre rel="JavaScript" data-line=""> const wrap =(iterationdelta,scrollto)=> { 迭代= iterationdelta trigger.scroll(scrollto) trigger.update() }
我們有無限的滾動!如果您有一隻帶有滾動輪的花哨的老鼠,您可以放鬆一下,那就去吧!很有趣!
這是一個顯示當前迭代和進度的演示:
滾動折斷
我們在那裡。但是,在使用這樣的功能時,總會有“很高興的人”。讓我們從滾動釦子開始。 GSAP使它變得容易,因為我們可以使用gsap.utils.snap而無需任何其他依賴。這樣可以處理捕捉到我們提供點的時間。我們聲明了0和1之間的步驟,並且在示例中有10個盒子。這意味著快照為0.1對我們有用。
const snap = gsap.utils.snap(1 / boxes.length)
這返回了我們可以使用的函數來捕捉我們的位置值。
捲軸結束後,我們只想捕捉。為此,我們可以在Scrolltrigger上使用事件偵聽器。滾動結束時,我們將滾動到某個位置。
scrolltrigger.addeventlistener('scrollend',()=> { 捲軸(scrub.vars.position) }))
這是捲軸的:
const scrollToposition = posity => { const snap_pos = snap(位置) const進度= (snap_pos -loop_head.duration() *迭代) / loop_head.duration() const scroll = progressToscroll(progress) 觸發器(滾動) }
我們在這裡做什麼?
- 計算時間點以下點
- 計算當前進度。假設loop_head.duration()為1,我們已捕獲到2.5。這使我們的進度為0.5,導致迭代為2,其中2.5-1 * 2/1 ==== 0.5。我們計算進度,以便始終在1到0之間。
- 計算滾動目的地。這是我們的Scrolltrigger可以覆蓋的距離的一小部分。在我們的示例中,我們設定了2000年的距離,我們想要其中的一小部分。我們創建一個新的函數progressToscroll來計算它。
const progressTosCroll = progress => gsap.utils.clamp(1,trigger.end -1,gsap.utils.wrap(0,1,progress) * trigger.end)
此功能採用進度值並將其映射到最大的滾動距離。但是我們使用夾具來確保該值永遠不會為0或2000。這很重要。我們正在維護對這些價值觀的捕捉,因為這將使我們陷入無限的循環。
那裡有一點要接。查看此演示,該演示顯示了每個快照上的更新值。
為什麼事情變得更加挑剔?擦洗持續時間和輕鬆已改變。較小的持續時間和強力輕鬆使我們的快照。
const scrub = gsap.to(playhead,{ 位置:0, onupdate :()=> { loop_head.totaltime(position_wrap(playhead.position)) },, 暫停:是的, 持續時間:0.25, 輕鬆:'power3', }))
但是,如果您玩了該演示,您會注意到存在問題。有時,當我們在快照中包裹時,播放頭會跳動。我們需要通過確保在捕捉時包裝來解決這個問題 - 但是,只有在必要時才。
const scrollToposition = posity => { const snap_pos = snap(位置) const進度= (snap_pos -loop_head.duration() *迭代) / loop_head.duration() const scroll = progressToscroll(progress) if(progress> = 1 ||進度<p>現在,我們有無限滾動的滾動!</p><h3 id="接下來是什麼">接下來是什麼?</h3><p>我們已經完成了實心無限滾動器的基礎。我們可以利用它來添加內容,例如控件或鍵盤功能。例如,這可能是一種連接“下一個”和“以前”按鈕和鍵盤控件的方法。我們要做的就是操縱時間,對嗎?</p><pre rel="JavaScript" data-line=""> const next =()=> scrollToposition(scrub.vars.position-(1 / boxes.length)) const prev =()=> scrollToposition(scrub.vars.position(1 / boxes.length)) //左右箭頭加A和D document.addeventlistener('keydown',event => { if(event.keycode === 37 || event.keycode === 65)next() if(event.keycode === 39 || event.keycode === 68)prev() })) document.queryselector('。next')。 addeventListener('click'sext) document.queryselector('。prev')。 addeventListener('click'prev)
那可以給我們這樣的東西。
我們可以利用我們的捲軸函數並根據需要顛簸價值。
就是這樣!
看到嗎? GSAP比元素更具動畫作用!在這裡,我們彎腰並操縱時間來創建一個幾乎完美的無限滑塊。沒有重複的元素,沒有混亂和良好的靈活性。
讓我們回顧一下我們涵蓋的內容:
- 我們可以動畫動畫。 ?
- 當我們操縱時間時,我們可以將時機作為定位工具。
- 如何使用Scrolltrigger通過代理擦洗動畫。
- 如何使用GSAP的一些很棒的實用程序來處理我們的邏輯。
您現在可以操縱時間! ?
進行“ meta” GSAP的概念開闢了各種可能性。你還能動畫嗎?聲音的?影片?至於“封面流”演示,這是去的地方!
以上是進行' meta gsap”:尋求'完美”無限滾動的詳細內容。更多資訊請關注PHP中文網其他相關文章!

SVG具有自己的一套元素,屬性和屬性集,以至於內聯SVG代碼可能會變得漫長而復雜。通過利用CSS和SVG 2規範的一些即將到來的功能,我們可以減少該代碼以進行清潔標記。

PWA(Progressive Web應用程序)已經與我們在一起了一段時間。但是,每次我嘗試向客戶解釋它時,同樣的問題都會出現:“我的用戶會成為


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

禪工作室 13.0.1
強大的PHP整合開發環境

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具