feat: glass effects containers

This commit is contained in:
Nathan Walker
2025-09-15 10:18:06 -07:00
parent abe0b7a9cd
commit f2ec80f4ff
19 changed files with 374 additions and 53 deletions

View File

@@ -1,4 +1,4 @@
import { Observable, EventData, Page, CoreTypes, GlassEffectConfig } from '@nativescript/core';
import { Observable, EventData, Page, CoreTypes, GlassEffectConfig, View, GlassEffectType, TouchAnimationOptions, Label, Image } from '@nativescript/core';
let page: Page;
@@ -7,10 +7,154 @@ export function navigatingTo(args: EventData) {
page.bindingContext = new GlassEffectModel();
}
const originalTransform = Symbol('originalTransform');
export class GlassEffectModel extends Observable {
iosGlassEffectInteractive: GlassEffectConfig = {
interactive: true,
tint: '#faabab',
variant: 'clear',
};
currentEffect: GlassEffectConfig = {
variant: 'none',
interactive: false,
// tint: '#ccc',
};
toggleGlassEffect(args) {
const btn = args.object as View;
this.currentEffect =
this.currentEffect.variant === 'none'
? {
variant: 'clear',
interactive: true,
// tint: '#faabab',
}
: {
variant: 'none',
interactive: false,
// tint: '#ccc',
};
btn.iosGlassEffect = this.currentEffect;
}
images = ['res://bg1.jpg', 'res://bg2.jpg', 'res://bg3.jpg'];
currentImage = this.images[0];
cycleImage() {
if (!this.image) {
return;
}
let currentIndex = this.images.indexOf(this.currentImage);
currentIndex++;
if (currentIndex === this.images.length) {
currentIndex = 0;
}
this.currentImage = this.images[currentIndex];
// this.notifyPropertyChange('currentImage', this.currentImage);
this.image.animate({ opacity: 0, duration: 300, curve: CoreTypes.AnimationCurve.easeInOut }).then(() => {
this.image.src = this.currentImage;
setTimeout(() => {
this.image.animate({ opacity: 1, duration: 800, curve: CoreTypes.AnimationCurve.easeInOut });
});
});
}
image: Image;
loadedImage(args) {
this.image = args.object as Image;
}
glassMerged = false;
glassTargets = {};
loadedGlass(args) {
const glass = args.object as View;
switch (glass.id) {
case 'glass1':
glass.translateX = -40;
break;
case 'glass2':
glass.translateX = 40;
break;
}
this.glassTargets[glass.id] = glass;
}
glassTargetLabels: { [key: string]: Label } = {};
loadedGlassLabels(args) {
const label = args.object as Label;
this.glassTargetLabels[label.id] = label;
}
toggleMergeGlass() {
if (!this.glassTargets['glass1'] || !this.glassTargets['glass2']) {
return;
}
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(() => {});
this.glassTargetLabels['like'].text = this.glassMerged ? 'Done' : 'Like';
}
touchAnimation: TouchAnimationOptions = {
down: (view: View) => {
if (__APPLE__) {
UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion(
0.3,
0,
0.5,
3,
UIViewAnimationOptions.CurveEaseInOut | UIViewAnimationOptions.AllowUserInteraction,
() => {
if (view?.ios) {
view[originalTransform] = view[originalTransform] ?? view.ios.transform;
view.ios.transform = CGAffineTransformConcat(view[originalTransform], CGAffineTransformMakeScale(0.97, 0.97));
}
},
null,
);
} else {
view
?.animate({
scale: { x: 0.97, y: 0.97 },
duration: 120,
curve: CoreTypes.AnimationCurve.easeInOut,
})
.then(() => {})
.catch(() => {});
}
},
up: (view: View) => {
if (__APPLE__) {
UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion(
0.3,
0,
0.5,
3,
UIViewAnimationOptions.CurveEaseInOut | UIViewAnimationOptions.AllowUserInteraction,
() => {
if (view?.ios) {
view.ios.transform = view[originalTransform] ?? CGAffineTransformMakeScale(1, 1);
}
},
null,
);
} else {
view
?.animate({
scale: { x: 1, y: 1 },
duration: 120,
curve: CoreTypes.AnimationCurve.easeInOut,
})
.then(() => {})
.catch(() => {});
}
},
};
}

