Skip to content

Instantly share code, notes, and snippets.

@karthikaruna
Last active September 26, 2020 08:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save karthikaruna/e831e3428f3b7d6061b9be4b6bfb4091 to your computer and use it in GitHub Desktop.
Save karthikaruna/e831e3428f3b7d6061b9be4b6bfb4091 to your computer and use it in GitHub Desktop.
New Twiddle
import Ember from 'ember';
const {
Component,
computed
} = Ember;
export default Component.extend({
scrollTop: 0,
itemCount: computed.alias('items.length'),
viewportHeight: computed(function () {
return this.get('itemCount') * this.get('rowHeight');
}),
startIndex: computed('scrollTop', function () {
let startNode = Math.floor(this.get('scrollTop') / this.get('rowHeight')) - this.get('renderAhead');
startNode = Math.max(0, startNode);
return startNode;
}),
visibleNodeCount: computed('startIndex', function () {
let count = Math.ceil(this.get('rootHeight') / this.get('rowHeight')) + 2 * this.get('renderAhead');
count = Math.min(this.get('itemCount') - this.get('startIndex'), count);
return count;
}),
visibleItems: computed('startIndex', 'visibleNodeCount', function () {
return this.get('items').slice(
this.get('startIndex'),
this.get('startIndex') + this.get('visibleNodeCount')
);
}),
offsetY: computed('startIndex', function () {
return this.get('startIndex') * this.get('rowHeight');
}),
spacerStyle: computed('offsetY', function () {
return `
will-change: transform;
transform: translateY(${this.get('offsetY')}px)
`;
}),
viewportStyle: computed(function () {
return `
will-change: transform;
overflow: hidden;
height: ${this.get('viewportHeight')}px;
position: relative;
`;
}),
rootStyle: computed(function () {
return `
overflow: auto;
height: ${this.get('rootHeight')}px;
`;
}),
handleScroll() {
requestAnimationFrame(() => {
this.set('scrollTop', this.get('$root.scrollTop'));
});
},
init() {
this._super(...arguments);
this.set('handleScroll', this.get('handleScroll').bind(this));
},
didInsertElement() {
this._super(...arguments);
this.set('$root', this.element.querySelector('.root'));
this.get('$root').addEventListener('scroll', this.get('handleScroll'), { passive: true });
},
willDestroyElement() {
this._super(...arguments);
this.get('$root').removeEventListener('scroll', this.get('handleScroll'));
}
})
import Controller from '@ember/controller';
export default class ApplicationController extends Controller {
items = new Array(100000)
.fill(null)
.map((item, index) => "Item " + (index + 1));
}
body {
margin: 12px 16px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 12pt;
}
* {
box-sizing: border-box;
}
.viewport {
background: #fefefe;
}
.spacer > div {
height: 30px;
line-height: 30px;
border: 1px solid #f5f5f5;
}
{{outlet}}
{{#virtual-scroll
items=items
rootHeight=400
rowHeight=30
renderAhead=20
as |itemsToRender|
}}
{{#each itemsToRender as |item|}}
<div>{{item}}</div>
{{/each}}
{{/virtual-scroll}}
<div class="root" style={{rootStyle}}>
<div class="viewport" style={{viewportStyle}}>
<div class="spacer" style={{spacerStyle}}>
{{yield visibleItems}}
</div>
</div>
</div>
{
"version": "0.17.1",
"EmberENV": {
"FEATURES": {},
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": false,
"_APPLICATION_TEMPLATE_WRAPPER": true,
"_JQUERY_INTEGRATION": true
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js",
"ember": "2.16.2",
"ember-template-compiler": "2.16.2",
"ember-testing": "2.16.2"
},
"addons": {
"@glimmer/component": "1.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment