Improvements

- Use Picture component for focus view
	- remove hover animation
	- bigger rows
	- avoid dangling, too-tall images
	- update node modules
This commit is contained in:
Aaron Gutierrez
2022-01-19 18:32:40 -07:00
parent e8b14c52c9
commit d96d63da8e
11 changed files with 7216 additions and 2492 deletions

View File

@@ -8,6 +8,6 @@
</head> </head>
<body> <body>
<div id="mount"><noscript>I don't like javascript, either.</noscript></div> <div id="mount"><noscript>I don't like javascript, either.</noscript></div>
<script type="text/javascript" src="dist/bundle.js"></script> <script type="text/javascript" src="bundle.js"></script>
</body> </body>
</html> </html>

9603
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -22,10 +22,11 @@
}, },
"devDependencies": { "devDependencies": {
"awesome-typescript-loader": "^5.2.1", "awesome-typescript-loader": "^5.2.1",
"esbuild": "^0.8.50",
"source-map-loader": "^0.2.4", "source-map-loader": "^0.2.4",
"typescript": "^3.9.7", "typescript": "^3.9.7",
"webpack": "^4.44.2", "webpack": "^5.66.0",
"webpack-cli": "^3.3.12", "webpack-cli": "^4.9.1",
"webpack-dev-server": "^3.11.0" "webpack-dev-server": "^4.7.3"
} }
} }

View File

