Here is an example of a scrollable parallax effect RadListView implemented using Angular (no ScrollView needed). It also provides an example of sticking the list header at the top.
Please me know if it works for you.
Component template:
<GridLayout class="page">
<RadListView (scrolled)="onScroll($event)" [items]="dataItems" itemReorder="true"
(itemReordered)="onItemReordered($event)">
<ListViewGridLayout tkListViewLayout scrollDirection="Vertical" spanCount="1" ios:itemHeight="150"
dynamicItemSize="false"></ListViewGridLayout>
<ng-template tkListItemTemplate let-item="item">
<StackLayout orientation="vertical">
<!-- list item content goes here -->
</StackLayout>
</ng-template>
<ng-template tkListViewHeader>
<StackLayout>
<GridLayout #fixedHeaderContainer class="fixed-header-container">
<label text="Fixed Content" verticalAlignment="center"></label>
</GridLayout>
<StackLayout class="list-header-container">
<StackLayout #listHeaderContainer>
<label text="List Title"></label>
</StackLayout>
</StackLayout>
</StackLayout>
</ng-template>
</RadListView>
<GridLayout verticalAlignment="top" [height]="dockContainerHeight" [opacity]="dockContainerOpacity">
<FlexboxLayout justifyContent="flex-start" alignItems="center" class="docked-label-wrapper">
<button class="fas" text=""></button>
<StackLayout flexGrow="1" height="100%" [opacity]="dockContentOpacity" orientation="horizontal">
<label text="List Title"></label>
</StackLayout>
</FlexboxLayout>
</GridLayout>
Component scss:
.fixed-header-container {
height: 200;
padding: 0 16;
background-color: green;
label {
font-size: 30;
font-weight: 700;
color: $white;
}
}
.list-header-container {
margin-top: -12;
border-radius: 12 12 0 0;
background-color: #ffffff;
label {
margin: 16 0;
font-size: 22;
color: black;
vertical-align: center;
}
.smaller-label {
font-size: 12;
color: #909090;
}
}
RadListView {
height: 100%;
background-color: #ffffff;
}
.docked-label-wrapper {
margin: 0 0 10;
background-color: #ffffff;
.fas {
margin: 0;
font-size: 18;
}
label {
font-size: 18;
color: black;
vertical-align: center;
}
}
Component ts:
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { ListViewEventData, ListViewScrollEventData } from 'nativescript-ui-listview';
import { DataItem, DataItemService } from './data-items.service';
export const DOCK_HEADER_HEIGHT = 58;
@Component({
moduleId: module.id,
selector: 'app-comp-name',
templateUrl: './comp-name.component.html',
styleUrls: ['./comp-name.component.scss']
})
export class CompNameComponent implements OnInit {
dataItems: DataItem[];
dockContainerHeight = DOCK_HEADER_HEIGHT;
dockContainerOpacity = 0;
dockContentOpacity = 0;
@ViewChild('fixedHeaderContainer', { static: false })
fixedHeaderContainerRef: ElementRef;
@ViewChild('listHeaderContainer')
listHeaderContainerRef: ElementRef;
constructor(private _dataItemService: DataItemService) {}
ngOnInit(): void {
this.dataItems = this._dataItemService.getDataItems();
}
onItemReordered(args: ListViewEventData) {
console.log('Item reordered. Old index: ' + args.index + ' ' + 'new index: ' + args.data.targetIndex);
}
onScroll(args: ListViewScrollEventData) {
if (!this.fixedHeaderContainerRef) {
return;
}
const offset = args.scrollOffset < 0 ? 0 : args.scrollOffset;
const fixedHeaderHeight = this.fixedHeaderContainerRef.nativeElement.getActualSize().height;
this.applyFixedHeaderTransition(offset);
this.applyTitleTransition(offset, fixedHeaderHeight);
this.applyDockHeaderTransition(offset, fixedHeaderHeight);
}
private applyFixedHeaderTransition(scrollOffset: number) {
this.fixedHeaderContainerRef.nativeElement.translateY = scrollOffset;
}
private applyTitleTransition(scrollOffset: number, fixedHeaderHeight: number) {
const maxHeightChange = fixedHeaderHeight - DOCK_HEADER_HEIGHT;
const titleElement = this.listHeaderContainerRef.nativeElement;
if (maxHeightChange < scrollOffset) {
titleElement.translateX = -(scrollOffset - maxHeightChange) / 1.2;
titleElement.translateY = -(scrollOffset - maxHeightChange) * 2;
titleElement.scaleX = 1 - (scrollOffset - maxHeightChange) / fixedHeaderHeight;
titleElement.scaleY = 1 - (scrollOffset - maxHeightChange) / fixedHeaderHeight;
} else {
titleElement.translateX = 0;
titleElement.translateY = 0;
titleElement.scaleX = 1;
titleElement.scaleY = 1;
}
}
private applyDockHeaderTransition(scrollOffset: number, fixedHeaderHeight: number) {
const maxHeightChange = fixedHeaderHeight - DOCK_HEADER_HEIGHT;
const containerOpacity = 1 - scrollOffset / maxHeightChange;
this.dockContainerOpacity = containerOpacity <= 0 ? 1 : 0;
this.dockContentOpacity = (scrollOffset - (fixedHeaderHeight - DOCK_HEADER_HEIGHT)) / DOCK_HEADER_HEIGHT - 0.2;
}
}