Finish initial work on d-figure

This commit is contained in:
Ludwig Schubert
2017-08-22 15:18:05 -07:00
parent 08fafcf2ee
commit 9993a679c1
5 changed files with 113 additions and 93 deletions
+1
View File
@@ -46,6 +46,7 @@
"@webcomponents/webcomponentsjs": "^1.0.7",
"assert": "^1.4.1",
"d3-time-format": "^2.0.3",
"intersection-observer": "^0.4.0",
"jsdom-wc": "^11.0.0-alpha-1",
"katex": "^0.7.1"
}
+1
View File
@@ -37,6 +37,7 @@ componentsConfig.plugins.push(copy({
'./node_modules/katex/dist/fonts': 'dist/fonts',
'./node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js': 'dist/webcomponents-lite.js',
'./node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js.map': 'dist/webcomponents-lite.js.map',
'./node_modules/intersection-observer/intersection-observer.js': 'dist/intersection-observer.js',
}));
Object.assign(transformsConfig, defaultConfig);
+77 -81
View File
@@ -1,28 +1,17 @@
`Figure
d-figure provides a state-machine of visibility events:
scroll out of view
+----------------+
*do work here* | |
+------+ +----------------+ +-+---------+ +-v---------+
| init +----> almostonscreen +----> onscreen | | offscreen |
+------+ +----------------+ +---------^-+ +---------+-+
| |
+----------------+
scroll into view
TODO:
* default styling
* get rid of init state?
* shadow dom?
* resizing/default size?
* offer centerscreen event?
* polyfills
`
const intersectionMargin = 1000;
// Figure
//
// d-figure provides a state-machine of visibility events:
//
// scroll out of view
// +----------------+
// *do work here* | |
// +----------------+ +-+---------+ +-v---------+
// | ready +----> onscreen | | offscreen |
// +----------------+ +---------^-+ +---------+-+
// | |
// +----------------+
// scroll into view
//
export class Figure extends HTMLElement {
@@ -30,81 +19,88 @@ export class Figure extends HTMLElement {
constructor() {
super();
// keep track of communicated state
this._init = false;
this._ready = false;
this._onscreen = false;
this._offscreen = true;
// we use two separate observers:
// one with an extra 1000px margin to warn if the viewpoint gets close,
// and one for the actual on/off screen events
this._marginObserver = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting && !this._almostonscreen) {
this.almostonscreen();
this._marginObserver.disconnect();
}
}
}, {
root: null, // listen on entire viewport
rootMargin: intersectionMargin + 'px 0px ' + intersectionMargin + 'px 0px',
threshold: 1.0,
});
this._directObserver = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
if (!this._almostonscreen) { this.almostonscreen(); }
if (this._offscreen) { this.onscreen(); }
} else {
if (this._onscreen) { this.offscreen(); }
}
}
}, {
root: null, // listen on entire viewport
rootMargin: '0px',
threshold: [0, 1.0],
});
}
connectedCallback() {
this._marginObserver.observe(this);
this._directObserver.observe(this);
this.init();
Figure.marginObserver.observe(this);
Figure.directObserver.observe(this);
}
disconnectedCallback() {
Figure.marginObserver.unobserve(this);
Figure.directObserver.unobserve(this);
}
/* Override addEventListener
* We want to notify elements that register late as well.
*/
// We use two separate observers:
// One with an extra 1000px margin to warn if the viewpoint gets close,
// And one for the actual on/off screen events
static get marginObserver() {
if (!Figure._marginObserver) {
const viewportHeight = window.innerHeight;
const margin = Math.floor(2 * viewportHeight);
Figure._marginObserver = new IntersectionObserver(
Figure.didObserveMarginIntersection, {
rootMargin: margin + 'px 0px ' + margin + 'px 0px', threshold: 0.01,
});
}
return Figure._marginObserver;
}
static didObserveMarginIntersection(entries) {
for (const entry of entries) {
const figure = entry.target;
if (entry.isIntersecting && !figure._ready) {
figure.ready();
}
}
}
static get directObserver() {
if (!Figure._directObserver) {
Figure._directObserver = new IntersectionObserver(
Figure.didObserveDirectIntersection, {
rootMargin: '0px', threshold: [0, 1.0],
}
);
}
return Figure._directObserver;
}
static didObserveDirectIntersection(entries) {
for (const entry of entries) {
const figure = entry.target;
if (entry.isIntersecting) {
if (!figure._ready) { figure.ready(); }
if (figure._offscreen) { figure.onscreen(); }
} else {
if (figure._onscreen) { figure.offscreen(); }
}
}
}
// Notify listeners that registered late, too:
addEventListener(eventName, callback) {
super.addEventListener(eventName, callback);
// if we had already dispatched something while presumingly no one was listening, we do so again
if (this._init && eventName === 'init') {
this.init();
}
if (this._almostonscreen && eventName === 'almostonscreen') {
this.almostonscreen();
if (this._ready && eventName === 'ready') {
this.ready();
}
if (this._onscreen && eventName === 'onscreen') {
this.onscreen();
}
}
// Custom Events
/* Custom Events */
init() {
this._init = true;
const event = new CustomEvent('init');
this.dispatchEvent(event);
}
almostonscreen() {
this._almostonscreen = true;
const event = new CustomEvent('almostonscreen');
ready() {
this._ready = true;
this.marginObserver.unobserve(this);
const event = new CustomEvent('ready');
this.dispatchEvent(event);
}
+2 -1
View File
@@ -52,7 +52,8 @@ d-article > * {
grid-column: margin-left / body;
}
.l-page {
.l-page,
d-figure {
grid-column: margin-left / page;
}
+32 -11
View File
@@ -1,17 +1,38 @@
const webcomponentPath = '../dist/webcomponents-lite.js';
const intersectionObserverPath = '../dist/intersection-observer.js';
const template = `
if ('registerElement' in document &&
'import' in document.createElement('link') &&
'content' in document.createElement('template')) {
// Platform supports webcomponents natively! :-)
} else {
// Platform does not support webcomponents--loading polyfills synchronously.
const scriptTag = document.createElement('script');
scriptTag.src = '${webcomponentPath}';
scriptTag.async = false;
document.currentScript.parentNode.insertBefore(scriptTag, document.currentScript.nextSibling);
}
if ('IntersectionObserver' in window &&
'IntersectionObserverEntry' in window &&
'intersectionRatio' in IntersectionObserverEntry.prototype) {
// Platform supports IntersectionObserver natively! :-)
if (!('isIntersecting' in IntersectionObserverEntry.prototype)) {
Object.defineProperty(IntersectionObserverEntry.prototype,
'isIntersecting', {
get: function () {
return this.intersectionRatio > 0;
}
});
}
} else {
// Platform does not support webcomponents--loading polyfills synchronously.
const scriptTag = document.createElement('script');
scriptTag.src = '${intersectionObserverPath}';
scriptTag.async = false;
document.currentScript.parentNode.insertBefore(scriptTag, document.currentScript.nextSibling);
}
if ('registerElement' in document &&
'import' in document.createElement('link') &&
'content' in document.createElement('template')) {
// Platform supports webcomponents natively! :-)
} else {
// Platform does not support webcomponents--loading polyfills synchronously.
const scriptTag = document.createElement('script');
scriptTag.src = '${webcomponentPath}';
scriptTag.async = false;
document.currentScript.parentNode.insertBefore(scriptTag, document.currentScript.nextSibling);
}
`;
export default function render(dom) {