View File

@@ -1,27 +1,55 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="Glass Effects" class="action-bar">
<ActionBar title="Glass Effects" color="white">
</ActionBar>
</Page.actionBar>
<GridLayout>
<Image src="https://cdn.wallpapersafari.com/89/64/c6MnRY.jpg" stretch="aspectFill" iosOverflowSafeArea="true" />
<GridLayout rows="*,auto,auto,auto,*">
<GridLayout backgroundColor="#000">
<GridLayout row="1" width="300" height="150" iosGlassEffect="regular" horizontalAlignment="center" verticalAlignment="middle">
<!-- <Image src="https://wallpapers.com/images/hd/yosemite-iphone-1440-x-2560-9t7u3ctc1hmq35p4.jpg" stretch="aspectFill" iosOverflowSafeArea="true" /> -->
<Image src="res://bg1.jpg" stretch="aspectFill" iosOverflowSafeArea="true" loaded="{{loadedImage}}" />
<!-- <ContentView backgroundColor="#000" height="300" verticalAlignment="bottom"/> -->
<ScrollView >
<StackLayout>
<GridLayout height="400" tap="{{cycleImage}}"/>
<GridLayout rows="*,auto,auto,auto,auto,auto,*">
<Button row="2" text="Toggle Glass" tap="{{toggleGlassEffect}}" horizontalAlignment="center" verticalAlignment="middle" class="c-white font-weight-bold m-y-20 p-4" fontSize="22" borderRadius="32" width="300" height="100" touchAnimation="{{touchAnimation}}" iosGlassEffect="{{currentEffect}}"/>
<LiquidGlass row="3" width="300" height="100" borderRadius="32">
<Label text="Glass Interactive" fontSize="22" class="p-4 font-weight-bold text-center" />
</LiquidGlass>
<!--
<LiquidGlassContainer row="4" tap="{{toggleMergeGlass}}" horizontalAlignment="center" verticalAlignment="middle" class="m-t-20" width="300" height="100">
<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>
<LiquidGlass id="glass2" loaded="{{loadedGlass}}" borderRadius="50" width="100" height="100">
<Label id="like" text="Like" fontSize="22" class="font-weight-bold text-center" loaded="{{loadedGlassLabels}}" />
</LiquidGlass>
</LiquidGlassContainer> -->
<!-- <GridLayout row="1" width="300" height="150" iosGlassEffect="regular" horizontalAlignment="center" verticalAlignment="middle">
<Label class="text-center c-white" fontWeight="bold" fontSize="18" text="Glass Effects Regular" />
</GridLayout>
</GridLayout> -->
<GridLayout row="2" width="300" height="150" iosGlassEffect="clear" horizontalAlignment="center" verticalAlignment="middle" class="m-t-10">
<!-- <GridLayout row="3" width="300" height="150" iosGlassEffect="clear" horizontalAlignment="center" verticalAlignment="middle" class="m-t-10" borderRadius="50">
<Label class="text-center c-white" fontWeight="bold" fontSize="18" text="Glass Effects Clear" />
</GridLayout>
<GridLayout row="3" width="300" height="150" iosGlassEffect="{{iosGlassEffectInteractive}}" horizontalAlignment="center" verticalAlignment="middle" class="m-t-10">
<GridLayout row="4" width="300" height="150" iosGlassEffect="{{iosGlassEffectInteractive}}" horizontalAlignment="center" verticalAlignment="middle" class="m-t-10" borderRadius="50">
<Label class="text-center c-white" fontWeight="bold" fontSize="18" text="Glass Effects Interactive" />
</GridLayout>
</GridLayout>
</GridLayout> -->
</GridLayout>
<GridLayout height="600"/>
</StackLayout>
</ScrollView>
</GridLayout>
</Page>