33 ChangeDetectionStrategy ,
44 Component ,
55 computed ,
6+ DestroyRef ,
67 effect ,
78 inject ,
89 input ,
@@ -59,6 +60,7 @@ export interface OriginAnimation {
5960 '[class]' : 'hostClasses()' ,
6061 '[style.transform]' : 'originTransform()' ,
6162 '[style.transition-duration]' : 'originDuration()' ,
63+ '[style.transition-property]' : 'originTransitionProperty()' ,
6264 } ,
6365 template : `
6466 <ng-template #slideContent>
@@ -75,6 +77,8 @@ export interface OriginAnimation {
7577 <lg-image-slide
7678 [item]="item()!"
7779 [index]="index()"
80+ [dummySrc]="dummySrc()"
81+ [deferSrc]="deferSrc()"
7882 (mediaLoad)="onLoad()"
7983 (mediaError)="onError()"
8084 />
@@ -198,6 +202,20 @@ export class LgSlideComponent {
198202 ! ! this . item ( ) ,
199203 ) ;
200204
205+ // 2.x first-slide dummy (`getDummyImageContent`): while the
206+ // zoom-from-origin flight runs, the trigger's already-decoded
207+ // thumbnail flies enlarged in place of the still-loading image; the
208+ // real image mounts only once the flight lands and the dummy drops
209+ // shortly after the load settles (`loadContentOnFirstSlideLoad`).
210+ protected readonly dummySrc = signal < string | null > ( null ) ;
211+ private dummyDone = false ;
212+ private dummyDropTimer : ReturnType < typeof setTimeout > | null = null ;
213+ /** v2 mounts the real image only once the flight lands. */
214+ protected readonly deferSrc = computed ( ( ) => {
215+ const anim = this . originAnim ( ) ;
216+ return ! ! this . dummySrc ( ) && ! ! anim && ! anim . closing ;
217+ } ) ;
218+
201219 constructor ( ) {
202220 // React counterpart: Slide's sticky `shouldLoad` ref — once content
203221 // mounts it stays for as long as the slide is in the DOM window.
@@ -206,6 +224,48 @@ export class LgSlideComponent {
206224 this . sticky . set ( true ) ;
207225 }
208226 } ) ;
227+ effect ( ( ) => {
228+ const anim = this . originAnim ( ) ;
229+ if (
230+ this . dummyDone ||
231+ this . dummySrc ( ) ||
232+ ! anim ||
233+ anim . closing ||
234+ this . completed ( ) ||
235+ this . slideType ( ) !== 'image'
236+ ) {
237+ return ;
238+ }
239+ untracked ( ( ) => {
240+ const src = this . runtime . getDummySrc ( this . index ( ) ) ;
241+ if ( src ) {
242+ this . dummySrc . set ( src ) ;
243+ this . runtime . firstSlideLoading . set ( true ) ;
244+ } else {
245+ this . dummyDone = true ;
246+ }
247+ } ) ;
248+ } ) ;
249+ effect ( ( ) => {
250+ if ( ! this . dummySrc ( ) || ! this . completed ( ) ) {
251+ return ;
252+ }
253+ untracked ( ( ) => {
254+ this . dummyDropTimer = setTimeout ( ( ) => {
255+ this . dummyDone = true ;
256+ this . dummySrc . set ( null ) ;
257+ this . runtime . firstSlideLoading . set ( false ) ;
258+ } , 300 ) ;
259+ } ) ;
260+ } ) ;
261+ inject ( DestroyRef ) . onDestroy ( ( ) => {
262+ if ( this . dummyDropTimer !== null ) {
263+ clearTimeout ( this . dummyDropTimer ) ;
264+ }
265+ if ( this . dummySrc ( ) ) {
266+ this . runtime . firstSlideLoading . set ( false ) ;
267+ }
268+ } ) ;
209269 // React counterpart: Slide's afterAppendSlide mount effect (2.x
210270 // afterAppendSlide fired once when the slide's content mounts).
211271 effect ( ( ) => {
@@ -257,6 +317,7 @@ export class LgSlideComponent {
257317 this . inProgress ( ) && 'lg-slide-progress' ,
258318 this . shouldLoad ( ) && 'lg-loaded' ,
259319 this . completed ( ) && 'lg-complete lg-complete_' ,
320+ ! ! this . dummySrc ( ) && 'lg-first-slide' ,
260321 this . originClasses ( ) ,
261322 ) ,
262323 ) ;
@@ -281,6 +342,16 @@ export class LgSlideComponent {
281342 : null ;
282343 } ) ;
283344
345+ // The origin transform must LAND, never animate: measuring
346+ // (computeOrigin) forces a recalc that baselines the item at
347+ // identity, and the `:not(.lg-start-end-progress)` inherit rule
348+ // would transition identity → origin — a visible
349+ // fullscreen→thumbnail shrink before the flight.
350+ protected readonly originTransitionProperty = computed ( ( ) => {
351+ const anim = this . originAnim ( ) ;
352+ return anim && anim . stage === 'init' ? 'none' : null ;
353+ } ) ;
354+
284355 private readonly originClasses = computed ( ( ) => {
285356 const anim = this . originAnim ( ) ;
286357 if ( ! anim || anim . stage === 'init' ) {
0 commit comments