# Conflicts:
#	codecs/cpp.Dockerfile
#	codecs/imagequant/example.html
#	codecs/webp/dec/webp_dec.d.ts
#	codecs/webp/dec/webp_dec.js
#	codecs/webp/dec/webp_dec.wasm
#	codecs/webp/enc/webp_enc.d.ts
#	codecs/webp/enc/webp_enc.js
#	codecs/webp/enc/webp_enc.wasm
#	package-lock.json
#	package.json
#	src/codecs/tiny.webp
#	src_old/codecs/encoders.ts
#	src_old/codecs/processor-worker/tiny.avif
#	src_old/codecs/processor-worker/tiny.webp
#	src_old/codecs/tiny.webp
#	src_old/components/compress/index.tsx
#	src_old/lib/util.ts
#	src_old/sw/util.ts
This commit is contained in:
Jake Archibald
2020-09-16 09:59:24 +01:00
parent dfee848a39
commit a6477b82fc
202 changed files with 10435 additions and 16953 deletions

View File

@@ -0,0 +1,43 @@
import { h, Component } from 'preact';
import * as prettyBytes from 'pretty-bytes';
import * as style from './style.scss';
interface Props {
blob: Blob;
compareTo?: Blob;
}
interface State {}
export default class FileSize extends Component<Props, State> {
render({ blob, compareTo }: Props) {
let comparison: JSX.Element | undefined;
if (compareTo) {
const delta = blob.size / compareTo.size;
if (delta > 1) {
const percent = Math.round((delta - 1) * 100) + '%';
comparison = (
<span class={`${style.sizeDelta} ${style.sizeIncrease}`}>
{percent === '0%' ? 'slightly' : percent} bigger
</span>
);
} else if (delta < 1) {
const percent = Math.round((1 - delta) * 100) + '%';
comparison = (
<span class={`${style.sizeDelta} ${style.sizeDecrease}`}>
{percent === '0%' ? 'slightly' : percent} smaller
</span>
);
} else {
comparison = <span class={style.sizeDelta}>no change</span>;
}
}
return (
<span>
{prettyBytes(blob.size)} {comparison}
</span>
);
}
}

View File

@@ -0,0 +1,139 @@
import { h, Component, ComponentChildren, ComponentChild } from 'preact';
import * as style from './style.scss';
import FileSize from './FileSize';
import {
DownloadIcon,
CopyAcrossIcon,
CopyAcrossIconProps,
} from '../../lib/icons';
import '../custom-els/LoadingSpinner';
import { SourceImage } from '../compress';
import { Fileish, bind } from '../../lib/initial-util';
interface Props {
loading: boolean;
source?: SourceImage;
imageFile?: Fileish;
downloadUrl?: string;
children: ComponentChildren;
copyDirection: CopyAcrossIconProps['copyDirection'];
buttonPosition: keyof typeof buttonPositionClass;
onCopyToOtherClick(): void;
}
interface State {
showLoadingState: boolean;
}
const buttonPositionClass = {
'stack-right': style.stackRight,
'download-right': style.downloadRight,
'download-left': style.downloadLeft,
};
const loadingReactionDelay = 500;
export default class Results extends Component<Props, State> {
state: State = {
showLoadingState: false,
};
/** The timeout ID between entering the loading state, and changing UI */
private loadingTimeoutId: number = 0;
componentDidUpdate(prevProps: Props, prevState: State) {
if (prevProps.loading && !this.props.loading) {
// Just stopped loading
clearTimeout(this.loadingTimeoutId);
this.setState({ showLoadingState: false });
} else if (!prevProps.loading && this.props.loading) {
// Just started loading
this.loadingTimeoutId = self.setTimeout(
() => this.setState({ showLoadingState: true }),
loadingReactionDelay,
);
}
}
@bind
private onCopyToOtherClick(event: Event) {
event.preventDefault();
this.props.onCopyToOtherClick();
}
@bind
onDownload() {
// GA cant do floats. So we round to ints. We're deliberately rounding to nearest kilobyte to
// avoid cases where exact image sizes leak something interesting about the user.
const before = Math.round(this.props.source!.file.size / 1024);
const after = Math.round(this.props.imageFile!.size / 1024);
const change = Math.round((after / before) * 1000);
ga('send', 'event', 'compression', 'download', {
metric1: before,
metric2: after,
metric3: change,
});
}
render(
{
source,
imageFile,
downloadUrl,
children,
copyDirection,
buttonPosition,
}: Props,
{ showLoadingState }: State,
) {
return (
<div class={`${style.results} ${buttonPositionClass[buttonPosition]}`}>
<div class={style.resultData}>
{(children as ComponentChild[])[0] ? (
<div class={style.resultTitle}>{children}</div>
) : null}
{!imageFile || showLoadingState ? (
'Working…'
) : (
<FileSize
blob={imageFile}
compareTo={
source && imageFile !== source.file ? source.file : undefined
}
/>
)}
</div>
<button
class={style.copyToOther}
title="Copy settings to other side"
onClick={this.onCopyToOtherClick}
>
<CopyAcrossIcon
class={style.copyIcon}
copyDirection={copyDirection}
/>
</button>
<div class={style.download}>
{downloadUrl && imageFile && (
<a
class={`${style.downloadLink} ${
showLoadingState ? style.downloadLinkDisable : ''
}`}
href={downloadUrl}
download={imageFile.name}
title="Download"
onClick={this.onDownload}
>
<DownloadIcon class={style.downloadIcon} />
</a>
)}
{showLoadingState && <loading-spinner class={style.spinner} />}
</div>
</div>
);
}
}