@@ -35,6 +35,16 @@ h2 {
border-bottom: 1px solid #eef; border-bottom: 1px solid #eef;
} }
.ImageSet-location,
.ImageSet-description {
white-space: nowrap;
}
.ImageSet-location:after {
content: " · ";
white-space: break-spaces;
}
.Grid { .Grid {
margin-bottom: 45px; margin-bottom: 45px;
} }
@@ -46,42 +56,27 @@ h2 {
.Grid img { .Grid img {
cursor: pointer; cursor: pointer;
transition-duration: .05s;
transition-property: transform, box-shadow;
transition-timing-function: ease-out;
}
.Grid img:hover {
box-shadow: 1px 2px 5px rgba(0, 0, 0, .7);
transform: scale(1.2);
} }
.BigPicture { .BigPicture {
align-items: center;
background-color: rgba(0, 0, 0, 0.6); background-color: rgba(0, 0, 0, 0.6);
bottom: 0; bottom: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-evenly;
left: 0; left: 0;
padding: 30px;
position: fixed; position: fixed;
right: 0; right: 0;
top: 0; top: 0;
z-index: 100; z-index: 100;
} }
.BigPicture-image {
background-position: center;
background-repeat: no-repeat;
background-size: contain;
flex: 1 1 auto;
}
.BigPicture-footer { .BigPicture-footer {
align-self: center; align-self: center;
display: flex; display: flex;
flex: 0 0 auto; flex: 0 0 auto;
justify-content: space-between; justify-content: space-between;
margin-top: 30px;
max-width: 200px; max-width: 200px;
width: 100%; width: 100%;
} }

View File

@@ -1,4 +1,5 @@
import * as Model from "../model"; import * as Model from "../model";
import { Picture } from "./picture";
import * as React from "react"; import * as React from "react";
@@ -6,6 +7,7 @@ export interface Props {
image: Model.Image; image: Model.Image;
onClose: () => void; onClose: () => void;
width: number; width: number;
height: number;
} }
export class BigPicture extends React.PureComponent<Props, {}> { export class BigPicture extends React.PureComponent<Props, {}> {
@@ -20,14 +22,17 @@ export class BigPicture extends React.PureComponent<Props, {}> {
} }
render() { render() {
const src = `img/1600/${this.props.image.src}`; const scaleWidth = this.props.image.width / this.props.width;
const scaleHeight = this.props.image.height / (this.props.height - 80);
const scale = Math.max(scaleWidth, scaleHeight);
return ( return (
<div className="BigPicture"> <div className="BigPicture">
<div <Picture
className="BigPicture-image" image={this.props.image}
style={{ onClick={() => {}}
backgroundImage: `url(${src})` height={this.props.image.height / scale}
}} width={this.props.image.width / scale}
/> />
<div className="BigPicture-footer"> <div className="BigPicture-footer">
<a <a

View File

@@ -8,10 +8,11 @@ export interface Props {
onImageSelected: (image: Model.Image) => void; onImageSelected: (image: Model.Image) => void;
pageBottom: number; pageBottom: number;
width: number; width: number;
height: number;
} }
export const ROW_HEIGHT = 200; export const ROW_HEIGHT = 250;
export const MOBILE_ROW_HEIGHT = 100; export const MOBILE_ROW_HEIGHT = 150;
interface Row { interface Row {
images: Model.Image[]; images: Model.Image[];
@@ -53,7 +54,7 @@ export class Grid extends React.PureComponent<Props, {}> {
}); });
const images = rows.map(row => { const images = rows.map(row => {
const height = this.props.width / row.width; const height = Math.min(this.props.height, this.props.width / row.width);
const pics = row.images.map(image => { const pics = row.images.map(image => {
return ( return (
@@ -61,6 +62,7 @@ export class Grid extends React.PureComponent<Props, {}> {
image={image} image={image}
onClick={() => this.props.onImageSelected(image)} onClick={() => this.props.onImageSelected(image)}
key={image.src} key={image.src}
height={height}
width={(image.width / image.height) * height} width={(image.width / image.height) * height}
defer={this.gridHeight > this.props.pageBottom} defer={this.gridHeight > this.props.pageBottom}
/> />

View File

@@ -9,6 +9,7 @@ export interface Props {
setGridHeight: (height: number) => void; setGridHeight: (height: number) => void;
pageBottom: number; pageBottom: number;
width: number; width: number;
height: number;
} }
export class ImageSet extends React.PureComponent<Props, {}> { export class ImageSet extends React.PureComponent<Props, {}> {
@@ -20,13 +21,15 @@ export class ImageSet extends React.PureComponent<Props, {}> {
return ( return (
<div className="ImageSet" ref={this.divRef}> <div className="ImageSet" ref={this.divRef}>
<h2> <h2>
{this.props.imageSet.location} · {this.props.imageSet.description} <span className="ImageSet-location">{this.props.imageSet.location}</span>
<span className="ImageSet-description">{this.props.imageSet.description}</span>
</h2> </h2>
<Grid <Grid
images={this.props.imageSet.images} images={this.props.imageSet.images}
onImageSelected={this.props.onImageSelected} onImageSelected={this.props.onImageSelected}
pageBottom={this.props.pageBottom} pageBottom={this.props.pageBottom}
width={this.props.width} width={this.props.width}
height={this.props.height}
/> />
</div> </div>
); );

View File

@@ -5,6 +5,7 @@ import * as React from "react";
export interface Props { export interface Props {
image: Model.Image; image: Model.Image;
onClick: () => void; onClick: () => void;
height: number;
width: number; width: number;
defer?: boolean; defer?: boolean;
} }
@@ -47,8 +48,10 @@ export class Picture extends React.PureComponent<Props, State> {
<source srcSet={srcSet.webp} type="image/webp" /> <source srcSet={srcSet.webp} type="image/webp" />
<source srcSet={srcSet.jpeg} type="image/jpeg" /> <source srcSet={srcSet.jpeg} type="image/jpeg" />
<img <img
id={this.props.image.src}
onClick={this.props.onClick} onClick={this.props.onClick}
src={srcSet.bestSrc} src={srcSet.bestSrc}
height={this.props.height + "px"}
width={this.props.width + "px"} width={this.props.width + "px"}
/> />
</picture> </picture>

View File

@@ -12,6 +12,7 @@ export interface State {
gridHeights: number[]; gridHeights: number[];
pageBottom: number; pageBottom: number;
width: number; width: number;
height: number;
} }
export class Root extends React.PureComponent<Props, State> { export class Root extends React.PureComponent<Props, State> {
@@ -31,11 +32,24 @@ export class Root extends React.PureComponent<Props, State> {
document.getElementById("mount")!.clientWidth document.getElementById("mount")!.clientWidth
); );
}; };
private _viewHeight = (): number => {
// iOS Safari does not set outerWidth/outerHeight
if (!window.outerHeight) {
return window.innerHeight;
}
return Math.min(
window.innerHeight,
window.outerHeight,
document.body.clientHeight
);
};
state: State = { state: State = {
gridHeights: [], gridHeights: [],
pageBottom: window.innerHeight + window.pageYOffset, pageBottom: window.innerHeight + window.pageYOffset,
width: this._viewWidth() width: this._viewWidth(),
height: this._viewHeight()
}; };
componentDidMount() { componentDidMount() {
@@ -65,6 +79,7 @@ export class Root extends React.PureComponent<Props, State> {
setGridHeight={this._setGridHeight(idx)} setGridHeight={this._setGridHeight(idx)}
onImageSelected={this._onImageSelected} onImageSelected={this._onImageSelected}
width={this.state.width} width={this.state.width}
height={this.state.height}
/> />
)) ))
: null; : null;
@@ -72,7 +87,7 @@ export class Root extends React.PureComponent<Props, State> {
return ( return (
<div className="Root"> <div className="Root">
{this._bigPicture()} {this._bigPicture()}
<h1>Ski</h1> <h1>Aaron's Ski Pictures</h1>
{imageSets} {imageSets}
</div> </div>
); );
@@ -84,6 +99,7 @@ export class Root extends React.PureComponent<Props, State> {
image={this.state.selectedImage} image={this.state.selectedImage}
onClose={this._showGrid} onClose={this._showGrid}
width={this.state.width} width={this.state.width}
height={this.state.height}
/> />
) : null; ) : null;
@@ -108,7 +124,8 @@ export class Root extends React.PureComponent<Props, State> {
private _onViewChange = () => { private _onViewChange = () => {
this.setState({ this.setState({
pageBottom: window.innerHeight + window.pageYOffset, pageBottom: window.innerHeight + window.pageYOffset,
width: this._viewWidth() width: this._viewWidth(),
height: this._viewHeight(),
}); });
}; };

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en-US"> <html lang="en-US">
<head> <head>
<title>Skiing - Aaron Gutierrez</title> <title>Skiing Aaron Gutierrez</title>
<link rel="stylesheet" href="{0}"> <link rel="stylesheet" href="{0}">
<meta charshet="utf-8"> <meta charshet="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">

View File

@@ -7,16 +7,21 @@ module.exports = (env) => {
entry: "./src/index.tsx", entry: "./src/index.tsx",
output: { output: {
filename: "bundle.js", filename: "bundle.js",
path: __dirname + "/dist" path: path.join(__dirname, "dist")
}, },
mode: mode, mode: mode,
// Enable sourcemaps for debugging webpack's output. // Enable sourcemaps for debugging webpack's output.
devtool: "source-map", devtool: "source-map",
performance: {
hints: false
},
devServer: { devServer: {
publicPath: "/dist/", static: {
directory: __dirname,
},
port: 8080 port: 8080
}, },