From e3182d4cf2141d4063bd863c9a74010b75e6f673 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Wed, 26 Feb 2020 21:17:38 -0800 Subject: [PATCH] Refactor image handling in preparation of multi-image slides. --- react-slideshow/src/App.css | 4 + react-slideshow/src/App.tsx | 167 ++++++++++++++++++++---------------- 2 files changed, 96 insertions(+), 75 deletions(-) diff --git a/react-slideshow/src/App.css b/react-slideshow/src/App.css index b05d68c..8b2849f 100644 --- a/react-slideshow/src/App.css +++ b/react-slideshow/src/App.css @@ -13,3 +13,7 @@ body, html, #root { #ui .meta { text-align: center; } + +#slide { + height: 100%; +} diff --git a/react-slideshow/src/App.tsx b/react-slideshow/src/App.tsx index f8d3a59..1a0ed38 100644 --- a/react-slideshow/src/App.tsx +++ b/react-slideshow/src/App.tsx @@ -51,6 +51,43 @@ function shuffle(a: Array) { 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; + nextSlide?: Slide; + prevSlide?: Slide; + constructor(items: Array) { + 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
+ } +}; + type MediaMetadata = { width: number; height: number; @@ -68,7 +105,7 @@ type AlbumProps = { type AlbumState = { error: any; mediaItems: Array | null; - idx: number; + curSlide?: Slide, showUI: boolean; timerID: any | null; }; @@ -76,7 +113,6 @@ class Album extends React.Component { state: AlbumState = { error: null, mediaItems: null, - idx: 0, showUI: this.props.showUI, timerID: null, }; @@ -88,12 +124,49 @@ class Album extends React.Component { fetch(process.env.PUBLIC_URL + `/api/album/${album}`) .then(res => res.json()) .then( - (result) => { - this.setState({mediaItems: result}); + (mediaItems: Array) => { + 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 { 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

Error: {JSON.stringify(error)}

; - } 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 { }; 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 { style={leftPrefetchStyle} onClick={(e)=>{ e.stopPropagation(); - this.setState({idx: prevIdx}) - }}> -
{image.filename}
+ this.setState({curSlide: curSlide?.prevSlide}) + }}>{ prevSlide?.render() } + {/* TODO(wathiede): make this work with multiple items. */} +
{curSlide?.items[0].filename}
{ e.stopPropagation(); - this.setState({idx: nextIdx}) - }}>
+ this.setState({curSlide: curSlide?.nextSlide}) + }}>{ nextSlide?.render() } ; } - return
{ + return
{ e.stopPropagation(); this.setState({showUI: !showUI}) }}> + { curSlide?.render() } { ui } -
; +
; } else { return

Loading...

; }