feat(ios): iosGlassEffect and LiquidGlass containers

This commit is contained in:
Nathan Walker
2025-10-21 17:48:34 -07:00
parent 9b963b4019
commit 90a50e3d13
9 changed files with 250 additions and 102 deletions

View File

@@ -1,4 +1,4 @@
import { Observable, EventData, Page, CoreTypes, GlassEffectConfig, View, GlassEffectType, TouchAnimationOptions, Label, Image } from '@nativescript/core';
import { Observable, EventData, Page, CoreTypes, GlassEffectConfig, View, Label, Animation, LiquidGlassContainer } from '@nativescript/core';
let page: Page;
@@ -42,10 +42,10 @@ export class GlassEffectModel extends Observable {
const glass = args.object as View;
switch (glass.id) {
case 'glass1':
glass.translateX = -40;
glass.translateX = 10;
break;
case 'glass2':
glass.translateX = 40;
glass.translateX = 70;
break;
}
@@ -58,22 +58,43 @@ export class GlassEffectModel extends Observable {
this.glassTargetLabels[label.id] = label;
}
toggleMergeGlass() {
async toggleMergeGlass(args) {
if (!this.glassTargets['glass1'] || !this.glassTargets['glass2']) {
return;
}
const container = args?.object as LiquidGlassContainer | undefined;
this.glassMerged = !this.glassMerged;
const glass1 = this.glassTargets['glass1'];
const glass2 = this.glassTargets['glass2'];
glass1.animate({ translate: { x: this.glassMerged ? -40 : 0, y: 0 }, duration: 300, curve: CoreTypes.AnimationCurve.easeInOut }).catch(() => {});
glass2.animate({ translate: { x: this.glassMerged ? 40 : 0, y: 0 }, duration: 300, curve: CoreTypes.AnimationCurve.easeInOut }).catch(() => {});
this.glassTargetLabels['share'].animate({ opacity: this.glassMerged ? 1 : 0, duration: 300, curve: CoreTypes.AnimationCurve.easeInOut }).catch(() => {});
// Use relative deltas for translate; the container will bake them into frames post-animation
const d1 = this.glassMerged ? 25 : -25; // left bubble moves inward/outward
const d2 = this.glassMerged ? -25 : 25; // right bubble moves inward/outward
this.glassTargetLabels['like'].text = this.glassMerged ? 'Done' : 'Like';
if (!this.glassMerged) {
this.glassTargetLabels['like'].text = 'Like';
}
const animateAll = new Animation([
{ target: glass1, translate: { x: d1, y: 0 }, duration: 300, curve: CoreTypes.AnimationCurve.easeOut },
{ target: glass2, translate: { x: d2, y: 0 }, duration: 300, curve: CoreTypes.AnimationCurve.easeOut },
{
target: this.glassTargetLabels['share'],
opacity: this.glassMerged ? 0 : 1,
duration: 300,
},
]);
animateAll.play().then(() => {
if (this.glassMerged) {
this.glassTargetLabels['like'].text = 'Done';
}
// Ask container to stabilize frames so UIGlassContainerEffect samples correct positions
setTimeout(() => container?.stabilizeLayout?.(), 0);
});
// for testing, on tap, can see glass effect changes animating differences
this.testGlassBindingChanges();
// this.testGlassBindingChanges();
}
testGlassBindingChanges() {

View File

@@ -24,7 +24,7 @@
</LiquidGlass>
<LiquidGlassContainer row="4" tap="{{toggleMergeGlass}}" horizontalAlignment="center" verticalAlignment="middle" class="m-t-20" width="300" height="100">
<LiquidGlassContainer row="4" tap="{{toggleMergeGlass}}" horizontalAlignment="left" verticalAlignment="middle" class="m-t-20" width="300" height="100" columns="*">
<LiquidGlass id="glass1" loaded="{{loadedGlass}}" borderRadius="50" width="100" height="100">
<Label id="share" text="Share" fontSize="22" class="font-weight-bold text-center" width="100" height="100" loaded="{{loadedGlassLabels}}" />
</LiquidGlass>
@@ -47,6 +47,9 @@
<Label class="text-center c-white" fontWeight="bold" fontSize="18" text="Glass Effects Interactive" />
</GridLayout>
</GridLayout>
<!-- make scrollable to view glass on scroll -->
<ContentView height="500"/>
</StackLayout>
</ScrollView>