View File

@@ -0,0 +1,131 @@
@keyframes action-enter {
from {
transform: rotate(-90deg);
opacity: 0;
animation-timing-function: ease-out;
}
}
@keyframes action-leave {
from {
transform: rotate(0deg);
opacity: 1;
animation-timing-function: ease-out;
}
}
.results {
display: grid;
grid-template-columns: [text] 1fr [copy-button] auto [download-button] auto;
background: rgba(0, 0, 0, 0.9);
font-size: 1rem;
@media (min-width: 400px) {
font-size: 1.2rem;
}
@media (min-width: 600px) {
font-size: 1.4rem;
}
&:focus {
outline: none;
}
}
.result-data {
grid-row: 1;
grid-column: text;
display: flex;
align-items: center;
padding: 0 10px;
white-space: nowrap;
overflow: hidden;
}
.download-right {
grid-template-columns: [copy-button] auto [text] 1fr [download-button] auto;
}
.download-left {
grid-template-columns: [download-button] auto [text] 1fr [copy-button] auto;
}
.stack-right {
& .result-data {
padding: 0 15px;
}
}
.result-title {
display: flex;
align-items: center;
margin-right: 0.4em;
}
.size-delta {
font-size: 0.8em;
font-style: italic;
position: relative;
top: -1px;
margin-left: 0.3em;
}
.size-increase {
color: #e35050;
}
.size-decrease {
color: #50e3c2;
}
.download {
grid-row: 1;
grid-column: download-button;
background: #34B9EB;
--size: 38px;
width: var(--size);
height: var(--size);
display: grid;
align-items: center;
justify-items: center;
}
.download-link {
animation: action-enter 0.2s;
grid-area: 1/1;
}
.download-link-disable {
pointer-events: none;
opacity: 0;
transform: rotate(90deg);
animation: action-leave 0.2s;
}
.download-icon,
.copy-icon {
color: #fff;
display: block;
--size: 24px;
width: var(--size);
height: var(--size);
padding: 7px;
filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.7));
}
.spinner {
--color: #fff;
--delay: 0;
--size: 22px;
grid-area: 1/1;
}
.copy-to-other {
grid-row: 1;
grid-column: copy-button;
composes: unbutton from '../../lib/util.scss';
composes: download;
background: #656565;
}