Skip to content

Commit

Permalink
Merge pull request #175 from dhilt/issue-173-support-native-scroll-an…
Browse files Browse the repository at this point in the history
…choring

Support native scroll anchoring
  • Loading branch information
dhilt authored May 16, 2020
2 parents 69c9cfa + d664a2c commit c90966a
Show file tree
Hide file tree
Showing 38 changed files with 85 additions and 171 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,10 @@ where the viewport is a scrollable area of finite height:
.viewport {
height: 300px;
overflow-y: auto;
overflow-anchor: none;
}
```

If the height of the viewport is not constrained, it will pull the entire content of the datasource and no scrollbar will appear. Also, in order to support Safari and IE browsers, the library got its own implementation of scroll anchoring feature applicable to the case of virtual scrolling. Currently this implementation may conflict with native "overflow-anchor" in browsers that support it, so it is highly recommended to turn native anchoring off.
If the height of the viewport is not constrained, it will pull the entire content of the datasource and no scrollbar will appear. Also, in order to support Safari and IE browsers, the library has its own implementation of the scroll anchoring feature applicable to the case of virtual scrolling. Previous versions of the library (prior to 1.6.4) had the requirement that the value of "overflow-anchor" css property should be set to "none" for the viewport element.

\*uiScroll acts like \*ngFor, but the datasource is an object of special type (IDatasource). It implements method _get_ to be used by the \*uiScroll directive to access the data by _index_ and _count_ parameters. The directive calls `Datasource.get` method each time a user scrolls to the edge of visible element list. That's the API provided by the \*uiScroll.

Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/append-prepend-sync.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ doPrepend() {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/append-prepend.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ doAppend() {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/bof-eof.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ BOF / EOF changes counter: {{edgeCounter}}
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/check-size.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ First visible item's index: {{datasource.adapter.firstVisible.$index}}
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/clip.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ doClip() {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ Visible items counter: {{visibleCount}}
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/is-loading-extended.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ counter: {{innerLoopCounter}}
width: 150px;
height: 175px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/is-loading.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ for {{isLoadingCounter}} times.
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/items-count.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export class DemoItemsCountComponent {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/reload.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ by index <input [(ngModel)]="reloadIndex">
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/adapter/remove.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ doRemove(id: number) {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/common/basic.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export class DemoBasicComponent {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/common/buffer-size.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export class DemoBufferSizeComponent {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/common/different-heights.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export class DemoDifferentHeightsComponent {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/common/horizontal.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export class DemoHorizontalComponent {
text: `.viewport.horizontal {
width: 250px;
height: 100px;
overflow-anchor: none;
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/common/infinite.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export class DemoInfiniteComponent {
width: 150px;
height: 175px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/common/item-size.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export class DemoItemSizeComponent {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/common/min-max-indexes.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export class DemoMinMaxIndexesComponent {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/common/padding.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export class DemoPaddingComponent {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/common/start-index.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export class DemoStartIndexComponent {
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
14 changes: 2 additions & 12 deletions demo/app/samples/common/window-viewport.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,8 @@
<div description>
<p>
The entire window might work as a Viewport if <em>windowViewport</em> setting is set to true.
To provide most stable experience in this case,
the overflow-anchor css property should be disabled on the top level of the DOM tree.
This is what the doc says:
</p>
<p>
<i>
In order to support Safari and IE browsers,
the library got its own implementation of scroll anchoring feature
applicable to the case of virtual scrolling.
Currently this implementation may conflict with native "overflow-anchor" in browsers that support it,
so it is highly recommended to turn native anchoring off.
</i>
Previous versions of the library (prior to 1.6.4) required the "overflow-anchor" css property
to be disabled at the top level of the DOM tree (body or html tag). Today it is not necessary.
</p>
</div>
</app-demo>
6 changes: 1 addition & 5 deletions demo/app/samples/common/window-viewport.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,7 @@ export class DemoWindowViewportComponent {
</div>`
}, {
name: DemoSourceType.Styles,
text: `body {
overflow-anchor: none;
}
.item {
text: `.item {
font-weight: bold;
height: 25px;
}`
Expand Down
1 change: 0 additions & 1 deletion demo/app/samples/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ Index to reload:
width: 150px;
height: 250px;
overflow-y: auto;
overflow-anchor: none;
}
.item {
font-weight: bold;
Expand Down
6 changes: 0 additions & 6 deletions demo/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ body {
line-height: 1.4;
}

.entire-window {
overflow-anchor: none;
}

h1 {
font-size: 42px;
letter-spacing: -.015em;
Expand Down Expand Up @@ -205,15 +201,13 @@ input[type=checkbox], input[type=radio] {
.demo .work .viewport, .viewport {
width: 150px;
height: 250px;
overflow-anchor: none;
overflow-y: auto;
margin-bottom: 15px;
}

.demo .work .viewport.horizontal {
width: 250px;
height: 100px;
overflow-anchor: none;
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
Expand Down
8 changes: 5 additions & 3 deletions src/component/classes/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Logger } from './logger';
import { FetchModel } from './state/fetch';
import { ClipModel } from './state/clip';
import { RenderModel } from './state/render';
import { AdjustModel } from './state/adjust';
import { ScrollState } from './state/scroll';

export class State implements IState {
Expand All @@ -29,10 +30,10 @@ export class State implements IState {
fetch: FetchModel;
clip: ClipModel;
render: RenderModel;
adjust: AdjustModel;
startIndex: number;
lastPosition: number;
preFetchPosition: number;
bwdPaddingAverageSizeItemsCount: number;
bwdAverageSizeItemsCount: number;

scrollState: IScrollState;

Expand Down Expand Up @@ -63,7 +64,8 @@ export class State implements IState {
this.fetch = new FetchModel();
this.clip = new ClipModel();
this.render = new RenderModel();
this.bwdPaddingAverageSizeItemsCount = 0;
this.adjust = new AdjustModel();
this.bwdAverageSizeItemsCount = 0;

this.scrollState = new ScrollState();
}
Expand Down
13 changes: 13 additions & 0 deletions src/component/classes/state/adjust.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export class AdjustModel {
positionBefore: number;
bwdAverageSizeItemsCount: number;

constructor() {
this.reset();
}

reset() {
this.positionBefore = 0;
this.bwdAverageSizeItemsCount = 0;
}
}
5 changes: 4 additions & 1 deletion src/component/classes/state/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { Item } from '../item';

export class FetchModel {
private _newItemsData: any[] | null;

items: Item[];
positionBefore: number | null;
firstIndexBuffer: number | null;
lastIndexBuffer: number | null;
firstIndex: number | null;
Expand All @@ -27,14 +29,15 @@ export class FetchModel {

reset() {
this._newItemsData = null;
this.simulate = false;
this.items = [];
this.positionBefore = null;
this.firstIndex = this.firstIndexBuffer = null;
this.lastIndex = this.lastIndexBuffer = null;
this.hasAnotherPack = false;
this.negativeSize = 0;
this.hasAverageItemSizeChanged = false;
this.direction = null;
this.simulate = false;
this.isPrepend = false;
this.isReplace = false;
}
Expand Down
2 changes: 0 additions & 2 deletions src/component/classes/state/render.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export class RenderModel {
positionBefore: number;
sizeBefore: number;
sizeAfter: number;
fwdPaddingBefore: number;
Expand All @@ -13,7 +12,6 @@ export class RenderModel {
}

reset() {
this.positionBefore = 0;
this.sizeBefore = 0;
this.sizeAfter = 0;
this.fwdPaddingBefore = 0;
Expand Down
20 changes: 1 addition & 19 deletions src/component/classes/state/scroll.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,22 @@
import {
Direction,
ScrollEventData as IScrollEventData,
ScrollState as IScrollState,
WindowScrollState as IWindowScrollState
ScrollState as IScrollState
} from '../../interfaces/index';

import { Logger } from '../logger';

class WindowScrollState implements IWindowScrollState {
positionToUpdate: number;
delta: number;

constructor() {
this.reset();
}

reset() {
this.delta = 0;
this.positionToUpdate = 0;
}
}

export class ScrollState implements IScrollState {
previous: IScrollEventData | null;
current: IScrollEventData | null;

scrollTimer: ReturnType<typeof setTimeout> | null;
window: IWindowScrollState;

syntheticPosition: number | null;
syntheticFulfill: boolean;
animationFrameId: number;

constructor() {
this.window = new WindowScrollState();
this.reset();
}

Expand All @@ -44,6 +27,5 @@ export class ScrollState implements IScrollState {
this.syntheticPosition = null;
this.syntheticFulfill = false;
this.animationFrameId = 0;
this.window.reset();
}
}
2 changes: 1 addition & 1 deletion src/component/classes/viewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class Viewport {
if (negativeSize) {
newPosition = negativeSize;
const { itemSize } = this.settings;
this.state.bwdPaddingAverageSizeItemsCount = itemSize ? negativeSize / itemSize : 0;
this.state.bwdAverageSizeItemsCount = itemSize ? negativeSize / itemSize : 0;
}
this.scrollPosition = newPosition;
this.state.scrollState.reset();
Expand Down
Loading

0 comments on commit c90966a

Please sign in to comment.