Refactor image handling in preparation of multi-image slides.

This commit is contained in:
Bill Thiede 2020-02-26 21:17:38 -08:00
parent 9f9c3cc00c
commit e3182d4cf2
2 changed files with 96 additions and 75 deletions

View File

@ -13,3 +13,7 @@ body, html, #root {
#ui .meta {
text-align: center;
}
#slide {
height: 100%;
}

View File

@ -51,6 +51,43 @@ function shuffle(a: Array<MediaItem>) {
return a;
}
class Slide {
// One or two items. For example if display is landscape we'll try to fit
// two portrait images and only one landscape.
items: Array<MediaItem>;
nextSlide?: Slide;
prevSlide?: Slide;
constructor(items: Array<MediaItem>) {
this.items = items;
}
render() {
let w = window.innerWidth * window.devicePixelRatio;
let h = window.innerHeight * window.devicePixelRatio;
let ratio = w/h;
if (ratio > 1) {
// Landscape image
w = roundup(w, IMAGE_CHUNK);
h = Math.round(w/ratio);
} else {
// Portrait image
h = roundup(h, IMAGE_CHUNK);
w = Math.round(h/ratio);
}
console.log(`Window size ${window.innerWidth}x${window.innerHeight} with a devicePixelRatio of ${window.devicePixelRatio} for a total size of ${w}x${h}`);
let style: React.CSSProperties = {
height: '100%',
width: '100%',
backgroundColor: 'black',
// TODO(wathiede): make this handle multiple items.
backgroundImage: `url(/api/image/${this.items[0].id}?w=${w}&h=${h})`,
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center',
backgroundSize: 'cover',
};
return <div style={style}></div>
}
};
type MediaMetadata = {
width: number;
height: number;
@ -68,7 +105,7 @@ type AlbumProps = {
type AlbumState = {
error: any;
mediaItems: Array<MediaItem> | null;
idx: number;
curSlide?: Slide,
showUI: boolean;
timerID: any | null;
};
@ -76,7 +113,6 @@ class Album extends React.Component<AlbumProps, AlbumState> {
state: AlbumState = {
error: null,
mediaItems: null,
idx: 0,
showUI: this.props.showUI,
timerID: null,
};
@ -88,12 +124,49 @@ class Album extends React.Component<AlbumProps, AlbumState> {
fetch(process.env.PUBLIC_URL + `/api/album/${album}`)
.then(res => res.json())
.then(
(result) => {
this.setState({mediaItems: result});
(mediaItems: Array<MediaItem>) => {
let w = window.innerWidth * window.devicePixelRatio;
let h = window.innerHeight * window.devicePixelRatio;
let ratio = w/h;
let landscapes = mediaItems.filter((mi) => {
let md = mi.mediaMetadata;
let ratio = md.width/md.height;
return ratio > 1;
});
let portraits = mediaItems.filter((mi) => {
let md = mi.mediaMetadata;
let ratio = md.width/md.height;
return ratio <= 1;
});
console.log(`${landscapes.length} landscape photos`);
console.log(`${portraits.length} portraits photos`);
let photos;
if (ratio > 1) {
console.log('display in landscape mode');
photos = landscapes;
} else {
console.log('display in portrait mode');
photos = portraits;
}
photos = shuffle(photos);
let slides = photos.map((p)=>{
return new Slide([p]);
});
let numSlides = slides.length;
slides.forEach((p, idx)=>{
let nextIdx = (idx+1)%numSlides;
let prevIdx = (numSlides+idx-1)%numSlides;
p.nextSlide = slides[nextIdx];
p.prevSlide = slides[prevIdx];
})
this.setState({curSlide: slides[0]});
let {sleepTimeSeconds} = this.props;
let timerID = setInterval(()=>{
let {idx} = this.state;
this.setState({idx: idx+1})
let {curSlide} = this.state;
this.setState({curSlide: curSlide?.nextSlide})
console.log('timer fired');
}, sleepTimeSeconds*1000);
this.setState({timerID});
@ -110,71 +183,15 @@ class Album extends React.Component<AlbumProps, AlbumState> {
render() {
// TODO(wathiede): fade transition.
// TODO(wathiede): pair-up portrait orientation images.
let w = window.innerWidth * window.devicePixelRatio;
let h = window.innerHeight * window.devicePixelRatio;
let ratio = w/h;
if (ratio > 1) {
// Landscape image
w = roundup(w, IMAGE_CHUNK);
h = Math.round(w/ratio);
} else {
// Portrait image
h = roundup(h, IMAGE_CHUNK);
w = Math.round(h/ratio);
}
console.log(`Window size ${window.innerWidth}x${window.innerHeight} with a devicePixelRatio of ${window.devicePixelRatio} for a total size of ${w}x${h}`);
//let w = roundup(window.innerWidth*window.devicePixelRatio, IMAGE_CHUNK);
//let h = roundup(window.innerHeight*window.devicePixelRatio, IMAGE_CHUNK);
let {idx, error, mediaItems, showUI} = this.state;
let {curSlide, error, mediaItems, showUI} = this.state;
if (error !== null) {
return <h2>Error: {JSON.stringify(error)}</h2>;
} else if (mediaItems !== null) {
let landscapes = mediaItems.filter((mi) => {
let md = mi.mediaMetadata;
let ratio = md.width/md.height;
return ratio > 1;
});
let portraits = mediaItems.filter((mi) => {
let md = mi.mediaMetadata;
let ratio = md.width/md.height;
return ratio <= 1;
});
console.log(`${landscapes.length} landscape photos`);
console.log(`${portraits.length} portraits photos`);
let photos;
if (ratio > 1) {
console.log('display in landscape mode');
photos = landscapes;
} else {
console.log('display in portrait mode');
photos = portraits;
}
photos = shuffle(photos);
let numImages = photos.length;
idx = idx % numImages;
let nextIdx = (idx+1)%numImages;
let prevIdx = (numImages+idx-1)%numImages;
let image = photos[idx];
let nextImage = photos[nextIdx];
let prevImage = photos[prevIdx];
let style: React.CSSProperties = {
height: '100%',
width: '100%',
backgroundColor: 'black',
backgroundImage: `url(/api/image/${image.id}?w=${w}&h=${h})`,
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center',
backgroundSize: 'cover',
};
} else if (curSlide) {
let nextSlide = curSlide?.nextSlide;
let prevSlide = curSlide?.prevSlide;
let prefetchStyle: React.CSSProperties = {
backgroundColor: 'rgba(127, 127, 127, 0.5)',
backgroundPosition: 'center center',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
bottom: 0,
height: '25%',
position: 'absolute',
@ -182,12 +199,10 @@ class Album extends React.Component<AlbumProps, AlbumState> {
};
let leftPrefetchStyle: React.CSSProperties = {
left: 0,
backgroundImage: `url(/api/image/${prevImage.id}?w=${w}&h=${h})`,
...prefetchStyle
};
let rightPrefetchStyle: React.CSSProperties = {
right: 0,
backgroundImage: `url(/api/image/${nextImage.id}?w=${w}&h=${h})`,
...prefetchStyle
};
let ui;
@ -197,23 +212,25 @@ class Album extends React.Component<AlbumProps, AlbumState> {
style={leftPrefetchStyle}
onClick={(e)=>{
e.stopPropagation();
this.setState({idx: prevIdx})
}}></div>
<div className="meta">{image.filename}</div>
this.setState({curSlide: curSlide?.prevSlide})
}}>{ prevSlide?.render() }</div>
{/* TODO(wathiede): make this work with multiple items. */}
<div className="meta">{curSlide?.items[0].filename}</div>
<div
style={rightPrefetchStyle}
onClick={(e)=>{
e.stopPropagation();
this.setState({idx: nextIdx})
}}></div>
this.setState({curSlide: curSlide?.nextSlide})
}}>{ nextSlide?.render() }</div>
</div>;
}
return <div style={style} onClick={(e)=>{
return <div id="slide" onClick={(e)=>{
e.stopPropagation();
this.setState({showUI: !showUI})
}}>
{ curSlide?.render() }
{ ui }
</div>;
</div>;
} else {
return <h2>Loading...</h2>;
}